2014-01-13 34 views
0

我有一個簡單的本機類,它每9秒在目錄中創建一個小文件。C++ CLI自動刪除非託管對象

void SampleClass::DoWork(char *directoryPath, SegmentCreatedDelegate callback, bool *cancel) 
{ 
    if(*cancel) return; 

    char* segmentFileName = "test%d.ts"; 

    // open the file to write 
    FILE *pFilempegTs = NULL; 

    int segmentIndex = 0; 
    clock_t beginingTime = clock(); 
    char currentSegmentPath[256]; 

    while(!*cancel) 
    { 
     // create the segment file to write if we haven't already. 
     if(pFilempegTs == NULL) 
     { 
      segmentIndex++; 
      strncpy(currentSegmentPath, directoryPath, sizeof(currentSegmentPath)); 
      strncat(currentSegmentPath, segmentFileName, sizeof(currentSegmentPath)); 
      sprintf(currentSegmentPath, currentSegmentPath, segmentIndex); 
      if((pFilempegTs = fopen(currentSegmentPath, "wb")) == NULL) 
      { 
       std::cout << "The file can not be opened for writing\n"; 
       return; 
      } 
     } 

     if((double(clock() - beginingTime)/CLOCKS_PER_SEC) >= 9) 
     { 
      fclose(pFilempegTs); 
      pFilempegTs = NULL; 
      callback(currentSegmentPath); // the moment I invoke the delegate, the currentSegmentPath gets deleted. 
      beginingTime = clock(); 
     } 
    } 

    if(pFilempegTs != NULL) 
    { 
     fclose(pFilempegTs); 
     callback(currentSegmentPath); // the moment I invoke the delegate, the currentSegmentPath gets deleted. 
    } 

    return; 
} 

當代碼在C++/cli項目中編譯本地類並通過.NET包裝調用時,此代碼完全按預期工作。

但是,如果本地類在本地dll(而不是C++ cli)中編譯並執行,則會在strncpy(currentSegmentPath, directoryPath, sizeof(currentSegmentPath))處遇到錯誤。看來,當我調用SegmentCreatedDelegate時,currentSegmentPath在內存中被刪除。請記住,只有在SampleClass位於本地dll中時纔會發生這種情況,並且使用__declspec(dllexport)從C++/cli調用。

注意:SegmentCreatedDelegate是一個託管到本機代理,使用以下代碼(.NET/C++/cli)創建。

void SampleClassNet::DoWork(System::String^ directoryPath, SegmentCreatedDelegateNet^ segmentCreatedCallback, bool% cancel) 
{ 
    SampleClass* nativeClass = new SampleClass(); 

    System::IntPtr directoryPathPointer = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(directoryPath); 
    char *directoryPathNative = static_cast<char*>(directoryPathPointer.ToPointer()); 

    System::IntPtr callbackPointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(segmentCreatedCallback); 

    pin_ptr<bool> pinnedCancel = &cancel; 
    bool* pinnedCancelRef = pinnedCancel; 

    nativeClass->DoWork(directoryPathNative, (SegmentCreatedDelegate)(void*)callbackPointer, pinnedCancelRef); 

    System::GC::KeepAlive(segmentCreatedCallback); 
    System::Runtime::InteropServices::Marshal::FreeHGlobal(directoryPathPointer); 
} 

爲什麼調用SegmentCreatedDelegate刪除currentSegmentPath,只有在本地方法是C++/CLI項目之外編制?

這是我得到的確切錯誤信息。

試圖讀取或寫入受保護的內存。這通常是指示其他內存已損壞的 。

UPDATE

我創建了一個示例項目重現該問題。

https://bitbucket.org/theonlylawislove/so-c-cli-deleting-unmanaged-object-automatically

更新2

我添加了一個[UnmanagedFunctionPointer(CallingConvention::Cdecl)]屬性到我的託管委託,我的問題似乎是決心。這兩個項目都使用cdecl調用約定,所以我不知道爲什麼需要這樣做。

+0

不要在'IntPtr'上做'(SegmentCreatedDelegate)(void *)callbackPointer'。相反,'(SegmentCreatedDelegate)callbackPointer.ToPointer()' –

+0

請同時顯示'SegmentCreatedDelegate'的typedef –

+0

您在[上一個問題]中得到了委託聲明錯誤(http://stackoverflow.com/questions/21054847/c- cli-marshaling-net-delegate-to-native-delegate),聽起來不像你有進步。你以前的代碼沒有崩潰是一個不幸的事故。 'currentSegmentPath'變量不能被「刪除」,它是一個局部變量。你一直堅持這個時間足夠長,爲了得到你需要的幫助,你必須發佈一個小的repro解決方案,將這個問題展示給文件共享服務,以便我們實際上可以重現問題。 –

回答

0

這可能是與調用約定的衝突。如this MSDN page所述,非託管和混合程序集使用_cdecl調用convertions,但對於純管理程序集,默認情況下,構建器使用_clrcall

嘗試使用相同的調用約定來編譯這兩個DLL。對於VS 2010,這可以做到項目屬性 - > C/C++ - >高級 - >調用約定

相關問題