【问题标题】:Errors in Delphi while trying to load procedures from dll尝试从 dll 加载程序时 Delphi 中的错误
【发布时间】:2014-09-28 16:35:58
【问题描述】:

我在从 dll 加载过程时遇到问题,无论是动态加载还是静态加载。当我将程序从 dll 放到我的单元时,一切正常。当我尝试用 dll 来做这件事时,它给了我

00526399 美元的第一次机会例外。异常类 $C0000005 带有消息“0x00526399 处的访问冲突:读取地址 0x00000390”。处理 Project1.exe (21988)

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls,Unit2;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Refresh;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;


implementation

type
plist = ^element;
element = record
  artist,title,genre: string[20];
  year,grade: integer;
  wsk: plist;
end;
database = file of element;

var
base: database;
first: plist;
handler: HModule;
{$R *.dfm}



procedure TForm1.Refresh();
var
current: plist;
begin
  ListView1.Clear;
  current:= first;
  while current<>nil do
  begin
    with ListView1.Items.Add do
    begin
      Caption:=current^.artist;
      SubItems.Add(current^.title);
      SubItems.Add(current^.genre);
      SubItems.Add(IntToStr(current^.year));
      SubItems.Add(IntToStr(current^.grade));
    end;
    current:=current^.wsk;
  end;
end;



procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var Save: procedure;
begin
handler:=LoadLibrary('lib.dll');
try
  @Save:=GetProcAddress(handler, PChar(2));
  if @Save = nil then raise Exception.Create('Load nie dziala');
  Save();
finally
FreeLibrary(handler);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
Load: procedure;
begin
handler:=LoadLibrary('lib.dll');
try
  @Load:=GetProcAddress(handler, PChar(1));
  if @Load = nil then raise Exception.Create('Load nie dziala');
  Load();
finally
FreeLibrary(handler);
end;
Refresh();
end;

procedure TForm1.Button1Click(Sender: TObject);
var
el: element;
Add: procedure(el:element);
begin
el.artist:=Edit1.Text;
el.title:=Edit2.Text;
el.genre:=Edit3.Text;
el.year:=StrToInt(Edit4.Text);
el.grade:=StrToInt(Edit5.Text);
handler:=LoadLibrary('lib.dll');
try
  @Add:=GetProcAddress(handler, PChar(3));
  if @Add = nil then raise Exception.Create('Load nie dziala');
  Add(el);
finally
FreeLibrary(handler);
Refresh();
{Form2:=TForm2.Create(Form1);
Form2.ShowModal;
Form2.Free;}
end;
end;
end.

dll 文件如下所示:

  library lib;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  System.SysUtils,
  System.Classes;

{$R *.res}

type plist = ^element;
element = record
  artist,title,genre:string[20];
  year,grade:integer;
  wsk: plist;
end;
database = file of element;

var
first: plist;
base: database;

procedure add(el: element); stdcall;
var current,tmp: plist;
begin
New(current);
current^ := el;
current^.wsk := nil;
if first = nil then
begin
  first:=current;
end else
begin
  tmp:=first;
  while tmp^.wsk<>nil do
  begin
    tmp:=tmp^.wsk;
  end;
  tmp^.wsk:=current;
end;

end;

procedure load();stdcall;
var
  el: element;
  i: integer;
begin
  AssignFile(base, 'baza.dat');
  if not FileExists('baza.dat') then
  begin
    Rewrite(base);
  end else
  begin
    Reset(base);
    for i := 0 to FileSize(base)-1 do
    begin
        read(base, el);
        add(el);
    end;
  end;
  CloseFile(base);
end;

procedure save();stdcall;
var
current: plist;
el: element;
begin
  AssignFile(base, 'baza.dat');
  Rewrite(base);
  current:=first;
  while current<>nil do
  begin
    el:=current^;
    el.wsk:=nil;
    write(base, el);
    current:= current^.wsk;
  end;
end;

exports
add index 1,
load index 2,
save index 3;
begin
end.

它还显示了一个错误:

预期的';'但在第 91 行收到了标识符“index”

但是导出就像我在网络上做的那样。

【问题讨论】:

    标签: delphi dll delphi-xe2


    【解决方案1】:

    明显的错误是:

    • 您不会执行太多错误检查。您假设对LoadLibrary 的调用总是成功的。
    • 调用约定不匹配。您在 DLL 中使用 stdcall,在可执行文件中使用 register
    • 序数不匹配。在 DLL 中,它是添加 (1)、加载 (2) 和保存 (3)。在可执行文件中添加 (3)、加载 (1) 和保存 (2)。
    • 每次调用 DLL 中的函数时,都会加载和卸载 DLL。这意味着每次卸载 DLL 时,DLL 中保存您的状态的全局变量都会丢失。

    坦率地说,这段代码真是一团糟。我建议您执行以下操作:

    1. 切换到使用函数名而不是序号的加载时间链接。这意味着在可执行文件中使用external 关键字。通过删除对LoadLibraryGetProcAddress 等的所有调用,这将大大简化您的代码。如果需要运行时链接,您可以稍后使用delayed 关键字添加它。
    2. 停止在 DLL 中使用全局状态,而是在模块之间来回传递信息。删除所有全局变量。但请确保不要来回传递 Delphi 对象。
    3. 使用PChar 而不是跨模块边界的短字符串。
    4. 停止使用链表和动态分配。这很难做对。在 DLL 中使用TList&lt;T&gt; 来存储元素列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 2012-05-09
      • 2011-04-28
      • 2011-10-13
      相关资源
      最近更新 更多