如何在 COM 中跨多线程使用 LoadLibrary?
假设我有在多线程 COM 应用程序的一个线程中运行的以下代码:
// 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);
This Works,太!现在,另一个线程出现并加载它的 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 没有本质上的错误(并且我认为函数指针内存清理是由库本身中超出我控制范围的某些内容引起的) )。
Let's say that I have the following code that's run in one thread of a multi-threaded COM application:
// 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);
This all works great and I can then call
myFoo->DoSomething(&parmPtr);
This works, too! Now, another thread comes along and loads up its foo:
// Thread 2
foo * myFooInThread2;
loadUpFoo(&myFooInThread2);
And this, too, works great. In thread 2 I can call DoSomething:
// Thread 2
myFooInThread2->DoSomething(&anotherParmPtr);
Now, when thread 1 eventually goes away, I have a problem. I notice that debugging in Visual Studio that the address of DoSomething can no longer be evaluated. After the first thread dies, when I call:
myFooInThread2->DoSomething(&anotherParmPtr);
I get an access violation. The myFooInThread2 pointer is still valid, but the function pointer was not. This function pointer was set by a call into loadUpFoo which in turn was in a dll loaded by LoadLibrary.
My question is: where do I start looking for the reason this is failing? Is it some problem with the way the external DLL (that I load with LoadLibrary) is setting the function pointer in my foo struct? Or is it something to do with differing threads using the same library? Or, could it somehow be related to my usage of COM in this application (would calling CoUninitialize in the first thread somehow free this memory or library)?
I can provide more details on the COM setup if that looks like it could be responsible. Thanks!
edit: Thanks for the suggestions so far. The foo struct is opaque - I don't really know much about its implementation. The foo struct is declared in a header I import. There are no reference counting methods that I explicitly call and there are no other interactions with the library that's loaded with LoadLibrary. I'm pretty sure the foo struct is not memory mapped to some COM class, but like I said it's opaque and I can't say for sure.
The foo pointers' lifetimes are properly managed (not deleted).
The foo structure is an encryption library, so I'm not at liberty to divulge any more. At this point, I'm confident that there is nothing inherently wrong with my usage of LoadLibrary across different threads and within a COM application (and I suppose that the function pointer memory cleanup is being caused by something in the library itself outside of my control).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您所显示的代码中没有任何与 COM 相关的内容。 LoadLibrary 不是特定于线程的,因此一旦您拥有该库的句柄,您就可以从所有线程中重用它。这同样适用于指向 fooLoader 方法的指针。
然而,fooLoader 内部肯定有一些特定于 COM 的东西。另外,这里还不清楚 foo 实例的生命周期控制是什么。
从你提到 COM 和你看到的时髦行为的事实来看,我偷偷地怀疑 foo 实际上是 COM 对象的 vtable 的内存映射,而 fooLoader 是 DllGetClassObject 或创建 COM 对象的另一个工厂方法。:-)
在任何情况下在这种情况下, foo 实例变得无效的最可能的解释是它被引用计数并且 DoSomething 调用 AddRef()/Release() 导致它自毁。
为了准确地查明发生了什么,您必须提供更多关于 fooLoader 的作用以及您认为您的代码与 COM 相关的信息。
There's nothing COM related in the code you've shown. LoadLibrary is not thread-specific, so once you have the handle to the lib, you can reuse it from all your threads. Same applies to the pointer to the fooLoader method.
However, there certainly could be something COM-specific inside fooLoader. Also, what's not clear here is what is the lifetime control of the foo instances.
From the fact that you mention COM and the funky behavior you see, I have the sneaky suspicion that foo is actually a memory map of a COM object's vtable and fooLoader is DllGetClassObject or another factory method that creates COM objects.. :-)
In any case, the most probable explanation for the foo instance becoming invalid would be that it is ref-counted and DoSomething calls AddRef()/Release() causing it to self-destroy.
To pinpoint exactly what's going on, you'll have to provide a bit more information on what fooLoader does and why you think your code is COM related.
线程 1 关闭时是否会调用
FreeLibrary
(或ExitThreadAndFreeLibrary
)?如果是这样,您正在尝试调用不再映射到进程中的代码。您的对象看起来仍然不错,因为实例数据仍然存在,但其方法的代码将消失。如果这是问题所在,您可以更改线程 1 以不释放库。
或者,您可以让第二个线程也调用
LoadLibrary
。LoadLibrary
和FreeLibrary
使用引用计数,因此如果您加载 DLL 3 次,它不会卸载,直到您释放它 3 次。引用计数是针对每个进程的,因此您可以从不同的线程加载相同的库。额外负载的成本非常低。By any chance, is Thread 1 calling
FreeLibrary
(orExitThreadAndFreeLibrary
) when it shuts down? If so, you're trying to call code that's no longer mapped into the process. Your object still looks good, because the instance data still exists, but the code for its methods would be gone.If this is the problem, you can change Thread 1 to not free the library.
Or, you could have the second thread also call
LoadLibrary
.LoadLibrary
andFreeLibrary
use reference counting, so if you load a DLL 3 times, it won't unload until you free it 3 times. The reference counting is per process, so you can load the same library from different threads. The additional loads are very low cost.DoSomething
的值由您加载的库决定。您应该能够确定它指向的位置。查看 Visual Studio 中的调试输出。它不仅会告诉您 DLL 的加载时间,还会告诉您加载的位置。你的函数真的指向你认为它应该指向的DLL吗?The value of
DoSomething
is determined by the library you load. You should be able to determine where it's pointing. Look at the debug output in Visual Studio. It will tell you not just when, but also where DLLs are loaded. Does your function really point into te DLL you think it should point to?