When looking to move the pointer on to the next record, an error message is shown that says:
Access violation at address 004070E2 in module 'main_p.exe'. Write of
address 00000000
Any way of resolving this?
var
i: integer;
begin
with dmData.dmEventInfo do
begin
tblEventinfo.Open;
i := 0;
while NOT tblEventinfo.Eof do
begin
arrNames[i] := tblEventinfo['bandname'];
tblEventinfo.Next;
i := i + 1;
end;
end;
end;
You don't show the declaration of the arrNames array , but I think the problem was in the Length.
var
i: integer; arrNames : array of string;
begin
SetLength(arrNames , tblEventinfo.RecordCount);
i := 0;
while NOT tblEventinfo.Eof do
begin
arrNames[i] := tblEventinfobandname.Value;
tblEventinfo.Next;
Inc(I);
end;
end;
Related
I am having trouble getting the serial port data from an equipment.
Below is the image of the expected result:
Desire result:
Unwanted result:
I use Ttimer so I can automatically get the data and put it to the Memo.
I need the data to be placed line by line in the memo.
This is the source code:
procedure TForm3.Timer1Timer(Sender: TObject);
var
k: Integer;
InBuffer: array[1..500] of char;
begin
for k:=1 to 500 do
InBuffer[k]:=' ';
Trim(InBuffer);
if cport.Connected = true then
begin
ComLed1.Kind := lkGreenLight;
cport.ReadStr(str,k);
Trim(str);
S:=str;
if str = '' then
begin
end
else
begin
memo1.lines.Add(str);
end;
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Connected';
end;
end;
My question is what is the problem? And what is the solution for this.
TMemo.Lines.Add() adds a line. The text you add will have a line break inserted at the end of it. It is clear that you are receiving the hardware data in pieces, and you are adding each piece separately as its own line in the Memo.
To do what you are attempting, you need to either:
Read the pieces from the hardware and cache them until you detect the end of a complete message, and then Add() only complete messages to the Memo. How you do this depends on the particular protocol the hardware is using to send data to you. Does it wrap the data in STX/ETX markers? Does it delimit messages? We don't know, you have not provided any information about that. And your code is trying (unsuccessfully) to trim a lot of data away that it probably shouldn't be throwing away at all.
Don't use Add() at all. You can use the SelText property instead to avoid inserting any line breaks you don't want.
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
That being said, your timer code is doing some odd things. InBuffer is filled with spaces, then (unsuccessfully) trimmed, and then completely ignored. You are passing an uninitialized k value to ReadStr(). The str value you do read is unsuccessfully trimmed before added to the Memo. You are assigning str to S and then ignoring S.
Try this instead:
procedure TForm3.Timer1Timer(Sender: TObject);
var
str: AnsiString;
begin
if cport.Connected then
begin
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
cport.ReadStr(str, 256);
str := Trim(str);
if str <> '' then
begin
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
end;
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
Alternatively (assuming you are using TComPort that has an OnRxChar event):
procedure TForm3.Timer1Timer(Sender: TObject);
begin
if cport.Connected then
begin
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
end
else
begin
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
procedure TForm3.cportRxChar(Sender: TObject; Count: Integer);
var
str: AnsiString;
begin
cport.ReadStr(str, Count);
str := Trim(str);
if str <> '' then
begin
memo1.SelStart := memo1.GetTextLen;
memo1.SelLength := 0;
memo1.SelText := str;
end;
end;
Edit based on new information provided in comments, try something like this:
private
buffer: AnsiString;
portConnected: boolean;
procedure TForm3.Timer1Timer(Sender: TObject);
begin
if cport.Connected then
begin
if not portConnected then
begin
portConnected := true;
buffer := '';
ComLed1.Kind := lkGreenLight;
txt_com_status1.Caption := 'Connected';
end;
end
else
begin
if portConnected then
begin
portConnected := false;
ComLed1.Kind := lkredLight;
txt_com_status1.Caption := 'Disconnected';
end;
end;
end;
procedure TForm3.cportRxChar(Sender: TObject; Count: Integer);
var
str: AnsiString;
i: integer;
begin
cport.ReadStr(str, Count);
buffer := buffer + str;
repeat
i := Pos(#10, buffer);
if i = 0 then Exit;
str := Copy(buffer, 1, i-1);
Delete(buffer, 1, i);
memo1.Lines.Add(str);
until buffer = '';
end;
i have a problem if i getting my defined type object.
I need to get my variables-defined object from a ListBox.
My data types:
type
TObjectData = class
Id: Integer;
DataType: String;
end;
TProjektInfo = record
Id: Integer;
Nazev: String;
end;
TReportSelect = record
Count: Integer;
Zakazka_Id: Integer;
Singles: Array of TProjektInfo;
Multies: Array of TProjektInfo;
end;
My procedure for fill listbox:
procedure TReportMain.VykresyFillProjectsList();
var
I,Id: Integer;
Nazev: String;
ItemData: TObjectData;
begin
VykresyProjectsListSections.Items.BeginUpdate;
VykresyProjectsListSections.Items.Clear;
for I := Low(ReportSelect.Singles) to High(ReportSelect.Singles) do
begin
Id := ReportSelect.Singles[I].Id;
Nazev := ReportSelect.Singles[I].Nazev;
ItemData := TObjectData.Create;
ItemData.Id := Id;
ItemData.DataType := 'single';
VykresyProjectsListSections.Items.AddObject(Nazev, TObject(ItemData));
ItemData.Free;
end;
for I := Low(ReportSelect.Multies) to High(ReportSelect.Multies) do
begin
Id := ReportSelect.Multies[I].Id;
Nazev := ReportSelect.Multies[I].Nazev;
ItemData := TObjectData.Create;
ItemData.Id := Id;
ItemData.DataType := 'multi';
VykresyProjectsListSections.Items.AddObject(Nazev, TObject(ItemData));
ItemData.Free;
end;
VykresyProjectsListSections.Items.EndUpdate;
end;
My button event on click for getting my datatype object (this is wrong where is commented):
procedure TReportMain.BtnExportProjectsClick(Sender: TObject);
var
ItemData: TObjectData;
Nazev: String;
I: Integer;
begin
ItemData := TObjectData.Create;
for I := 0 to VykresyProjectsListSections.Count - 1 do
begin
if VykresyProjectsListSections.Checked[I] then
begin
ItemData := TObjectData(VykresyProjectsListSections.Items.Objects[I]); // <--- This is wrong, why ?
Nazev := VykresyProjectsListSections.Items.Strings[I];
showMessage(Format('Nazev: %s ID: %d Type: %s', [Nazev, ItemData.Id, ItemData.DataType]));
end;
end;
end;
What happens to you is probably an access violation.
The variable you're trying to access is undefined because you have already freed the object the variable is pointing to.
In the code above, the ItemData object is always freed after it's added to the list.
You have to write some code to free the object when the list is cleared or freed.
This can be done in the OnDestroy event of your form:
procedure TReportMain.FormDestroy(Sender: TObject);
var
i: Integer;
begin
for i := VykresyProjectsListSections.Items.Count-1 downto 0 do begin
VykresyProjectsListSections.Items.Objects[i].Free;
VykresyProjectsListSections.Delete(i);
end;
VykresyProjectsListSections.Free;//free the list if not owned by the application
end;
As a side note, you can test if a TCheckListBox element is checked like this:
var
i: Integer;
begin
for i := 0 to VykresyProjectsListSections.Items.Count-1 do begin
if VykresyProjectsListSections.State[i] = cbChecked then
//do your stuff
end;
end;
You are calling ItemData.Free after AddObject(). This is wrong, since the object will not be valid anymore.
This will cause the error when later accessing the object in the CheckListBox.
I don't know if it is even possible, but what I need is to access (use and free) a TStream variable by it pointer, passed thought a string parameter to another function.
Here is a "not working" example of what I am trying to do:
procedure TForm1.Button1Click(Sender: TObject);
var
Stm: TMemoryStream;
begin
Stm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Stm);
Stm.Position := 0;
Memo.Clear;
Edit.Text := IntToStr(Integer(#Stm));
except
Stm.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
PStm: ^TMemoryStream;
begin
PStm := Pointer(StrToInt(Edit.Text));
try
Memo.Lines.LoadFromStream(PStm^); // <--- Access Violation
finally
PStm^.Free;
end;
end;
Thanks for any help to solve this!
TStream is a reference type. Your Stm variable holds a pointer to the stream object instance. You want to pass this pointer value, not the address of the local variable. Here's the fixed code:
procedure TForm1.Button1Click(Sender: TObject);
var
Stm: TMemoryStream;
begin
Stm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Stm);
Stm.Position := 0;
Memo.Clear;
Edit.Text := IntToStr(Integer(Stm));
except
Stm.Free;
raise;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Stm: TMemoryStream;
begin
Stm := Pointer(StrToInt(Edit.Text));
try
Memo.Lines.LoadFromStream(Stm);
finally
Stm.Free;
end;
end;
I am a newbie in Delphi programming and I need some help. I have a problem with spliting my serial data. This is my code:
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
DataByte : string;
x, i: integer;
save_data : TStringList;
begin
save_data := TStringList.create;
for x := 0 to Count-1 do begin
ComPort1.ReadStr(DataByte,1);
if DataByte = 'n' then
begin
memo1.Text := '';
end
else
begin
memo1.Text := memo1.Text + DataByte;
Split(' ', DataByte, save_data);
end;
end;
save_gyroX := save_data[0];
save_gyroY := save_data[1];
save_gyroZ := save_data[2];
save_accelX := save_data[3];
save_accelY := save_data[4];
save_accelZ := save_data[5];
SerialProcess();
save_data.Free;
end;
My Split(' ', DataByte, save_data); doesn't work. I don't understand because I just split String data which is taken from the serial port. This is my Split() procedure:
procedure TForm1.Split(const Delimiter: Char; Input: string; const Strings: TStrings) ;
begin
Assert(Assigned(Strings));
Strings.Clear;
Strings.Delimiter := Delimiter;
Strings.DelimitedText := Input;
end;
I do not know why my program is giving me a EStringListError error.
You are calling ReadStr() to read individual bytes, and calling Split() on every byte (except for 'n'). So the TStringList will only ever hold 1 string at a time. Like MBo said, you need to fix your loop to avoid that, eg:
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
DataByte : string;
x: integer;
save_data : TStringList;
begin
ComPort1.ReadStr(DataByte, Count);
for x := 1 to Length(DataByte) do
begin
if DataByte[x] = 'n' then
begin
Memo1.Text := '';
end
else
begin
Memo1.Text := Memo1.Text + DataByte[x];
end;
end;
save_data := TStringList.create;
try
Split(' ', DataByte, save_data);
save_gyroX := save_data[0];
save_gyroY := save_data[1];
save_gyroZ := save_data[2];
save_accelX := save_data[3];
save_accelY := save_data[4];
save_accelZ := save_data[5];
SerialProcess();
finally
save_data.Free;
end;
end;
That being said, you are not taking into account that the number of bytes you receive for any given OnRxChar event call is arbitrary. It is whatever raw bytes have been read at that exact moment. You are assuming a full string with at least 6 delimited substrings, and that is simply not guaranteed. You need to buffer the raw data as you receive it, and then parse and remove only completed strings from the buffer as needed.
Try something more like this:
var
DataBuffer: string;
// consider using the OnRxBuf event instead...
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
DataByte : string;
x: integer;
save_data : TStringList;
begin
ComPort1.ReadStr(DataByte, Count);
DataBuffer := DataBuffer + DataByte;
x := Pos('n', DataBuffer);
if x = 0 then Exit;
save_data := TStringList.Create;
try
repeat
DataByte := Copy(DataBuffer, 1, x-1);
Delete(DataBuffer, 1, x);
Memo1.Text := DataByte;
Split(' ', DataByte, save_data);
if save_data.Count >= 6 then
begin
save_gyroX := save_data[0];
save_gyroY := save_data[1];
save_gyroZ := save_data[2];
save_accelX := save_data[3];
save_accelY := save_data[4];
save_accelZ := save_data[5];
SerialProcess();
end;
x := Pos('n', DataBuffer);
until x = 0;
finally
save_data.Free;
end;
end;
if Comport is Dejan Crnila CPort class, then this line
ComPort1.ReadStr(DataByte,1);
replaces Databyte contents every time, and this string always is 1-byte length.
Just read all bytes from buffer with single call
ComPort1.ReadStr(DataByte, Count);
and do work with string
I use the following code to eval the msg. content (body / lines) of an E Mail msg received with the INDY 10 components
function LinesFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
begin
for i := 0 to aMsg.MessageParts.AttachmentCount-1 do
begin
if (amsg.MessageParts.Items[i].ContentType ='HTML') then
begin
if (amsg.MessageParts.Items[i] is Tidtext) then
Result := TidText(amsg.MessageParts.Items[i]).body;
end;
end;
end;
regarding this code I have 2 questions :
a) is this the correct way of finding the Tlines part in an arbitray mail message ?
( consider the advice shown at INDY 10 EMAIL MSG PARTS )
b) where can I find a tutorial of all the different Contenttype string values?
The correct ContentType value to look for is text/html. Use Indy's IsHeaderMediaType() function to check it, as the ContentType value may have additional attributes associated with it that your comparison needs to ignore.
You also need to take the TIdMessage.ContentType into account as well, as HTML emails may not be MIME encoded and thus not use the TIdMessage.MessageParts` collection at all.
And lastly, you loop needs to use the MessageParts.Count property instead of the MessageParts.AttachmentsCount property.
Try this:
function HTMLFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
Part: TIdMessagePart;
begin
Result := nil;
if IsHeaderMediaType(aMsg.ContentType, 'text/html') then
begin
Result := aMsg.Body;
Exit;
end;
for i := 0 to aMsg.MessageParts.Count-1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part is TIdText) and IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
Result := TIdText(Part).Body;
Exit;
end;
end;
end;
With that said, this is technically not the correct way to handle MIME. Officially, a conforming reader is supposed to loop backwards through the MIME parts, as they are ordered from the simpliest form downwards towards the most complex form. So you loop backwards, taking MIME nesting into account, looking for the most complex form you support. Something more like this (untested):
procedure DisplayPlainText(Body: TStrings);
begin
// display plain text as needed...
end;
procedure DisplayHTML(Body: TStrings);
begin
// display html as needed...
end;
procedure DisplayMultiPartAlternative(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer:
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
end;
end;
// nothing supported to display...
end;
procedure DisplayMultiPartMixed(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer;
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'multipart/alternative') then
begin
DisplayMultiPartAlternative(aMsg, ParentPart.Index, aLastIndex);
Exit;
end;
if IsHeaderMediaType(ParentPart.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
aLastIndex := i;
end;
end;
// nothing supported to display...
end;
procedure DisplayMsg(aMsg: TIdMessage);
var
ContentType: string;
begin
ContentType := ExtractHeaderMediaType(aMsg.ContentType);
case PosInStrArray(ContentType, ['multipart/mixed', 'multipart/alternative', 'text/html', 'text/plain'], False) of
0: begin
DisplayMultiPartAlternative(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
1: begin
DisplayMultiPartMixed(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
2: begin
DisplayHTML(aMsg.Body);
Exit;
end;
3: begin
DisplayPlainText(aMsg.Body);
Exit;
end;
else
// nothing supported to display...
end;
end;