2009-10-07 50 views
0

我有一個用於規劃卡車等如何使用Delphi中的「_pascal調用約定」調用dll?

現在,我們希望使用德爾福2007年 我們有一個DLL中的C++頭路由程序RouteLogix一個dll RL6_dll.dll和工作的例子,在C++ Builder中使用它。

下面是該文件的一個示例:

// Use this routine to find the directory where the data-xxx subdirectories 
// are expected. 
// char * vszBuf - address of a character array to receive the (null-terminated) path. 
// int nBufSize - is the size of the array 
//     (internally we allow paths up to 256 characters long) 

DllFn(void) RL6_GetLocalGeoDir(char *vszBuf, int nBufSize); 

我試着從德爾福:

procedure TfrmRL6Xml.Button1Click(Sender: TObject); 
var 
    s1: PChar; 
    IntValue : Integer; 
    RL6_GetLocalGeoDir: function(vszBuf: pchar; nBufSize: Integer): integer; stdcall; 
begin 
    handle := LoadLibrary('C:\Carp\RL6_app2\rl6dll\RL6_DLL.dll'); 
    if handle <> 0 then 
    begin 
     @DllFn := GetProcAddress(handle, 'RL6_PREINIT'); 
     @RL6_GetLocalGeoDir := GetProcAddress(handle, 'RL6_GETLOCALGEODIR'); 

     s1 := '                                  '; 
     IntValue := length (s1); 
     RL6_GetLocalGeoDir (s1, IntValue); 
     showMessage(s1); 
    end; 
end; 

所以現在我期待S1包含字符串,而是功能似乎處理INTVALUE爲字符串。似乎s1和IntValue參數被交換。我們當然嘗試了RL6_GetLocalGeoDir(IntValue,s1),但那也不起作用。任何建議如何打電話給它?

回答

0

你需要調用程序具有預先分配的緩存,並用正確的聲明,就像這樣:

procedure TfrmRL6Xml.Button1Click(Sender: TObject); 
var 
    s: AnsiString; 
    IntValue : Integer; 
    RL6_GetLocalGeoDir: procedure(vszBuf: PAnsiChar; nBufSize: Integer); stdcall; 
begin 
    handle := LoadLibrary('C:\Carp\RL6_app2\rl6dll\RL6_DLL.dll'); 
    if handle <> 0 then 
    begin 
     @DllFn := GetProcAddress(handle, 'RL6_PREINIT'); 
     @RL6_GetLocalGeoDir := GetProcAddress(handle, 'RL6_GETLOCALGEODIR'); 

     IntValue := 256; 
     SetLength(s, IntValue); 
     RL6_GetLocalGeoDir (PAnsiChar(s), IntValue); 
     s := PAnsiChar(s); 
     showMessage(s); 
    end; 
end; 

編輯:

你修改的問題仍然包含惡意代碼。您使用

var 
    s1: PChar; 

s1 := '                                  '; 
IntValue := length (s1); 

這是錯誤的,因爲您不提供緩衝區,而是指向代碼段中的字符串常量的指針。使用這將導致死機,只是如API函數GetWindowsDirectory()嘗試:

var 
    P: PAnsiChar; 
begin 
    P := '                    '; 
    GetWindowsDirectory(P, 80); 
end; 

運行,這將導致訪問衝突ntddll.dll,在代碼區域(例如$0044C6C0)地址寫入。

+0

糟糕,我已經簡化了這個例子。該示例現在已得到糾正。 –

+2

我不認爲它被糾正了,你爲什麼要在'PChar'上調用'Length()'? – mghie

+0

最好使它PAnsiChar和AnsiString,因爲在C中「char」是1個字節的字符。 –

2

任務標題提到了Pascal調用約定,但問題主體永遠不會回到該主題。 DLL的文檔是否說它使用Pascal調用約定?這是現在使用的罕見調用約定。 (它在16位時間內被用在Windows API中,雖然今天某些標題仍然表示PASCAL,但該宏已被重新定義爲指代stdcall調用約定)。

您還沒有顯示了DllFn的定義 - 既不在C代碼中,也不在Delphi代碼中。在C中,我想象它是一個包含函數調用約定的宏,所以去找到這個定義來確認真正使用的是什麼。在Delphi中,它看起來像你正在使用它作爲函數指針。我鼓勵您在應用程序中使用與DLL使用相同的函數名稱。它讓每個參與者的生活變得更輕鬆 - 沒有人再看代碼,並且想知道什麼函數真的被調用。

如果確認該DLL真正使用Pascal調用約定,然後在Delphi中指定它爲改變「STDCALL」指令你的函數聲明爲「帕斯卡」那樣簡單:

RL6_GetLocalGeoDir: procedure(vszBuf: PAnsiChar; nBufSize: Integer); pascal; 

我也改變了PChar參數,使用PAnsiChar,因爲現在Delphi的某些版本是Unicode的,PChar類型可能意味着PWideChar,您不希望在這裏。