1

假设我有以下代码在多线程 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);

这也有效!现在,另一个线程出现并加载了它的 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 本质上没有任何问题(并且我认为函数指针内存清理是由库本身中的某些东西引起的,我无法控制)。

4

3 回答 3

3

您显示的代码中没有任何与 COM 相关的内容。LoadLibrary 不是特定于线程的,因此一旦您拥有该库的句柄,就可以从所有线程中重用它。同样适用于指向 fooLoader 方法的指针。

然而,在 fooLoader 中肯定有一些特定于 COM 的东西。此外,这里还不清楚 foo 实例的生命周期控制是什么。

从您提到 COM 和您看到的时髦行为这一事实来看,我偷偷怀疑 foo 实际上是 COM 对象的 vtable 的内存映射,而 fooLoader 是 DllGetClassObject 或其他创建 COM 对象的工厂方法.. :-)

在任何情况下,对于 foo 实例变为无效的最可能的解释是它被引用计数并且 DoSomething 调用 AddRef()/Release() 导致它自毁。

要准确指出发生了什么,您必须提供更多关于 fooLoader 的功能以及您认为您的代码与 COM 相关的原因的信息。

于 2009-09-04T22:32:37.993 回答
2

无论如何,线程 1是否在关闭时调用FreeLibrary(或)?ExitThreadAndFreeLibrary如果是这样,您正在尝试调用不再映射到进程中的代码。您的对象看起来仍然不错,因为实例数据仍然存在,但其方法的代码将消失。

如果这是问题所在,您可以将线程 1 更改为不释放库。

或者,您可以让第二个线程也调用LoadLibrary. LoadLibraryFreeLibrary使用引用计数,所以如果你加载一个 DLL 3 次,它不会卸载,直到你释放它 3 次。引用计数是按进程计算的,因此您可以从不同的线程加载相同的库。额外的负载成本非常低。

于 2009-09-07T21:09:57.693 回答
0

的值DoSomething由您加载的库决定。您应该能够确定它指向的位置。查看 Visual Studio 中的调试输出。它不仅会告诉您加载 DLL 的时间,还会告诉您加载的位置。你的函数真的指向你认为它应该指向的 DLL 吗?

于 2009-09-07T11:06:18.083 回答