2016-09-25 29 views
2

設置C++/CLI靜態型扣對象會導致未處理的運行時異常

雖然試圖創建在一個C/CLI包裝翻譯單元範圍的靜態對象爲本地C++ lib中,我跑成問題與使用auto關鍵字。 C++/CLI包裝器是純粹的功能,所以我需要使用ConcurrentDictionary在調用之間保持一些狀態(以處理託管< - >本地代理翻譯)。我第一次嘗試這個(簡化):

static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>(); 

但這失敗,編譯:

具有靜態存儲持續時間的變量不能具有手柄或跟蹤引用類型

對於簡單,我決定繼續使用類型扣除,同時我找到了問題:

static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>(); 

令我驚訝的是,編譯和鏈接!我去寫更多的代碼一段時間,然後我跑我的,它使用C++/CLI包裝C#測試應用程序並獲得了未處理的運行時異常:

未處理的異常:System.IO.FileLoadException:無法加載文件或程序程序集'DotNetTestLibWrapper,Version = 1.0.6111.33189,Culture = neutral,PublicKeyToken = null'或其依賴項之一。無法找到或加載類型。 (來自HRESULT的異常:0x80131522)---> System.TypeLoadException:從程序集'DotNetTestLibWrapper,Version = 1.0.6111.33189,Culture = neutral,PublicKeyToken = null'中輸入''具有非法類型的字段。

我沒有馬上知道這個問題是什麼,因爲我做了其他編輯。通過打開融合日誌並使用Fuslogvw.exe,我終於獲得了更多信息。問題是類型推導出的靜態ConcurrentDictionary。

我創建了一個SSCCE證明問題:https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary

在這篇文章中描述的位於此處的代碼:https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary/blob/master/DotNetTestLibWrapper/DotNetTestLibWrapper.cpp

MSVS版本:

微軟的Visual Studio社區2015年版14.0。 25431.01更新3 Microsoft .NET Framework版本4.6.01038

問題

這些都導致編譯器錯誤:

static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>(); 
static auto^ GlobalData2 = gcnew ConcurrentDictionary<String^, String^>(); 

但這編譯+鏈接,但會導致非法類型的未處理的運行時異常:

static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>(); 

這是怎麼回事,併爲什麼會發生? MSVS似乎認爲(閱讀:智能感知如此)autoauto^定義是相同的。但是,它們顯然不是一個編譯而另一個不編譯。

注:閱讀一些有關原始編譯器的問題後,該問題的實際解決方案是這樣的:

ref struct GlobalData 
{ 
    static ConcurrentDictionary<String^, String^>^ GlobalData4 = gcnew ConcurrentDictionary<String^, String^>(); 
}; 

這很好,我只是好奇,什麼是真正發生的事情與該類型推演。

回答

5

Hmya,編譯器bug,它不應該允許你這樣聲明它。毫無疑問,C++ 11引入了更改,需要禁用此聲明的額外檢查在聲明中不適用於auto

您嘗試調用的臭名昭着的SIOF(靜態初始化順序Fiasco)不是.NET功能。但這正是你在這裏得到的,編譯器生成的初始值(通常爲,僅用於非託管代碼的)會導致構造函數運行得太早。 確切地說出錯是很難從調試器蹤跡看到的,除非它不美觀。但最基本的問題當然是模塊初始化程序需要先運行以設置C++/CLI的執行環境。在此之前,任何本機C++初始化器。那還沒有發生。

你需要這樣做正確的方式,靜態成員由類型初始化程序(又名靜態構造函數,又名.cctor)初始化。在使用任何類成員之前,CLR會自動調用它。您不必顯式編寫該構造​​函數,編譯器會自動從字段初始化表達式中爲您寫入該構造函數。

ref class Globals { 
public: 
    static ConcurrentDictionary<String^, String^>^ Data1 = gcnew ConcurrentDictionary<String^, String^>(); 
    // etc... 
}; 
+0

我不知道如果編譯器能夠支持管理「靜態」存儲通過定義這樣的編譯器生成的類*和*從模塊初始化調用它的靜態構造函數。 –

+0

好吧,它不需要CLR支持靜態構造函數。而他們沒有。 –

相關問題