?
Solved

Returning a Record Containing Various Data

Posted on 2005-03-20
5
Medium Priority
?
325 Views
Last Modified: 2010-05-18
In my app i have declared the following types:

type
  pRetData = ^TRetData;
  TRetData = record
    Data: TByteArray;
    Size: Integer;
  end;

I have a DLL with the following exported function:

function EndEdit: pRetData; stdcall;
var
  Data: TRetData;
begin
  MainForm.Hide;

  Data.Data := pByteArray(MainForm.Properties)^;
  Data.Size := Length(MainForm.Properties);

  Result := @Data;

  MainForm.Release;
end;

And In my App I load the DLL, get the address of the function and then do the following (The function is declared properly and I do load it properly):

procedure TPluginProperties.sButton1Click(Sender: TObject);
var
  PropData: String;
  Data: pRetData;
  i: Integer;
begin
  try
    if @EndEdit <> nil then
    begin
      PropData := '';

      Data := EndEdit;

      for i := 0 to Pred(Data^.Size) do
        PropData := PropData + Char(Data^.Data[i]);    // This is where it crashes

      TPlugin(Obj).Properties := PropData;
    end;
  except
  end;
end;

MainForm.Properties in the DLL is a string variable that contains various pieces of data including the Lines.Text property of a listbox.

If the listbox has only 1 item then the code works fine, but any more and it crashes the application of the indicated line. I can even read the contents of a binary file into MainForm.Properties and return it to the application successfully, so why does the contents of a listbox cause it to fail???
0
Comment
Question by:paulb1989
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
5 Comments
 
LVL 34

Accepted Solution

by:
Slick812 earned 2000 total points
ID: 13586873
hello  paulb1989 , , You code look like it is OK except for the line -

     for i := 0 to Pred(Data^.Size) do

which I think should be -

     for i := 0 to Pred(Data^.Size)-1 do

I do not think this would crash it, I would think from your description that there is something wrong about the way you load your list box to the MainForm.Properties, although you probally use a standard listbox method for strings, It may be that your MainForm.Properties  Length is more than the  32 Kilobytes that the  TByteArray  is set to, , The largest amount it can handle is 32 Kb.

any way. .  For me , I would use some different methods for this type of thing, here is some code I might use in the DLL library -



type
  PRetData = ^TRetData;
  TRetData = record
    DataType: Integer; // set to an integer so you can use more than one kind of data
    pData: Pointer; // changed this to a much more flexable type
                    // it now can do more than 32 Kbytes
 // if you are just going to use charater transfer, I would uses PChar type for pData
    Size: Integer;
  end;




// could not see why you used a Pointer type for result
// changed to Non-Pointer type
function EndEdit: TRetData; export;
var
  Str1: String;
begin
  //MainForm.Hide;
  Str1 := 'A few words to test this thing';
  Result.DataType := 1; // set to one for string type Data
  Result.pData := PChar(Str1);
  // pData can just be set to the address of ANY data you want to transfer
  Result.Size := Length(Str1);
  // I left this Size in it but may not be needed for charater transfer

  //MainForm.Release;
end;


 - - - - - - - - -  - - - - - - - - - - - - - - - - -  - - - - -

code in Program


procedure TForm1.sbut_LoadDLLClick(Sender: TObject);
var
  EndEdit: function: TRetData;
  RetData1: TRetData;
  Str1: String;
  hLib, i: Integer;
begin
hLib := LoadLibrary('demoDLL.dll');
if hLib < 32 then
  begin
  ShowMessage('Did not load the DLL');
  exit;
  end;

@EndEdit := GetProcAddress(hLib, 'EndEdit');
if @EndEdit = nil then
  begin
  ShowMessage('EndEdit is nil');
  Exit;
  end;

RetData1 := EndEdit;
if RetData1.DataType <> 1 then
  begin
  FreeLibrary(hLib);
  ShowMessage('Data Type is NOT String');
  Exit;
  end;

Str1 := PChar(RetData1.pData); // makes it easy to get string out
Showmessage(Str1+' '+IntToStr(PByteArray(RetData1.pData)[RetData1.Size-1]));
// you can TypeCast any to PByteArray to access the bytes in it

