Issues with using a record type and using getmem

I have a record type defined.  There are several fields, and I want to
assign to them from a custom class: (D2010, win7)


<code>
type tmyrec record
  field1: pchar;
...
end;

type tmyclass = class
  ffield1: string;
  public
    property field1: string read ffield1 write ffield1;
    procedure assignto(var aMyRec: tmyrec);
    procedure freeMem(var aMyRec: tmyrec);
end;

...

procedure tmyclass.assignto(var aMyRec: tmyrec);
begin
  if not assigned(aMyRec.field1) then getmem(aMyRec.field1,4);
  aMyRec.field1 := pchar(self.field1);
end;                                   < access violation here.

procedure tmyclass.freeMem(var aMyRec: tmyrec);
begin
  system.freemem(aMyRec.field1);
end;

....

Calling program does this:

procedure myproc(oMyClass: TMyClass);
var
  myRec: tMyRec;
begin
  oMyClass.assignto(myRec); < access violation here.
end;
</code>

The problem is, I get a access violation when the assignto method
exits.   oMyClass has been created and populated.

Also, the first time I ran the program (in the IDE), the statement   
  assigned(aMyRec.field1) 
evaluated to false.  In subsequent runs, it evaluates to True. I infer
that somehow myRec is never going out of scope.
0
jeremy
9/24/2010 10:17:12 PM
📁 embarcadero.delphi.win32
📃 2183 articles.
⭐ 0 followers.

💬 9 Replies
👁️‍🗨️ 574 Views

jeremy grand wrote:

> I have a record type defined.  There are several fields, and I want to
> assign to them from a custom class: (D2010, win7)
> 
> 
> 
> procedure tmyclass.assignto(var aMyRec: tmyrec);
> begin
>   if not assigned(aMyRec.field1) then getmem(aMyRec.field1,4);
>   aMyRec.field1 := pchar(self.field1);
> end;                                   < access violation here.
> 

I don't see a reason to allocate memory by yourself why not leave
Delphi to do that? You are not allocating enough space except if your
string is always a single character. You should write:
{code}GetMem(aMyRec.field1, (1+length(self.field1)) * SizeOf(Char));
{code}

I tried with Delphi 2007 even when I allocated less memory there was no
AV error.
0
Lajos
9/24/2010 11:46:09 PM
In your code:
{code}
procedure myproc(oMyClass: TMyClass);
var
myRec: tMyRec;
{code}
The myRec is not initialized.
So the myRec.field1 PChar is not necessary nil.
So it's not allocated in AssignTo
So there is an access violation.

Just initialize the myRec content:

{code}
procedure myproc(oMyClass: TMyClass);
var
myRec: tMyRec;
begin
  fillchar(myRec,sizeof(myRec),0);
  oMyClass.assignto(myRec); 
end;
{code}
0
Arnaud
9/25/2010 11:03:01 AM
Lajos Juhasz wrote:

> I don't see a reason to allocate memory by yourself why not leave
> Delphi to do that? You are not allocating enough space except if your
> string is always a single character. You should write:
> {code}GetMem(aMyRec.field1, (1+length(self.field1)) * SizeOf(Char));
> {code}

Lajos, thanks.  I tried both approaches just now.  Turns out that one
of the pchar fields I was declaring was being assigned a string of
length 255.  There apparently is a limit on how long a pchar can be,
but I don't see it in the literature.


And I did do what Arnaud suggests -- initialize the record var. Doing
that, the assigned(myrec.field1) function returns false.

But I'm still confused about memory allocation. Are you saying that
delphi will allocate the memory, and then free it again when the record
goes out of scope?

My overall goal is to use the record to pass parameters into a dll, and
that's what got me started on explicit allocation of memory.  One pchar
field in the record is supposed to hold result text and will be
populated by the dll.  Surely that one needs to be allocated prior to
calling the dll.


Jeremy
0
jeremy
9/25/2010 5:08:43 PM
jeremy grand wrote:

> Lajos, thanks.  I tried both approaches just now.  Turns out that one
> of the pchar fields I was declaring was being assigned a string of
> length 255.  There apparently is a limit on how long a pchar can be,
> but I don't see it in the literature.

