代码之家  ›  专栏  ›  技术社区  ›  Zach

如何在COM中跨多个线程使用LoadLibrary?

  •  1
  • Zach  · 技术社区  · 15 年前

    假设我有以下代码在多线程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中的调试无法再评估剂量测量的地址。在第一个线程死后,当我调用:

    myFooInThread2->DoSomething(&anotherParmPtr);
    

    我有一个访问冲突。myfoointhread2指针仍然有效,但函数指针无效。这个函数指针是通过调用loadupfoo来设置的,而loadlibrary又加载了一个dll。

    我的问题是:我从哪里开始寻找失败的原因?外部dll(我用loadlibrary加载)在foo结构中设置函数指针的方式有问题吗?或者它与使用相同库的不同线程有关?或者,它是否与我在这个应用程序中使用COM有关(调用第一个线程中的couninitialize会以某种方式释放内存或库)?

    如果可能的话,我可以提供关于COM设置的更多详细信息。谢谢!

    编辑: 谢谢你到目前为止的建议。foo结构是不透明的-我对它的实现不太了解。foo结构在头i import中声明。没有我显式调用的引用计数方法,也没有与加载了LoadLibrary的库进行其他交互。我很确定foo结构不是内存映射到某个com类,但是就像我说的那样,它是不透明的,我不能确定。

    foo指针的生命周期管理正确(未删除)。

    foo结构是一个加密库,所以我不能再泄露了。此时,我确信在不同的线程和COM应用程序中使用LoadLibrary没有任何本质上的错误(并且我认为函数指针内存清理是由库本身的某些超出我控制范围的东西引起的)。

    3 回复  |  直到 15 年前
        1
  •  3
  •   Franci Penov    15 年前

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

    不过,在“傻瓜”里面肯定有一些特定的东西。另外,这里不清楚的是foo实例的生存期控制是什么。

    从你提到COM的事实和你所看到的古怪行为来看,我有一种潜移默化的怀疑,即foo实际上是一个COM对象的vtable的内存映射,而傻瓜则是dllgetclassobject或另一个创建COM对象的工厂方法。-)

    在任何情况下,对foo实例变为无效的最可能解释是,它被引用计数,dosomething调用addref()/release()导致它自我销毁。

    为了准确地指出正在发生的事情,您必须提供一些关于傻瓜做什么以及为什么您认为您的代码与COM相关的更多信息。

        2
  •  2
  •   Adrian McCarthy    15 年前

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

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

    或者,您可以让第二个线程也调用 LoadLibrary . 加载 自由主义者 使用引用计数,所以如果您加载一个DLL三次,它将不会卸载,直到您释放它三次。引用计数是按进程进行的,因此您可以从不同的线程加载相同的库。附加负载的成本非常低。

        3
  •  0
  •   MSalters    15 年前

    价值 DoSomething 由您加载的库决定。你应该能够确定它指向哪里。查看Visual Studio中的调试输出。它不仅可以告诉您何时加载DLL,还可以告诉您在何处加载DLL。您的函数是否真的指向您认为应该指向的TE DLL?