// more ways to get string out
Str1 := '';
SetLength(Str1, RetData1.Size);
StrLCopy(PChar(Str1), RetData1.pData, RetData1.Size);
ShowMessage(Str1);

Str1 := '';
for i := 0 to RetData1.Size-1 do
  Str1 := Str1+ Char(PByteArray(RetData1.pData)[i]);
ShowMessage(Str1);

FreeLibrary(hLib);
end;


 = = = = = = = = = = = =  = = = =  = = = = = =

maybe you can get some Ideas to help you from this
ask questions if you need more
0
 
LVL 5

Author Comment

by:paulb1989
ID: 13587274
OK you have said that the TByteArray has a 32kb limit so I wrote this:

type
  pExByteArray = ^TExByteArray;
  TExByteArray = array of Byte;

and used TExByteArray instead of TByteArray. However, my app crashes int he same place as before... I tried putting the -1 in the loop aswell, but that made no difference.

I cannot use a pChar variable as the string may contain null characters... Is there any way to make my TExByteArray work of another way to do this? I need to be able to return any type of data, and more than 32kb. The typical contents of the string I want to return will be built using the Add2String method of ExeMod.pas, and can contain the contents of a binary file aswell as other data.
0
 
LVL 34

Expert Comment

by:Slick812
ID: 13587537
I do not Understand some of your comment, I can not see any reason (at least from your comments so far) why you would want to use an array of Byte (of any size) for the data transfer, If you use that method you will need to copy the bytes to the byte array and then copy the bytes out of the byte array to get your data, for me it would seem to be more efficient just to copy the bytes out of the original Data memory segment, and not have to copy them. . . .

  In my code I use a Pointer to reference the Data you want to transfer, I tried to show you how to read the data into a string if it had Null characters (#0), it was the second method -

SetLength(Str1, RetData1.Size);
StrLCopy(PChar(Str1), RetData1.pData, RetData1.Size);

 this will transfer (copy) even null charaters in a string, you failed to mention about the null chararcter in the first so I had

Str1 := PChar(RetData1.pData);

which will only copy up to the first null. . .

but where or not you use string data or any data (binary as you call it) you should be able to use a Pointer type and then typecast that into any type you may need, or type it to a  PByteArray(RetData1.pData) and read any but you like, as with -

PByteArray(RetData1.pData)[i]


any way, ,  you say you want to use more than 32 kb in a Byte array, you can type an array to any size you need (I think maybe there is a 4 Gig limit, but not sure. .  something like

type
  PBigByteArray = ^TBigByteArray;
  TBigByteArray = array(0..MAXINT) of Byte;

HOWEVER  - I would NPT use this array as you have in your record -

pRetData = ^TRetData;
  TRetData = record
    Data: TBigByteArray;
    Size: Integer;
  end;

why?  because whenever this array is used it will require 2 gig of memory, so you will get an out of memory exception (if you no got 2 gig of memory), instead  I would use

pRetData = ^TRetData;
  TRetData = record
    Data: PBigByteArray;
    Size: Integer;
  end;

so you can assign you own memory to it when used , so with this you are back to a Pointer type and you can just type cast your data to it,
0
 
LVL 34

Expert Comment

by:Slick812
ID: 13587561
I think is you use a variable array like

TExByteArray = array of Byte;

you may have to include the ShareMem unit, which is OK I guess, but I highly recommend that you do not use the ShareMem unit
0
 
LVL 5

Author Comment

by:paulb1989
ID: 13587678
OK Thankyou I'm sorry I didn't realise what you were doing in the code the first time you posted, but now I get it and my app works fine. Thankyou !
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Michael from AdRem Software outlines event notifications and Automatic Corrective Actions in network monitoring. Automatic Corrective Actions are scripts, which can automatically run upon discovery of a certain undesirable condition in your network.…
In this video, Percona Director of Solution Engineering Jon Tobin discusses the function and features of Percona Server for MongoDB. How Percona can help Percona can help you determine if Percona Server for MongoDB is the right solution for …
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question