Well, I was wrong.  There is another issue corrupting my record in the
dll.  Haven't found it yet, but shortening my result string just kept
the corruption from causing a fault.  Pure coincidence.
0
jeremy
9/25/2010 5:40:15 PM
"jeremy grand" <jeremy@ninprodata.com> schreef in bericht 
news:289401@forums.embarcadero.com...
>I have a record type defined.  There are several fields, and I want to
> assign to them from a custom class: (D2010, win7)
>
>
> <code>
> type tmyrec record
>  field1: pchar;
> ..
> end;
>
> type tmyclass = class
>  ffield1: string;
>  public
>    property field1: string read ffield1 write ffield1;
>    procedure assignto(var aMyRec: tmyrec);
>    procedure freeMem(var aMyRec: tmyrec);
> end;
>
> ..
>
> procedure tmyclass.assignto(var aMyRec: tmyrec);
> begin
>  if not assigned(aMyRec.field1) then getmem(aMyRec.field1,4);
>  aMyRec.field1 := pchar(self.field1);
> end;                                   < access violation here.
>
> procedure tmyclass.freeMem(var aMyRec: tmyrec);
> begin
>  system.freemem(aMyRec.field1);
> end;
>

A Pchar is just a buffer (of char). When you want to copy data from your 
TmyClass.field1 (a string) to aMyRec.field1 (a Pchar), you need to create 
enough space in the buffer to hold the string. Your getmem call allocates 
only 4 bytes to the buffer. Shouldn't that be length(ffield1)+1 instead? The 
+1 being for a terminating zero byte.
Tom
0
Tom
9/25/2010 7:16:11 PM
Tom deNeef wrote:

> When you want to copy data from your TmyClass.field1 (a string) to
> aMyRec.field1 (a Pchar), you need to create enough space in the
> buffer to hold the string. Your getmem call allocates only 4 bytes to
> the buffer. Shouldn't that be length(ffield1)+1 instead? The +1 being
> for a terminating zero byte.  Tom

Yes, I'm doing that now.  But also, I stumbled onto strPCopy in my
searching, and that fixes the AV problem.  I was assigning
PChar(mystring), and that, if I understand correctly, doesn't copy the
string, it just assigns a pointer.  So progress is being made.

I'm now confronted with what EurekaLog calls a Memory Overrun leak.
But that is way ahead of where I was.  This has something to do with
copying a string that is too long for the allocated memory I think. So,
we're taking ground from the enemy but paying for it in lots of
frustration.

Jeremy.
0
jeremy
9/25/2010 10:45:25 PM
Tom deNeef wrote:

 
> A Pchar is just a buffer (of char). When you want to copy data from
> your TmyClass.field1 (a string) to aMyRec.field1 (a Pchar), you need
> to create enough space in the buffer to hold the string. Your getmem
> call allocates only 4 bytes to the buffer. Shouldn't that be
> length(ffield1)+1 instead? The +1 being for a terminating zero byte.
> Tom

Tom It should be (length(ffield1)+1)*SizeOf(char)) as I wrote in my
previous post. Delphi 2009 SizeOf(char)=2 as it's unicode.

I was a bit fast on testing and followed your wrong design. Of course
you have to copy the string as Delphi would just redirect the pointer
and have the errors you got.

My next guess would be:

{code}
procedure tmyclass.assignto(var aMyRec: tmyrec);

var llen: integer;

begin
  llen:=length(self.field1)
  if Assigned(aMyrec.field1) then
    freeMem(aMyRec.field1);

  GetMem(aMyRec.Field1,(llen+1)*SizeOf(char));
  StrLCopy(aMyRec.Field1, PChar(lTempStr), llen);
end;
{code}


This code will free the previous value (we don't need a memory leak).
Allocate the memory for the new string. and finally to copy the string.

>  One pchar field in the record is supposed to hold result text and 
> will be populated by the dll.

If the dll needs to change the PChar you should also send the size of
the PCHAR or allocate buffer large enough. Can you show the entire
record and some code in the DLL how you populate the PCHAR?
0
Lajos
9/25/2010 11:49:49 PM
Lajos Juhasz wrote:

> It should be (length(ffield1)+1)*SizeOf(char))

That disposes of the last error message -- I was thinking the 0
position (the length position) would be 1 byte long [length*size+1],
but apparently it wants to be sizeOf(char) wide [(length+1)*size].

Friends, thank you for your help on this. I believe I can now move on
to producing actual functionality (g).

Jeremy
0
jeremy
9/26/2010 1:11:58 AM
jeremy grand wrote:

> Lajos Juhasz wrote:
> 
> > It should be (length(ffield1)+1)*SizeOf(char))
> 
> That disposes of the last error message -- I was thinking the 0
> position (the length position) would be 1 byte long [length*size+1],
> but apparently it wants to be sizeOf(char) wide [(length+1)*size].
> 
> Friends, thank you for your help on this. I believe I can now move on
> to producing actual functionality (g).
> 
> Jeremy

I'm glad we solved this problem. 

Pchar is a null terminated string. In this case it's terminated with a
Unicode #0 that is with #0#0.


Lajos

PS. I would like to thank to Rudy for writing his article on strings
and pchar.
0
Lajos
9/26/2010 1:46:35 AM
Reply: