I have a simple Dll written in Delphi:
library usr_d;
uses
System.SysUtils, System.Classes, DB,Vcl.Dialogs
;
{$R *.res}
Procedure SetMyData(DataSet: TDataSet);export;
begin
if Assigned(DataSet) then
begin
ShowMessage(DataSet.FieldByName('MyFieldName1').AsString);
try
DataSet.First;
except on E: Exception do
ShowMessage('Error accessing field: ' + E.Message);
end;
end
else
ShowMessage('DataSet parameter is not assigned!');
end;
exports
SetMyData;
begin
end.
I call it this way:
Procedure TMainForm.CallMyDll;
type
TSetMyData = procedure(DataSet: TDataSet); stdcall;
Var
MyHandle : HMODULE;
SetMyData : TSetMyData;
Begin
MyHandle := LoadLibrary('c:\MyFolder\usr_d.dll');
if MyHandle <> 0 then
begin
@SetMyData := GetProcAddress(MyHandle, 'SetMyData');
if @SetMyData <> nil then
begin
SetMyData(MyQuery as TDataSet);
end;
FreeLibrary(MyHandle);
end;
End;
When I run the program and call the procedure of the Dll, the "ShowMessage(DataSet.FieldByName('MyFieldName1').AsString);" works correctly, it shows the field value. I tried it with a few different datasets.
However if I issue: "DataSet.First;" or "DataSet.Next;", then I get Access Violation, error accessing field.
What should I do? Thank you!
As previous respondents have explained, you cannot safely pass a Delphi object across exe/dll boundaries.
The reasons?
First of all, the exe and the dll have separate heap managers which will wreak total havoc because each of them thinks it is solely responsible for allocating and deallocating memory. There are ways to make them share a heap manager - google sharemem or simplesharemem.
Secondly, the Delphi smart linker removes any methods that aren't used in the exe but also does the same thing for the dll. So the memory layout of tDataset will be different in both modules which will most likely result in access violations. The only way to overcome that is by using packages.
But having said that, there are some datasets that can easily be shared between executable and DLL, for example if they are based on COM objects. For example, tAdodataset has a property "recordset" that contains the underlying data and tAdoConnection has a property "connectionobject" that contains the underlying database connection.
What I mean is, if a tAdoConnection in a DLL uses the same "connectionobject" as a tAdoconnection in an EXE, they basically share the same connection.