Как использовать 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, поэтому простите любые очевидные ошибки):
- вызов
DllGetClassObject(clsid, IID_IClassFactory, &cf)
, гдеclsid
это CLSID, для которого вы хотите получить объект класса, иcf
конечно фабрика класса. - вызов
cf->CreateInstance(0, iid, &obj)
, гдеiid
является IID интерфейса, который вы хотели бы использовать, иobj
конечно объект. - ???
- профит!
(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, но почему бы вам?