2013-07-10 70 views
3

下面的C++類foo.dll如何通過禁用名稱重整

class a{ 
    private: 
    int _answer; 

    public: 
    a(int answer) { _answer = answer; } 
    __declspec(dllexport) int GetAnswer() { return _answer; } 
} 

我想從C#中的PInvoke的getAnswer的PInvoke一個實例方法。要做到這一點,我用下面的方法:

[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint= "something")] 
public static extern int GetAnswer(IntPtr thisA); 

我傳遞一個指向一個(我從別的地方得到的,這不重要)一個IntPtr。 CallingConvention = CallingConvention.ThisCall確保它的處理正確

這個問題最棒的是我知道我是對的,因爲它已經很好用了!使用Depends.exe,我可以看到「GetAnswer」被導出爲?GetAnswer @ a @@ UAEHXZ(或者某個關閉 - 重點在於它已被名稱損壞)。當我將入侵的名字插入EntryPoint的「東西」時,一切都很好!我花了大約一天的時間才發現使用Depends.exe,因此我將在此留下,以幫助任何有類似問題的人。

我真正的問題是:有沒有什麼辦法來禁用C++名稱重整上的getAnswer讓我不需要把錯位的名字作爲我的切入點。在那裏看到這個名字會被打破,因爲我對名字重組的理解是,如果編譯器改變了,它就會改變。對於我想要pInvoke的每個實例方法,使用Depends.exe都是一個痛處。

編輯:忘了添加我試過的東西: 我似乎不能把extern「C」放在函數聲明中,雖然我可以把它放在定義上。這似乎並沒有幫助,雖然

唯一的其他解決方案,我能想到的是C風格的功能封裝了實例方法和採用的一個實例作爲一個(當你想想看,這是顯而易見的)參數。然後,禁用該包裝器上的名稱修改,然後pInvoke。不過,我寧願堅持已有的解決方案。我已經告訴我的同事pInvoke很棒。如果我必須在C++庫中添加特殊函數才能使pInvoke正常工作,那麼我會看起來像一個白癡。

+0

可能重複的[P/Invoke。如何使用C#編組調用非託管方法?](http://stackoverflow.com/questions/9211128/p-invoke-how-to-call-unmanaged-method-with-marshalling-from-c) –

+0

名稱裝飾實際上非常有用,當您忘記更改pinvoke聲明時,當C++代碼發生更改而不是發生討厭的AccessViolation時,您將得到非常好的錯誤。其他尋找名字的簡單方法是'dumpbin/exports foo.dll'和鏈接器的.map文件。你不能捏住這個函數,需要一個C++/CLI包裝器。 –

+0

@HansPassant你是什麼意思,你不能捏?我已經在這麼做了! –

回答

3

您不能禁用重整的C++類的方法,但你可能能夠出口下,使用/EXPORT或DEF文件您選擇的名稱的功能。

然而,你的整個做法是脆的,因爲你靠的實現細節,即this作爲一個隱含參數傳遞。更重要的是,出口一個班級的個別方法是痛苦的祕訣。

用於暴露C++類到.NET語言最明智的策略是:

  1. 創建平坦C包裝功能和P /調用這些。
  2. 創建一個C++/CLI混合模式層,用於發佈包裝本機類的託管類。

選項2在我看來是最好。

+0

我不知道的人,thiscall似乎相當有據可查的是一個實現細節http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention .aspx http://msdn.microsoft.com/en-us/library/ek8tkfbw(v=vs.100).aspx。我會給def文件一個鏡頭,雖然 –

+0

後續:通過def文件重命名的作品,但有同樣的問題,邁克爾Goldshteyn指出。你必須添加?GetAnswer @ a @@ UAEHXZ =對def文件的GetAnswer,它要求您先驗地知道損壞的名稱。 –

1

您可以使用註釋/連接器的#pragma傳遞/EXPORT開關的連接應允許重新命名導出的符號:

#pragma comment(linker, "/EXPORT:[email protected]@@UAEHXZ") 

不幸的是,這並沒有解決您的需要使用依賴或其他工具查找錯誤的名稱。

+0

所以我很清楚,在閱讀完文檔後,我相信雜注將「?GetAnswer @ a @@ UAEHXZ」轉換爲導出表中的「GetAnswer」。要使用它,我需要編譯我的C++庫,查找重名的名稱,然後用那個#pragma重新編譯庫,對吧? –

+0

簡而言之,是的...... –

0

從提問者:我實際上

去解決方案我結束了與C風格的功能封裝了實例方法,並採取了一個作爲參數的實例去。那樣的話,如果這個類從來沒有被繼承,那麼正確的虛擬方法就會被調用。

我故意不跟C++/CLI,因爲它只是多了一個項目來管理。如果我需要使用一個類的所有方法,我會考慮它,但我真的只需要這一個序列化類數據的方法。

0

您不必禁用它實際上包含了很多的功能本身是如何申報信息的重整名稱,它基本上代表了功能的整個簽名函數名被取消後錯位。我知道你已經找到了一個字,另一個答案已被標記爲正確的答案。我在下面寫的是我們如何使它按照你的意願工作。

[DllImport("foo.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "#OrdinalNumber")] 
public static extern int GetAnswer(IntPtr thisA); 

如果與真實序號GetAnsweer的,如「#1」替換「#OrdinalNumber」,它將爲你想要的工作。

你可能只是考慮入口點屬性是相同的函數名我們傳遞給GetProcAddress在那裏你可以通過函數名或函數的序號。

你調用一個C的非靜態成員函數的做法++類確實是正確的,thiscall正確使用而這正是thiscall調用約定來在C#中的P/Invoke的發揮。這種方法的問題是你必須查看DLL的PE信息,導出函數信息並找出你想調用的每個函數的序號,如果你有大量的C++函數要調用,你可以想要自動化這樣一個過程。