2014-09-28 73 views
2

從DLL加載過程時,無論是動態加載還是靜態加載過程中遇到問題。當我把程序從dll傳遞給我的單元時,一切正常。當我試着使用DLL來做到這一點它給了我德爾福在嘗試從DLL加載過程時出錯

First chance exception at $00526399. Exception class $C0000005 with message 'access violation at 0x00526399: read of address 0x00000390'. Process 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. 

這也說明我的錯誤:

Expected ';' but received and identifier 'index' at line 91

但出口完成就像我在網上紅。

回答

5

明顯的錯誤是:

  • 你不進行太多的錯誤檢查。您認爲對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<T>來存儲元素列表。