C++ 如何在C++;

C++ 如何在C++;,c++,visual-c++,com,dll,loadlibrary,C++,Visual C++,Com,Dll,Loadlibrary,首先,COM对我来说就像黑魔法。但我需要在一个项目中使用COM dll 所以,我有一个DLL,我正在开发,我需要一些功能,可以在一个单独的COM DLL。当我查看带有Depends.exe的COM DLL时,我会看到类似DllGetClassObject()的方法和其他函数,但我对这些函数都不感兴趣 我可以访问COM DLL(遗留)源代码,但它很混乱,我更愿意使用二进制的COM DLL,就像一个大黑匣子,不知道里面发生了什么 那么,如何使用LoadLibrary从代码中调用COM DLL函数呢?

首先,COM对我来说就像黑魔法。但我需要在一个项目中使用COM dll

所以,我有一个DLL,我正在开发,我需要一些功能,可以在一个单独的COM DLL。当我查看带有Depends.exe的COM DLL时,我会看到类似DllGetClassObject()的方法和其他函数,但我对这些函数都不感兴趣

我可以访问COM DLL(遗留)源代码,但它很混乱,我更愿意使用二进制的COM DLL,就像一个大黑匣子,不知道里面发生了什么

那么,如何使用LoadLibrary从代码中调用COM DLL函数呢?可能吗?如果,是的,你能给我一个如何做的例子吗

我正在使用Visual Studio 6进行此项目


非常感谢

一般来说,您应该更喜欢或不喜欢直接访问
DllGetClassObject
但是如果您处理的是一个无法注册或不想注册的DLL,那么下面将介绍这些函数在幕后的作用(部分)


给定CLSID,
DllGetClassObject
允许您获取类对象,您可以从中创建实例(如果我没记错的话,可以通过
IClassFactory
接口)

