delphidllaccess-violation

Delphi: Dll -> DataSet.First causes Access Violation


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!


Solution

  • 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.