Как использовать COM DLL с LoadLibrary в C++

во-первых, COM для меня как черная магия. Но мне нужно использовать com dll в одном проекте, над которым я работаю.

Итак, у меня есть DLL, которую я разрабатываю, и мне нужны некоторые функции, которые доступны в отдельной COM DLL. Когда я смотрю на COM DLL с зависит.exe я вижу такие методы, как DllGetClassObject() и другие функции, но ни одна из функций меня не интересует.

У меня есть доступ к исходному коду COM DLL (legacy), но это беспорядок, и я бы предпочел использовать COM DLL в двоичном формате, как большой черный ящик, не зная, что происходит внутри.

Итак, как я могу вызвать функции COM DLL из моего кода с помощью LoadLibrary? Возможно ли это? Если да, то не могли бы вы привести пример, как это сделать?

Я использую Visual Studio 6 для этого проекта.

Спасибо большое!

6 ответов


как правило, вы должны использовать CoCreateInstance() для создания экземпляра объекта из COM DLL. Когда вы это сделаете, нет необходимости сначала загружать DLL и получать адреса proc, как вам нужно было бы сделать с обычной DLL. Это связано с тем, что Windows "знает" о типах, которые реализует COM DLL, в какой DLL они реализованы и как создать их экземпляр. (Предполагая, конечно, что COM DLL зарегистрирована, что обычно и происходит).

Предположим, у вас есть COM DLL с интерфейсом IDog, который вы захотите использовать. В таком случае...--13-->

собака.idl

interface IDog : IUnknown
{
  HRESULT Bark();
};

coclass Dog
{
  [default] Interface IDog;
};

mycode в.cpp

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

все это управление памятью может стать довольно шероховатым, хотя, и ATL предоставляет умные указатели, которые делают задачу создания экземпляров и управления этими объектами немного проще:

CComPtr<IDog> dog;
dog.CoCreateInstance(CLSID_DOG);
dog->Bark();

EDIT:

когда я сказал выше, что:

Windows "знает"о типах, которые реализует COM DLL [...и] какая DLL они реализовано в

...Я действительно замалчивал, как именно Windows это знает. Это не магия, хотя поначалу может показаться немного оккультной.

библиотеки COM поставляются с библиотеками типов, в которых перечислены интерфейсы и Коклассы, предоставляемые библиотекой. Эта библиотека типов находится в виде файла на жестком диске-очень часто она встроена непосредственно в ту же DLL или EXE, что и сама библиотека. Windows знает, где найти библиотеку типов и Сама библиотека COM, посмотрев в реестре Windows. Записи в реестре сообщают Windows, где на жестком диске находится DLL.

когда вы называете CoCreateInstance, Windows ищет clsid в реестре Windows, находит соответствующую DLL, загружает его и выполняет правильный код в DLL, который реализует COM-объект.

как эта информация попадает в реестр Windows? Когда COM DLL установлен, он зарегистрирован. Обычно это делается бег!--37-->команду regsvr32.exe, который в свою очередь загружает DLL в память и вызывает функцию с именем DllRegisterServer. Эта функция, реализованная на вашем COM-сервере, добавляет информацию о necesarry в реестр. Если вы используете ATL или другую платформу COM, это, вероятно, делается под капотом, чтобы вам не пришлось напрямую взаимодействовать с реестром. DllRegisterServer только должен быть вызван один раз, при установке.

если вы попытаетесь назвать CoCreateInstance для COM-объекта, который еще не зарегистрирован через regsvr32/


в общем, вы должны предпочесть CoCreateInstance или CoGetClassObject вместо того чтобы обращаться к DllGetClassObject напрямую. но если вы имеете дело с DLL, которую вы не можете или не хотите регистрировать, то ниже описывается (часть), что эти функции делают за кулисами.


учитывая CLSID,DllGetClassObject позволяет получить объект класса, от которого вы можете создавать экземпляры (через IClassFactory интерфейс, если я правильно помню правильно.)

сводка шагов (прошло некоторое время с тех пор, как я последний раз касался COM, поэтому простите любые очевидные ошибки):

  1. вызов DllGetClassObject(clsid, IID_IClassFactory, &cf), где clsid это CLSID, для которого вы хотите получить объект класса, и cf конечно фабрика класса.
  2. вызов cf->CreateInstance(0, iid, &obj), где iid является IID интерфейса, который вы хотели бы использовать, и obj конечно объект.
  3. ???
  4. профит!

(CoCreateInstance выполняет шаги 1 и 2. CoGetClassObject выполняет Шаг 1. Вы бы использовали CoGetClassObject Если вам нужно создать несколько экземпляров одного класса, так что Шаг 1 не нужно повторять каждый раз.)


вы напрямую не используете LoadLibrary () с библиотекой COM. CoCreateInstance () вызовет эту функцию, если она еще не создана, а затем создаст экземпляр класса, реализованного в библиотеке, в кучу и, наконец, вернет вам необработанный указатель на этот объект. Конечно, он может потерпеть неудачу во время процесса, и, таким образом, какой-то механизм для вас, чтобы проверить статус, как HRESULT.

для простоты его использования вы можете думать о библиотеке COM Как общей DLL с 1) некоторые предопределенные функция ввода (main), 2) вы должны вызвать некоторую предопределенную функцию, такую как CoCreateInstance (), чтобы ввести ее, и принять, что это так, потому что это должно.


если библиотека типов встроена в DLL, вы можете импортировать ее в свой проект:

#import "whatever.dll"

это автоматически создаст файлы заголовков, которые будут включены в ваш проект и позволят вам использовать экспортированные объекты.


вот немного кода, показывающего, как получить фабрику классов и использовать ее для создания COM-объекта. Он использует структуру для отслеживания дескриптора модуля и указателя функции DllGetClassObject. Вы должны держаться за дескриптор модуля, пока не закончите с COM-объектом.

чтобы использовать эту функцию, необходимо выделить экземпляр структуры ComModuleInfo и задать szDLL имя файла DLL или полный путь. Затем вызовите функцию с идентификатором класса и идентификатором интерфейса COM-объект, который вы хотите получить из этой DLL.

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;
}

Если это COM DLL, все, что вам нужно сделать, это добавить его в качестве ссылки на ваш проект, а затем вы можете вызвать функции, которые находятся в DLL.

Да, вы можете использовать функции COM низкого уровня, такие как DLLGetClassObject, но почему бы вам?