步骤摘要(我上次接触COM已经有一段时间了,请原谅任何明显的错误):

  • 调用
    DllGetClassObject(clsid,IID\u IClassFactory,&cf)
    ,其中
    clsid
    是要获取类对象的clsid,而
    cf
    当然是类工厂
  • 调用
    cf->CreateInstance(0,iid,&obj)
    ,其中
    iid
    是您想要使用的接口的iid,而
    obj
    当然是对象
  • 利润
    CoCreateInstance
    执行步骤1和2。
    CoGetClassObject
    执行步骤1。如果需要创建同一类的多个实例,则可以使用
    CoGetClassObject
    ,这样就不必每次重复步骤1。)

    如果它是COM DLL,则只需将其添加为对项目的引用,然后可以调用DLL中的函数


    是的,您可以使用诸如DLLGetClassObject之类的低级COM函数,但为什么要这样做呢?

    下面是一些代码,说明如何获取类工厂并使用它创建COM对象。它使用结构跟踪模块句柄和DllGetClassObject函数指针。在处理完COM对象之前,应该一直保持模块句柄

    要使用此函数,需要分配ComModuleInfo结构的实例,并将szDLL设置为DLL文件名或完整路径名。然后使用要从该DLL获取的COM对象的类id和接口id调用该函数

    typedef struct {
       TCHAR   szDLL[MAX_PATH];
       HMODULE hModule;
       HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
       } ComModuleInfo;
    
    HRESULT CreateCOMObject(
       ComModuleInfo & mod,  // [in,out] 
       REFCLSID iidClass,    // [in] CLSID of the COM object to create
       REFIID iidInterface,  // [in] GUID of the interface to get
       LPVOID FAR* ppIface)  // [in] on success, interface to the COM object is returned
    {
        HRESULT hr = S_OK;
    
        *ppIface = NULL; // in case we fail, make sure we return a null interface.
    
        // init the ComModuleInfo if this is the first time we have seen it.
        //
        if ( ! mod.pfnGetFactory)
        {     
           if ( ! mod.hModule)
           {
              mod.hModule = LoadLibrary(mod.szDLL);
              if ( ! mod.hModule)
                 return HRESULT_FROM_WIN32(GetLastError());
           }
           mod.pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(mod.hModule, "DllGetClassObject");
           if ( ! mod.pfnGetFactory)
              return HRESULT_FROM_WIN32(GetLastError());
        }
    
        IClassFactory* pFactory = NULL;
        hr = mod.pfnGetFactory(iidClass, IID_IClassFactory, (void**)&pFactory);
        if (SUCCEEDED(hr))
        {
           hr = pFactory->CreateInstance(NULL, iidInterface, (void**)ppIface);
           pFactory->Release();
        }
    
        return hr;
    }
    

    通常,您会使用
    CoCreateInstance()
    从COM DLL实例化对象。执行此操作时,无需像处理普通DLL那样首先加载DLL并获取proc地址。这是因为Windows“知道”COM DLL实现的类型、它们在哪个DLL中实现以及如何实例化它们。(当然,假设COM DLL已注册,通常是这样)

    假设您有一个带有想要使用的IDog接口的COM DLL。那么,

    dog.idl myCode.cpp 尽管如此,所有这些内存管理的东西都会变得非常单调,ATL提供了智能指针,使实例化和管理这些对象的任务变得更加容易:

    CComPtr<IDog> dog;
    dog.CoCreateInstance(CLSID_DOG);
    dog->Bark();
    
    CComPtr狗;
    CoCreateInstance(CLSID_dog);
    狗->吠叫();
    
    编辑: 当我在上面说:

    Windows“知道”COM DLL实现的类型[…和] 它们是在什么DLL中实现的

    …我确实掩饰了Windows是如何知道这一点的。这不是魔法,虽然一开始它看起来有点神秘

    COM库附带类型库,其中列出了库提供的接口和类。这个类型库是以硬盘上的文件形式存在的——通常它直接嵌入到与库本身相同的DLL或EXE中。通过在Windows注册表中查找,Windows知道在哪里可以找到类型库和COM库本身。注册表中的条目告诉Windows DLL在硬盘上的位置

    调用
    CoCreateInstance
    时,Windows在Windows注册表中查找clsid,找到相应的DLL,加载它,并在DLL中执行实现COM对象的正确代码

    这些信息是如何进入Windows注册表的?安装COM DLL后,将对其进行注册。这通常是通过运行来完成的,这反过来会将DLL加载到内存中,并调用名为
    DllRegisterServer
    的函数。该函数在COM服务器中实现,用于向注册表添加必要的注册信息。如果您使用的是ATL或其他COM框架,那么这可能是在幕后完成的,这样您就不必直接与注册表接口
    DllRegisterServer
    只需在安装时调用一次

    如果尝试为尚未通过
    regsvr32
    /
    DllRegisterServer
    进程注册的COM对象调用
    CoCreateInstance
    ,则
    CoCreateInstance
    将失败,并显示以下错误:

    未注册的类别


    幸运的是,解决方法是在COM服务器上调用
    regsvr32
    ,然后再试一次。

    您不直接将LoadLibrary()用于COM库。CoCreateInstance()将调用此函数(如果尚未调用),然后将库中实现的类的一个新实例添加到堆中,最后向您返回指向该对象的原始指针。当然,在这个过程中,它可能会失败,因此需要一些机制来检查状态,如HRESULT

    为了使用它的简单性,哟
    IDog* piDog = 0;
    CoCreateInstance(CLSID_DOG, 0,  CLSCTX_INPROC_SERVER, IID_IDOG,  &piDog); // windows will instantiate the IDog object and place the pointer to it in piDog
    piDog->Bark();  // do stuff
    piDog->Release();  // were done with it now
    piDog = 0;  // no need to delete it -- COM objects generally delete themselves
    
    CComPtr<IDog> dog;
    dog.CoCreateInstance(CLSID_DOG);
    dog->Bark();
    
    #import "whatever.dll"