尝试从dll加载过程时Delphi中的错误

时间:2014-09-28 16:35:58

标签: delphi dll delphi-xe2

从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行的标识符“索引”

但是出口就像我在网上红了一样。

1 个答案:

答案 0 :(得分:5)

明显的错误是:

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

坦率地说,这段代码真的很乱。我建议您执行以下操作:

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