2012-07-22 51 views
5

鑑於:可執行文件使用dll。他們有不同的c/C++運行時。它們之間的界面中存在哪些限制? 除了它們使用相同的編譯器,相同的Boost版本(但不同的預編譯boost庫)。exe與dll之間的接口與不同的C/C++運行時庫

我知道不同的運行時可以有不同的堆。所以,刪除必須對應新的來自同一堆。

最重要的是,我們不能通過接口STL對象傳遞,因爲當我們構建EXE STL對象鏈接到一個運行時 和建立dll相同的對象(如果我們通過引用傳遞它或通過接口複製)將鏈接另一個運行時 而另一個運行時可以對該對象有不同的實現。

讓我們考慮的情況:

  1. 我認爲以下是安全的。 Dll導出函數,它具有參數:對包含專用STL類作爲成員的導出的用戶定義類的引用。 Dll爲這個對象分配內存。 Exe調用此對象的發佈方法,當想要刪除它時。

  2. 我覺得以下是不安全的。用戶定義的類在exe中實例化並通過exe/dll接口傳遞。 該類包含專用STL類作爲成員。 exe和dll共享此用戶類的頭文件/實現文件。 當這個類被構建在不同的項目中時,將使用不同的STL實現。例如,string :: size()(來自不同運行時)的不同實現 將應用於內存中的同一對象。

  3. 我認爲以下是安全的。用戶定義的類在exe中實例化並通過exe/dll接口傳遞。 該類不依賴於標準庫,它只使用原始C++類型。 exe和dll共享此用戶類的頭文件/實現文件。 另外我們必須控制那個新的和刪除對應的相同的堆。例如,我們可以重載new/delete,使它們使用:: GetProcessHeap。

  4. 我認爲以下是不安全的:通過exe/dll接口傳遞boost對象,因爲它們可以依賴於標準庫類。另外刪除可能不對應新的堆。

  5. 我認爲以下是不安全的:即使我們通過exe/dll接口傳遞boost對象,並且它們不依賴於標準庫類但不能實現爲標頭 - 只能通過一個boost來創建對象lib(用於一個運行時),並與另一個boost lib(用於另一個運行時)一起使用。另外刪除可能不對應新的堆。

另外我想使用一些智能指針的味道從EXE到DLL以及從DLL到EXE傳遞對象的引用(在第3項中提到)。 我認爲這個智能指針還應該重載new/delete來從默認進程堆中分配引用計數器。 當它會嘗試刪除尖銳的物體,它會調用刪除此對象重載(如在項目3)

對於從第1項我想使用自定義的智能指針,它將調用尖銳物體 的釋放方法的對象(如boost :: shared_ptr自定義版本)

哪些問題沒有提及?請糾正我。

回答

4

您的問題的一般答案是:如果實際完成的內容不取決於運行時,則對從EXE/DLL接收的對象執行某些操作是安全的。例如。 vtable調用,函數指針調用,來自DLL的顯式函數調用。

依賴於運行時的事情(來自頭文件的內聯方法,對STL對象佈局做出任何假設的任何事情等)都是不安全的。

回來到您的實例:

  1. 如果你調用Release()方法,你要小心,並確保你會從DLL調用Release()的執行,而不是另一種實現,是由編譯器創建EXE文件創建的。確保它的最簡單方法是使Release()變爲虛擬,以便調用始終是使用vtable中的方法指針(由DLL提供)的調用。

  2. 正確的,來自STL的內聯方法如string :: size()會在這裏引發問題。

  3. 對。如果new/delete都重載以使用GetProcessHeap(),則代碼將工作。
  4. 一般來說,沒錯。具體來說,請參閱您需要的boost類的實現。
  5. 如果它們不依賴於STL,並且你確定兩個編譯器的內存佔用是相同的,並且你提供了自定義的new/delete(),它通常不應該是一個問題(見下面的註釋)。

備註:

有一些小概率的事情,可能會導致問題上面沒有提到的:

  1. 如果使用不同的編譯器,他們可能會在默認情況下使用不同的調用約定(CDECL/STDCALL)。
  2. 默認對齊選項也可能不同,從而導致不同的內存佈局。
  3. 如果從DLL中拋出異常,帶有不同運行時的exe可能無法捕獲它們,但會崩潰。
  4. 使用DLL導入/導出屬性標記方法並確保它們未內聯可能會非常麻煩。此外,如果您使用不同的編譯器,則C++名稱修改算法可能會有所不同。

總結了一些事情之後,建議看看如何在Microsoft COM中實現並設計類似的東西。即將EXE/DLL交互限制爲:

  1. 接口。即僅具有純虛擬方法的C++類。使用虛擬Release()刪除對象。
  2. 簡單明確定義的結構。確保所有編譯器的對齊選項都匹配。如果你想用不平凡的結構,並且使用不同的編譯器,最好是偏執狂,並把檢查這樣的:通過和/或返回指針接口

    struct MyStruct 
    { 
        ... 
    }; 
    C_ASSERT(sizeof(MyStruct) == ...); 
    C_ASSERT(FIELD_OFFSET(MyStruct, MyMember) = ...); 
    
  3. 類似C的功能。

  4. 不要拋出EXE調用方法的例外