2009-09-04 17 views
1

比方說,我有一個是在一個多線程COM應用程序的一個線程運行下面的代碼:如何在COM多個線程中使用LoadLibrary?

// Thread 1 
struct foo { 
    int (*DoSomething)(void ** parm); 
}; 

HMODULE fooHandle = LoadLibrary("pathToFoo"); 
typedef int (*loadUpFooFP)(foo ** fooPtrPtr); 
loadUpFooFP loadUpFoo; 
loadUpFoo = (loadUpFooFP)GetProcAddress(fooHandle, "fooLoader"); 
foo * myFoo; 
loadUpFoo(&myFoo); 

這一切的偉大工程,然後我就可以調用

myFoo->DoSomething(&parmPtr); 

這工作,也!現在,另一個線程來裝載它的foo:

// Thread 2 
foo * myFooInThread2; 
loadUpFoo(&myFooInThread2); 

而且,這也很好。在線程2我可以調用的DoSomething:

// Thread 2 
myFooInThread2->DoSomething(&anotherParmPtr); 

現在,當線程1最終消失,我有一個問題。我注意到在Visual Studio中調試DoSomething的地址不能再被評估。第一個線程死後,當我打電話時:

myFooInThread2->DoSomething(&anotherParmPtr); 

我得到一個訪問衝突。 myFooInThread2指針仍然有效,但函數指針不是。這個函數指針是通過調用loadUpFoo來設置的,而loadUpFoo又是由LoadLibrary加載的一個dll。

我的問題是:我從哪裏開始尋找失敗的原因?外部DLL(我用LoadLibrary加載)在我的foo結構中設置函數指針的方式有問題嗎?或者是使用相同的庫來處理不同的線程?或者,它可能以某種方式與我在此應用程序中使用COM有關(可能會在第一個線程中調用CoUninitialize以某種方式釋放此內存或庫)?

我可以提供有關COM設置的更多詳細信息,如果它看起來可能是負責任的。謝謝!

編輯:感謝您的建議。 foo結構是不透明的 - 我不太瞭解它的實現。 foo結構在我導入的頭文件中聲明。沒有我明確調用的引用計數方法,並且與LoadLibrary加載的庫沒有其他交互。我很確定foo結構不是映射到某個COM類的內存,但就像我說的那樣是不透明的,我不能肯定地說。

foo指針的生命期被正確管理(未刪除)。

foo結構是一個加密庫,所以我不能隨意泄露。在這一點上,我相信我在不同的線程和COM應用程序中使用LoadLibrary並沒有什麼本質上的錯誤(我認爲函數指針內存清除是由我自己控制之外的庫本身引起的)。

+3

您顯示的代碼不使用COM。這只是普通的DLL使用。 – 2009-09-04 21:47:55

回答

3

您所顯示的代碼中沒有任何COM相關。 LoadLibrary不是線程特定的,所以一旦你擁有了這個庫的句柄,你可以從你的所有線程中重用它。這同樣適用於指向fooLoader方法的指針。

但是,在fooLoader中肯定會有某些特定於COM的東西。另外,這裏不清楚的是foo實例的生命週期控制是什麼。

從你提到COM和你看到的奇怪行爲這一事實,我有一個鬼鬼祟祟的懷疑,即foo實際上是一個COM對象的vtable的存儲器映射,而fooLoader是DllGetClassObject或另一個創建COM對象的工廠方法。:-)

在任何情況下,foo實例變得無效的最可能的解釋是它被重新計數並且DoSomething調用AddRef()/ Release()導致它自毀。

要準確找出發生了什麼,您必須提供更多有關fooLoader的功能以及爲什麼您認爲您的代碼與COM相關的信息。

+0

的確,在我提供的代碼中沒有任何COM相關,我對沒有更清楚的道歉表示抱歉。 你看到的所有代碼(在兩個線程中)都是在COM類中執行的,這就是爲什麼我提到了COM。我注意到過去與COM有關的古怪行爲,以及我如何使用CoInitialize/CoUninitialze,這源於我對COM缺乏經驗。 假設我錯了,並且使用LoadLibrary加載的不透明庫正在使用COM。如果是這樣的話,會在以某種方式加載庫的線程中調用CoUninitialize,從而導致映射的內存變爲無效? – Zach 2009-09-10 13:46:44

+1

不,CoUninitialize不會導致內存變爲未映射,只要有未完成的LoadMemory而沒有相應的免費呼叫。但是,如果COM在該線程上是非空的,並且fooLoader使用COM來填充foo實例,則其指針可能是代理指針,代理將無法正常工作。 – 2009-09-10 16:31:26

0

DoSomething的值由您加載的庫確定。你應該能夠確定它指向的位置。查看Visual Studio中的調試輸出。它不僅會告訴你何時,還會在哪裏加載DLL。你的函數是否真的指向你認爲應該指向的DLL?

2

無論如何,線程1在關閉時調用FreeLibrary(或ExitThreadAndFreeLibrary)?如果是這樣,你試圖調用不再映射到進程中的代碼。你的對象仍然看起來不錯,因爲實例數據依然存在,但其方法的代碼將會消失。

如果這是問題,您可以更改線程1以不釋放庫。

或者,你可以有第二個線程也叫LoadLibraryLoadLibraryFreeLibrary使用引用計數,所以如果你加載一個DLL 3次,它將不會卸載,直到你釋放它3次。引用計數是每個進程的,因此您可以從不同的線程加載相同的庫。額外的負載是非常低的成本。

+0

至少從功能的角度來看,這看起來確實如此。但是在這種情況下FreeLibrary永遠不會被我調用,我可以看到(我在FreeLibrary中設置了一個在我的代碼中出現的唯一的斷點)。 – Zach 2009-09-10 13:42:51