2011-10-04 145 views
3

我無法從C頭中將類轉換爲在Delphi中使用。將C頭中的__declspec轉換爲Delphi

在C頭文件中的說明的一個片段是這樣的:

class __declspec(uuid("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")) 
ISomeInterface 
{  
public: 
    virtual 
    BOOL 
    SomeBoolMethod(
    VOID 
) const = 0; 
} 

我正在寫導出接受一個I​​SomeInterface參數的方法的DLL,例如

function MyExportFunc (pSomeInterface: ISomeInterface): Cardinal; export; stdcall; 
var 
    aBool: BOOL; 
begin 
    aBool := pSomeInterface.SomeBoolMethod; 
end; 

我已經聲明ISomeInterface在德爾福這樣的:

type ISomeInterface = class 
    function SomeBoolMethod: BOOL; cdecl; virtual; abstract; 
end; 

調用訪問衝突的pSomeInterface.SomeBoolMethod結果。

我在做一些根本性的錯誤嗎?

實際的C頭是httpserv.h,我試圖在Delphi中實現IIS7本機模塊。

一些C++代碼,其工作原理是這樣的:

HRESULT 
__stdcall 
RegisterModule(
    DWORD       dwServerVersion, 
    IHttpModuleRegistrationInfo * pModuleInfo, 
    IHttpServer *     pHttpServer 
) 
{ 
    // etc 
} 

當調試我看到pModuleInfo參數包含具有下它6元__vfptr構件(名爲[0]〜[5]和具有地址作爲值),我推斷指針IHttpModuleRegistrationInfo類中的虛擬方法。

德爾福RegisterModule出口現在看起來像這樣:

function RegisterModule (dwServerVersion: DWORD; var pModuleInfo: Pointer; var pHttpServer: Pointer): HRESULT; export; stdcall; 
begin 
    // etc 
end; 

pModuleInfo包含在CPP例子的等效地址到__vfptr構件,並且假定在__vfptr的順序是相同的標頭中的類聲明文件I提取方法地址:

function RegisterModule (dwServerVersion: DWORD; var pModuleInfo: Pointer; var pHttpServer: Pointer): HRESULT; export; stdcall; 
var 
    vfptr: Pointer; 
    ptrGetName: Pointer; 
    ptrGetId: Pointer; 
begin 
    vfptr := pModuleInfo; 
    ptrGetName := Pointer (Pointer (Cardinal(vfptr))^); 
    ptrGetId := Pointer (Pointer (Cardinal(vfptr) + 4)^); 
end; 

我現在有方法地址來調用,所以現在我只需要以某種方式調用它。儘管我可能會以這種錯誤的方式進行討論!

+2

該類將很難轉換爲Delphi。首先,它不是一個真正的COM風格的界面。其次,它的方法使用Microsoft的默認調用約定來調用方法,這就是thiscall; Delphi不支持該調用約定,因此沒有Delphi代碼可以調用它。如果您對C++代碼有任何影響,請將其更改爲與其他Windows API兼容的內容。 –

回答

1

