遇到了一个大问题:Delphi 2010中的TDictionary和dll!

use*_*386 1 delphi delphi-2010

当我试图从动态加载的dll中访问主机程序中的TDictionary变量时,我遇到了一个非常严重的问题.这是完整的代码,任何人都可以提供一些帮助?谢谢!

===========主程序项目源代码===================

program main;

uses
  ShareMem,
  Forms,
  uMain in 'uMain.pas' {Form1},
  uCommon in 'uCommon.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Run Code Online (Sandbox Code Playgroud)

==============单位uMain ================

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, uCommon;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


var
  Form1: TForm1;


implementation

{$R *.dfm}


type
  Tfoo = function(ADic: TMyDic): string; stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
  Dic: TMyDic;
  HLib: THandle;
  foo: Tfoo;
begin
  Dic := TMyDic.Create;
  try
    Dic.Add(1, 'World!');
    Dic.Add(2, 'Hello, ');

    HLib := LoadLibrary('Mydll.dll');
    try
      @foo := GetProcAddress(HLib, 'foo');
      ShowMessage(foo(Dic));
    finally
      FreeLibrary(HLib);
    end;
  finally
    Dic.Free;
  end;
end;

end.
Run Code Online (Sandbox Code Playgroud)

================= dll项目源代码=====================

library MyDll;

uses
  ShareMem,
  SysUtils,
  Classes,
  uCommon in 'uCommon.pas';

function foo(ADic: TMyDic):string; stdcall;
var
  I: Integer;
  S: string;
begin
  for I in ADic.Keys do
  begin
    S := S + ADic[I];
  end;
  Result := s;
end;


exports
  foo;

end.
Run Code Online (Sandbox Code Playgroud)

================ unit uCommon ==============

unit uCommon;

interface
uses
  SysUtils, Generics.Collections;

type
  TMyDic = TDictionary<Integer, string>;



implementation

end.
Run Code Online (Sandbox Code Playgroud)

Mas*_*ler 5

你有例外吗?可能是访问冲突或无效的指针操作?

如果DLL有自己的内存管理器,则无法在Delphi和DLL之间共享字符串和对象.由于您使用的是Delphi 2010,因此默认情况下应安装FastMM.添加"SimpleShareMem"作为DLL和EXE 的使用列表中的第一件事,看看是否无法解决问题?

编辑:回应海报的其他信息:

卸载DLL后,您正在调用dic.free.即使你共享内存管理器,也会给你一个访问冲突.这就是原因.

免费调用TObject.Destroy,这是一个虚方法.编译器生成代码以在对象的虚拟方法表中查找它.但是VMT存储在特定于模块的静态存储器中,而不是存储器管理器分配的共享存储器中.您卸载了DLL并从对象中的VMT指针下方拉出了地毯,因此当它尝试调用虚拟方法时会出现访问冲突.

您可以通过确保卸载DLL 之前调用Free来解决此问题.或者您可以使用运行时包而不是DLL,这可以通过将对象的VMT放在外部包中来解决此问题,该外部包在完成之前不会被卸载.


All*_*uer 5

我强烈反对在可执行文件和常规DLL之间传递对象实例.主要是出于您遇到的确切原因.如果重建DLL并且您以某种不兼容的微妙方式更改了对象会发生什么?

正如Mason指出的那样,包是将应用程序划分为模块的首选方式.

  • 而且对象不需要改变自己.如果我错了,请纠正我,但仅编译器指令可以改变对象的二进制表示.(如果数据对齐对对象没有影响,我很确定"最小枚举大小"开关会). (3认同)