【问题标题】:How Do I use LoadLibrary in COM Accross Multiple Threads?如何在 COM 中跨多个线程使用 LoadLibrary?
【发布时间】:2010-11-25 18:46:42
【问题描述】:

假设我有以下代码在多线程 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 本质上没有任何问题(我认为函数指针内存清理是由库本身中的某些东西引起的,我无法控制) )。

【问题讨论】:

  • 您显示的代码不使用 COM。这只是普通的 DLL 使用。

标签: c++ multithreading com loadlibrary


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 在我提供的代码中确实没有任何与 COM 相关的内容,我很抱歉没有更清楚。您看到的所有代码(在两个线程中)都在 COM 类中执行,这就是我提到 COM 的原因。我注意到过去与 COM 以及我如何使用 CoInitialize/CoUninitialze 有关的古怪行为,这是由于我缺乏使用 COM 的经验。假设我错了,我用 LoadLibrary 加载的不透明库正在使用 COM。如果是这种情况,在加载库的线程中调用 CoUninitialize 会以某种方式导致映射的内存无效吗?
  • 不,CoUninitialize 不会导致内存未映射,只要有未完成的 LoadMemory 而没有相应的 Free 调用。但是,如果 COM 在该线程上未初始化并且 fooLoader 已使用 COM 填充 foo 实例,则它拥有的指针可能是代理指针,并且代理将无法正常工作。
【解决方案2】:

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

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

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

【讨论】:

  • 这看起来确实像正在发生的事情,至少从功能的角度来看是这样。但是在我可以看到的这种情况下,我从来没有调用过 FreeLibrary(我在 FreeLibrary 上设置了一个断点,它只出现在我的代码中)。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-05
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-28
    • 2011-04-28
    相关资源
    最近更新 更多