__declspec(uuid將UUID附加到類定義中,以便編譯器在請求__uuidof operator時可以將其應用到代碼中的其他位置。

也就是說,如果你正在移植到Delphi,你基本上可以從類中省略這個規範。至於接口,當你用Delphi聲明一個接口時,你肯定有機會爲這個定義提供一個IID。

代碼中的訪問衝突與__declspec並不完全相關。您的C++ ISomeInterface不完全是一個接口,因爲它不是從IUnknown繼承。 IIRC,與德爾福你不能聲明這種類型的接口,無論你聲明必須從至少IUnknown派生。所以沒有辦法安全和方便地轉換接口(確切地說,這個類 - 因爲它不是一個有效的COM接口定義)。

這裏是匹配/轉換做得正確:

MIDL_INTERFACE("56a86897-0ad4-11ce-b03a-0020af0ba770") 
IReferenceClock : public IUnknown 
{ 
public: 
    virtual HRESULT STDMETHODCALLTYPE GetTime( 
     /* [out] */ REFERENCE_TIME *pTime) = 0; 
// ... 

而且從http://code.google.com/p/dspack/source/browse/trunk/src/DirectX9/DirectSound.pas#456

type 
    IReferenceClock = interface(IUnknown) 
    ['{56a86897-0ad4-11ce-b03a-0020af0ba770}'] 
    // IReferenceClock methods 
    function GetTime(out pTime: TReferenceTime): HResult; stdcall; 
// ... 
+0

你如何解釋AV?根據Hallvard的文章,Q中的代碼看起來很好嗎? –

+0

原始文章中的C++和Delphi接口無法比擬,根本沒有機會。讓我們看看類/接口虛擬方法表:在C++類中,只有一個虛擬方法SomeBoolMethod(我們現在可以忽略它的約定)。如果Delphi的接口按照你的建議聲明爲'interface',那麼它的第一個虛擬方法就是IUnknown :: QueryInterface,SomeBoolMethod將是第四個,這在C++ VMT中是不存在的。由於OP在Delphi中將該類聲明爲'class',所以C++和Delphi'class'es也不兼容聲明。所以Access Violation只是保證。 –

+0

使東西兼容的方法是從'IUnknown'派生C++類,在Delphi端聲明它爲'interface',然後檢查/匹配約定和參數。 –

3

其德爾福雙胞胎不要聲明爲cdecl。 32位COM方法的調用約定是C被稱爲stdcall的別名,別名CALLBACK和WINAPI。看看德爾福是否有相同的一個。如果Delphi支持COM,它就有一個。

此外,請確保C接口不是從IUnknown派生的 - 實際上所有的COM接口都是這樣做的。如果是的話,從Delphi的等價物中派生出你的接口。

此外,我不確定Delphi中的對象佈局是否與COM(作爲第一個數據項的虛擬函數指針表)匹配。再次,找到Delphi實現COM的方式。

+0

Delphi中的接口不使用類似於C++的抽象類。 –

+0

雖然他們可能需要在這裏,因爲沒有IUnknown –

+0

在Delphi中,'IInterface'等同於'IUnknown'。 –

2

在Delphi中,無論您是否指定,所有類都從TObject派生。這意味着下面的Delphi聲明:

type 
    ISomeInterface = class 
    function SomeBoolMethod: BOOL; cdecl; virtual; abstract; 
    end; 

是與此相同:

type 
    ISomeInterface = class(TObject) 
    function SomeBoolMethod: BOOL; cdecl; virtual; abstract; 
    end; 

這使得ISomeInterface類在Delphi比在C中ISomeInterface類的VMT不同++,它可以的VMT導致AVs。

同樣地,如其他人提及的,宣告的Delphi ISomeInterface類型作爲interface代替class將implicitally派生它從IUnknown,其中C++類是沒有做,無論是。

簡而言之,Delphi根本無法重現C++可以使用的普通香草類類型的類型。在Delphi中最接近(不改變C++代碼以使用與Delphi更兼容的東西)是聲明record類型,而不是class類型或interface類型。但是,由於使用了虛擬方法,因此必須在Delphi中手動重現C++ VMT,並且必須聲明所有方法都具有this(Delphi192中的Self)指針的顯式參數。

否則,請同時切換C++代碼和Delphi代碼以使用COM接口,這是一種二進制標準,因此兩種語言都將相互兼容。

+1

儘管Delphi中的TObject類具有虛擬方法,但它們在VMT中都具有負偏移 - TObject類使用編譯器魔術。我不明白爲什麼Delphi類不能重現C++類的VMT。 – kludg

+1

它不是單獨的VMT。這些方法可能必須使用MS-C++的* __ thiscall *約定來調用。 Delphi不能輕易地產生模仿它的函數或方法。它需要彙編程序,並使功能無法從德爾福方面。 –

+0

@Rudy - 是的,默認'thiscall'可能是AV的原因。在Delphi端使用'thiscall'很棘手 - 請參閱http://stackoverflow.com/questions/2667925/pass-a-delphi-class-to-ac-function-method-that-expects-a-class-with-thiscall 。現在我看到,舊問題的作者在虛擬析構函數方面存在問題(它在Delphi VMT中具有負偏移量,應該是'thunked'),但現在沒有虛擬析構函數。 – kludg