2011-07-20 59 views
7

刪除在DLL中創建的對象時,我需要一些關於運行時/堆問題的說明。在我回答我的問題之前,需要一些介紹...刪除在DLL中創建的對象

在我的項目中,一個DLL(由它的名稱指定)返回一個新的Grabber對象。在我的代碼的早期版本中,DLL導出這樣的功能:

extern "C" 
__declspec(dllexport) Grabber* CreateGrabber(string settings) 
{ 
    return new SomeSpecificGrabber(settings); 
} 

在EXE我用這樣的靜態函數來創建一個新的抓取對象:

static Grabber* createGrabberObject(const std::string& grabberType, const std::string& grabberSettings) 
{ 
    FARPROC hProc = 0; 

    // load dll with the name of grabberType 
    HMODULE hDLL = LoadLibrary(grabberType.c_str()); 

    // get address for CreateGrabber function 
    hProc = GetProcAddress(hDLL, "CreateGrabber"); 

    // instantiate a function pointer of our type and typecast the address 
    // of the CreateGrabber function to this type 
    CreateGrabberFunctionType CreateGrabberFunction = (CreateGrabberFunctionType)hProc; 

    // call CreateGrabber in DLL to get a Grabber object 
    return CreateGrabberFunction(grabberSettings); 
} 

在EXE一個抓取對象的生命週期由智能指針管理:

shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings")); 

這一切,只要我編譯EXE並與/MDd設置(VC++ 2010)的DLL工作得很好,WHI ch意味着EXE和DLL使用相同的堆。

現在我想用/MTd設置編譯我的解決方案。有了這個,我得到了一個類型爲_CrtIsValidHeapPointer的運行時斷言,用於傳遞給DLL的設置字符串對象。這是有道理的,因爲DLL試圖刪除在EXE中創建的字符串對象。而且他們不再使用相同的堆。

我解決此問題得到了通過更改導出的DLL函數一點點(的const char*代替string):

extern "C" 
__declspec(dllexport) Grabber* CreateGrabber(const char* settings) 
{ 
    return new SomeSpecificGrabber(settings); 
} 

而在createGrabberObject我通過grabberSettings.c_str(),而不是grabberSettings的DLL功能。

現在一切正常了。但現在我的第一個問題:爲什麼我不能在myGrabberObj被刪除時得到_CrtIsValidHeapPointer斷言?該對象是從DLL內部創建的,但是從EXE中刪除(通過智能指針)。爲什麼我在這裏沒有與上面的字符串對象相同的問題?

我想一個乾淨的解決方案是該DLL還出口函數是這樣的:

extern "C" 
__declspec(dllexport) void DeleteGrabber(Grabber* grabber) 
{ 
    delete grabber; 
} 

然後我也有我的EXE這一個DLL調用DeleteGrabber靜態函數:

static void deleteGrabberObject(const std::string& grabberType, Grabber* grabber) 
{ 
    FARPROC hProc = 0; 

    // load dll with the name of grabberType 
    HMODULE hDLL = LoadLibrary(grabberType.c_str()); 

    // get address for DeleteGrabber function 
    hProc = GetProcAddress(hDLL, "DeleteGrabber"); 

    // instantiate a function pointer of our type and typecast the address 
    // of the DeleteGrabber function to this type 
    DeleteGrabberFunctionType DeleteGrabberFunction = (DeleteGrabberFunctionType)hProc; 

    // call DeleteGrabber in DLL 
    DeleteGrabberFunction(grabber); 
} 

這個靜態函數可以自動然後由智能指針叫做:

shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings"), 
boost::bind(deleteGrabberObject, "SomeGrabber.DLL", _1)); 

這也適用。但是這裏來了我的第二個問題:靜態函數createGrabberObjectdeleteGrabberObject都加載DLL。這是否意味着創建了兩個不同的堆,因爲加載了這個DLL的兩個實例(那麼這個解決方案根本無法解決我的問題)?或者這兩個靜態函數使用相同的堆?

我希望有人能解釋這裏發生了什麼...

回答

5

該DLL是引用計數,沒有加載兩次,並且當您使用LoadLibrary時,它只會被加載一次,並且它們將使用相同的堆。靜態函數是這個問題的正常解決方案。

+0

謝謝!這真的很快! –

3

對於第二個問題,僅僅因爲您加載了兩次DLL並不意味着有兩個實例。該操作系統足夠智能,只能加載一次。

編輯:對於第一個問題,這可能是因爲共享指針從未實際超出範圍或因爲VC運行時無法正確檢測到該情況(它並沒有致命失敗,但內存wasn'釋放)。

+0

感謝您的快速響應!實際上,共享指針超出了範圍(在DLL中'〜SomeSpecificGrabber'中的斷點被擊中)。所以它看起來像VC運行時沒有檢測到它。 –

3

嗯,這是因爲(在你的情況),兩堆正在工作。該DLL有另一個堆管理器,和EXE有不同。這可能是因爲:

  • 調試/發佈矛盾
  • 使用的VC-運行時(一個是VC8,一個是例如VC9)。以上也複合!
  • 用於構建DLL/EXE的不同模型(/MT[d]標誌,鏈接爲static-lib等)。
  • 您已經顯示delete針對new這是有效的。但是這也可能是new實際上是malloc/HeapAlloc的情況。

總之,內存X堆管理器分配將不會受到Ÿ堆管理器中找到,因此斷言!