sta*_*rhu 0 delphi dll access-violation
我有一个用 Delphi 编写的简单 Dll:
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.
Run Code Online (Sandbox Code Playgroud)
我这样称呼它:
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;
Run Code Online (Sandbox Code Playgroud)
当我运行程序并调用Dll的过程时,“ShowMessage(DataSet.FieldByName('MyFieldName1').AsString);” 工作正常,它显示字段值。我用几个不同的数据集尝试过。
但是,如果我发出:“DataSet.First;” 或“DataSet.Next;”,然后我收到访问冲突,访问字段时出错。
我应该怎么办?谢谢你!
Rem*_*eau 12
当制作普通DLL 时,您只能跨 DLL 边界交换普通的 C 兼容类型(即整数、字符等)。您根本无法在普通DLL中跨 DLL 边界使用重要数据,例如 Delphi RTL/VCL 对象。
您需要创建一个包 (BPL),它是一种特殊类型的 DLL,具有对 RTL/VCL 框架的内置支持。
并且,为了在包中通过 DLL 边界共享 Delphi 对象,您还需要让 BPL 项目和调用项目(无论是 EXE 还是另一个 BPL)启用运行时包,以便它们可以共享单个RTL/VCL 框架实现的实例、内存管理器、RTTI 等。
如果无法将 DLL 重新设计成包,那么您将不得不改变整个方法。例如,您可以使用回调函数,以便与对象相关的所有内容都保留在 DLL 边界的一侧,例如:
MyCommon.pas
type
TMyCallbackFuncs = record
GetFieldStr: function(FieldName, Buffer: PChar; BufLen: Integer; UserData: Pointer): Integer; stdcall;
SetDataToFirst: procedure(UserData: Pointer); stdcall;
end;
Run Code Online (Sandbox Code Playgroud)
usr_d.pas
library usr_d;
uses
System.SysUtils, System.Classes, Vcl.Dialogs, MyCommon;
{$R *.res}
procedure SetMyData(var Callbacks: TMyCallbackFuncs; UserData: Pointer); stdcall; export;
var
Value: array[0..255] of Char;
begin
try
Callbacks.GetFieldStr('MyFieldName1', Value, Length(Value), UserData);
ShowMessage(Value);
Callbacks.SetDataToFirst(UserData);
except
ShowMessage('Error accessing field');
end;
end;
exports
SetMyData;
begin
end.
Run Code Online (Sandbox Code Playgroud)
CallMyDll.pas
uses
..., MyCommon;
function GetFieldStr(FieldName, Buffer: PChar; BufLen: Integer; UserData: Pointer): Integer; stdcall;
begin
StrPLCopy(Buffer, TDataSet(UserData).FieldByName(FieldName).AsString, BufLen-1);
Result := StrLen(Buffer);
end;
procedure SetDataToFirst(UserData: Pointer); stdcall;
begin
TDataSet(UserData).First;
end;
procedure TMainForm.CallMyDll;
type
TSetMyData = procedure(var Callbacks: TCallbackFuncs; UserData: Pointer); stdcall;
var
MyHandle : HMODULE;
SetMyData : TSetMyData;
Callbacks : TMyCallbackFuncs;
begin
MyHandle := LoadLibrary('c:\MyFolder\usr_d.dll');
if MyHandle <> 0 then
begin
@SetMyData := GetProcAddress(MyHandle, 'SetMyData');
if @SetMyData <> nil then
begin
Callbacks.GetFieldStr := @GetFieldStr;
Callbacks.SetDataToFirst := @SetDataToFirst;
SetMyData(Callbacks, MyQuery as TDataSet);
end;
FreeLibrary(MyHandle);
end;
end;
Run Code Online (Sandbox Code Playgroud)