First, COM is like black magic for me. But I need to use COM dll in one project I'm working on.
So, I have a DLL I am developing and I need some functionalities that are available in a separate COM DLL. When I look to the COM DLL with Depends.exe I see methods like DllGetClassObject() and other functions but none of the functions I'm interested in.
I have access to the COM DLL (legacy) source code but it's a mess and I'd rather like to use the COM DLL in binary like a big black box not knowing what's going on inside.
So, how can I call the COM DLL functions from my code using LoadLibrary? Is it possible? If, yes, could you give me an example of how to do it?
I'm using Visual Studio 6 for this project.
Thanks a lot!
In general, you should prefer CoCreateInstance or CoGetClassObject rather than accessing DllGetClassObject directly. But if you're dealing with a DLL that you can't, or don't want to, register, then the below describes (part of) what these function do behind the scenes.
Given a CLSID, DllGetClassObject allows you to get the class object, from which you can create instances (via the IClassFactory interface, if I remember correctly).
Summary of steps (it's been a while since I've last touched COM, so pardon any obvious errors):
Call DllGetClassObject(clsid, IID_IClassFactory, &cf), where clsid is the CLSID you want to get the class object for, and cf is of course the class factory.
Call cf->CreateInstance(0, iid, &obj), where iid is the IID of the interface you'd like to use, and obj is of course the object.
???
Profit!
(CoCreateInstance performs steps 1 and 2. CoGetClassObject performs step 1. You would use CoGetClassObject if you need to create many instances of the same class, so that step 1 doesn't need to be repeated each time.)
Typically you would use CoCreateInstance() to instantiate an object from a COM DLL. When you do this, there's no need to load the DLL first and get proc addresses like you would need to do with a normal DLL. This is because Windows "knows" about the types that a COM DLL implements, what DLL they are implemented in, and how to instantiate them. (Assuming of course that the COM DLL is registered, which it typically is).
Suppose you have a COM DLL with the IDog interface you want to use. In that case,
dog.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
All this memory management stuff can get pretty grungy, though, and the ATL provides smart pointers that make the task of instantiating & managing these objects a little easier:
CComPtr<IDog> dog;
dog.CoCreateInstance(CLSID_DOG);
dog->Bark();
EDIT:
When I said above that:
Windows "knows" about the types that a COM DLL implements [...and]
what DLL they are implemented in
...I really glossed over exactly how Windows knows this. It's not magic, although it might seem a little occult-ish at first.
COM libraries come with Type Libraries, which list the Interfaces and CoClasses that the library provides. This Type Library is in the form of a file on your hard drive -- very often it is embedded directly in the same DLL or EXE as the library itself. Windows knows where to find the Type Library and the COM Library itself by looking in the Windows Registry. Entries in the Registry tell Windows where on the hard drive the DLL is located.
When you call CoCreateInstance, Windows looks the clsid up in the Windows Registry, finds the corresponding DLL, loads it, and executes the proper code in the DLL that implements the COM object.
How does this information get in to the Windows Registry? When a COM DLL is installed, it is registered. This is typically done by running regsvr32.exe, which in turn loads your DLL in to memory and calls a function named DllRegisterServer. That function, implemented in your COM server, adds the necesarry information to the Registry. If you are using ATL or another COM framework, this is probably being done under the hood so that you don't have to interface with the Registry directly. DllRegisterServer only needs to be called once, at install-time.
If you try to call CoCreateInstance for a COM object that has not yet been registered via the regsvr32/DllRegisterServer process, then CoCreateInstance will fail with an error that says:
Class Not Registered
Fortunately, the fix for this is to simply call regsvr32 on your COM server, and then try again.
You do not directly use LoadLibrary() with a COM library. CoCreateInstance() will call this function if it's not already, and then new an instance of the class you implemented in the library onto the heap and finally return to you a raw pointer to that object. Of course, it could fail during the process, and thus some mechanism for you to check the status like HRESULT.
For simplicity of using it, you can think of a COM library as a common DLL with 1) some predefined entry(main) function, 2) you have to call some predefined function like CoCreateInstance() to enter it, and accept that it's like that because it has to.
If the type library is embedded in the DLL you can import it into your project:
#import "whatever.dll"
This will auto-generate header files that get included in your project and allow you to use the exported objects.
Here's a bit of code showing how to get the class factory and use it to create a COM object. It uses a struct to keep track of the module handle and DllGetClassObject function pointer. You should hold on to the module handle until you are done with the COM object.
To use this function, you need to allocate an instance of the ComModuleInfo struct and set the szDLL to the DLL filename or full path name. Then call the function with the class id and interface Id of the COM object you want to get from that 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;
}
If it's a COM DLL, all you need do is add it as a reference to your project, and then you can call the functions that are within the DLL.
Yes, you can use the low level COM functions like DLLGetClassObject, but why would you?
Related
I have a question for com handling.
I'm using a third party dll which I include through #import, using named_guids as argument. I don't know if I should use another argument or not for proper importing..?
The problem is that there are different versions of the dll. One particular function that I'm using is added in the last version of the dll. So, if a costumer have an older version there is an exception - access violation executing location (some address). I understand that the error is telling me that the function can't be found, but how to catch this exception?
I tried with try-catch(_com_error) - nothing, try-catch(std::exception) - nothing, even try-catch(...) - still nothing.
Can I catch this kind of exception and log the exception and tell the costumer that there is a problem with the dll?
I'm not working with LoadLibrary and GetProcAddress.
The developer of the third party DLL violated a core principle of COM: Interfaces are immutable. This implies that once published, an interface may not change whatsoever. You are seeing the effect of what happens when this rule isn't followed: your call to the non-existent method results in an access violation.
If the COM interface derives from IDispatch, then you have an easy way to check, whether a method exists or not: Use GetIDsOfNames to get the DISPID of the method. If it doesn't exist, the function will return with a DISP_E_UNKNOWNNAME error:
DISPID dispID;
BSTR methodName = SysAllocString(L"MyMethod");
// Check hr return value
HRESULT hr = piDisp->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_SYSTEM_DEFAULT, &dispID);
SysFreeString(methodName);
I need to load a .dll file from another company code in order for me to call its function. But how do I load it when I dont know how to set its variable? The dll file is "Interop.OphirLMMeasurementLib.dll" and this is the cut out from data sheet.
"The OphirLMMeasurement COM object follows the standard COM practice
of returning an HRESULT from its methods. The HRESULT can be passed to
GetErrorFromCode to get a descriptive string, or the standard COM
GetErrorInfo function can be called (see documentation for your
environment). Some client environments (such as VB6 and all .NET
languages) do not return this HRESULT from the method call; instead
they throw an exception when a method returns a failure HRESULT. This
exception will normally contain within it the error number and the
descriptive string.
----------
3.3 Methods and Events
3.3.1 Device Communications
Close
CloseAll
GetKnownWirelessDevices
OpenUSBDevice
OpenWirelessDevice
ResetAllDevices
ResetDevice
ScanUSB
ScanWireless
So how do I call the function of ScanUSB from this .dll file?
The important word is COM. Short for Component Object Model, this is Microsoft's language-neutral mechanism to call methods on objects.
In this case, ScanUSB is a method on objects of the OphirLMMeasurement type. I don't know from just this bit how to get such an object - CreateInstance is the likely way. I suspect OpenUSBDevice will be necessary then, before ScanUSB. But we'd also need to know the arguments, and you left out those parts of the documentation.
i need to instantiate com object which is .dll and on local machine in visual c++,i know that it can be done by using CoCreateInstance("clsid") ,but i am confused about declaration.so can anyone explain all steps involved?
for late binding as well as early binding
is any import/inclusion required
how to declare com object ?
any other steps required before createinstance (e.g CoInitialize?)
or provide any specific reference involving step by step code
First you have to call CoInitialize and don't forget to callCoUnitialize if initialization was successful.
So your code will have the following structure:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
try
{
CoCreateInstance(...)
// ...
}
catch (_com_error &e)
{
//...
}
CoUninitialize();
}
For more information visit MSDN. I recommend you to start with The COM Library and then you should read something about CoInitialize and CoCreateInstance functions before you use them.
This tutorial could help you too: Introduction to COM - What It Is and How to Use It.
#import is very much recommended. if you import the typelib with #import, you'll be using the Native COM framework, which isolates some gritty details and makes life generally easier.
In Native COM, something like this:
LibName::IMyInterfacePtr pInterface;
In raw C++:
IMyInterface *pInterface;
But see above.
Call CoInitialize() in the beginning of the program, CoUninitialize() in the end. if running inside a DLL, then it's much more complicated.
I'm writing a small wrapper to get familiar with some important topics in C++ (WinAPI, COM, STL,OOP). For now, my class shall be able to create a (child) window.
Mainly, this window is connected to a global message loop that distribute messages to a local loop of the right instance (global is static, local is virtual).
Obviously, there are surely better ways to do that but I'm using std::maps to store HWND and their instance pointer in pairs (the Global loop looks for the pointer with the HWND-parameter, gets itself the pointer from the map and calls then the local loop).
Now, it appears that the map does not accept any values because of a unknown reason. It seems to allocate enough space but something went wrong anyway [ (error) 0 is displayed instead of the entries in visual C++).
I've looked that up in google as well and found out that maps cause some trouble when used in classes AND DLLs. May this be the reason and is there any solution??
Protected scope of class: static std::map<HWND,MAP_BASE_OBJECT*> m_LoopBuf
Implementation in .cpp-file: std::map<HWND,MAP_BASE_OBJECT*> HWindow::m_LoopBuf;
OK, because you asked for more code here you are:
I actually don't import the DLL because I use COM.
`
HRESULT hr = S_OK;
int retval = 1;
MSG msg = {0};
IClassFactory* Class = NULL;
IWindow* Wnd= NULL;
hr = CoInitialize(NULL);
hr = CoCreateInstance(CLSID_IWindow,NULL,CLSCTX_INPROC_SERVER,IID_IWindow,(LPVOID*)&Wnd);
Wnd->CreateOverlappedWindow(L"My window",L"MyClass",250,250,250,250,0,0,0,0,0,hInst);
while (GetMessage(&msg,NULL,0,0)>0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Wnd->Release();
return hr;
The COM-object IWindow is implemented in the class HWindow (in the DLL). The program expample works well until "CreateOverlappedWindow".
Without seeing how you are using DLLs specifically, I can only speculate that you are using static linking to the C++ runtime library in your DLLs. That means the memory allocated by each DLL is allocated on a heap separate from the main running program. You either have to change the linkage on your DLLs so that there is only one shared copy of the C++ runtime library or create an allocator for the map that is located in a specific location so that the map only uses one runtime library for its allocations.
I am trying to build an app that uses a COM component in VisualStudio ยด05
in native C++.
The mix of native and managed desciptions of things in the MSDN totally wrecked my
brain. (I think the MSDN is a total mess in that respect)
I need a short and simple native C++ sample of code to load my Component
and make it usable.
I am ok with the compiler creating wrappers and the like.
Please don't advise me to use the dialog based MFC example, because
it does not work with this component and is in itself a huge
pile of c... code.
Can this be an issue native com vs managed com?
I am totally lost, please give me some bearings...
EDIT: Thanks for all the help.
My problem is that all I have is a registered dll (actually the OCX, see below)
. I (personally) know
what the Interface should look like, but how do I tell my program?
There are no headers that define
IDs for Interfaces that I could use. But I read that the c++ compiler
can extract and wrap it up for me. Anyone know how this is done?
CLARIFICATION: I have only the OCX and a clue from the documentation
of the component, what methods it should expose.
Fully working example (exactly what you need) from my blog article: How to Call COM Object from Visual Studio C++?
// https://helloacm.com/how-to-call-com-object-from-visual-studio-c/
#include <iostream>
#include <objbase.h>
#include <unknwn.h>
#include <Propvarutil.h>
#import "wshom.ocx" no_namespace, raw_interfaces_only
using namespace std;
int main() {
HRESULT hr;
CLSID clsid;
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);
IWshShell *pApp = nullptr;
hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));
if (FAILED(hr) || pApp == nullptr) {
throw "Cannot Create COM Object";
}
int out;
VARIANT s;
InitVariantFromInt32(0, &s);
VARIANT title;
InitVariantFromString(PCWSTR(L"title"), &title);
VARIANT type;
InitVariantFromInt32(4096, &type);
BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
pApp->Popup(msg, &s, &title, &type, &out);
CoUninitialize();
cout << "Out = " << out;
return 0;
}
The bare minimums for instantiating a COM object are as follows:
1) Must have a COM apartment available.
This is accomplished by most applications by calling CoInitialize / CoInitializeEx to setup the COM library and the initial COM apartment if it is the first time for a process.
2) Call CoCreateInstance / CoCreateInstanceEx to create an object, and specify flags to denote how it will be instantiated.
3) Properly balance calls of AddRef and Release on the interfaces of any COM components that you create, calling the last Release() when you are done using a COM component.
-
In a managed application, #1 is almost always handled for you. #2 is abstracted away if you import a reference to a COM library, and you can just use the imported names as if they were .NET class definitions and such. #3 is automatically handled for you, but your needs may vary. Unfortunately, there are sometimes quirks in how references are handled in managed applications, which can cause COM objects to stick around longer than intended. The Marshal helper class in System.Runtime has methods that can help you out there if you encounter issues.
-
In an unmanaged application, you will have to do some legwork if you are creating an application from scratch.
Call CoInitialize/CoInitializeEx early in your application's main thread to setup the apartment.
When your application's main thread is about to exit, call CoUninitialize() to close out the apartment.
For additional threads that you create, you should also call CoInitialize/CoInitializeEx when they start if you need to use COM objects from those threads. Also, depending on your application you may want to set the apartment parameters.
For those threads, also call CoUninitialize() when they are exiting to cleanup properly.
I applaud your efforts to go with native C++ to deal with COM - you need to go through the pain to truly appreciate today's luxurious (managed) development environment :)
Back when the world (and I) were younger, Kraig Brockshmidt's book "Inside OLE" was the tome for making sense of COM (before COM even was COM). This book predates managed code, so no chance of managed confusion here. There's a second edition, too.
Don Box's books "Essential COM" and "Effective COM" were later, but welcome additions to the store of (unmanaged) COM knowledge.
However, if your wallet doesn't extend to acquiring these dusty old books, the Microsoft COM tutorial material here could help set you on the right track.
Happy hacking.
I use a combination of ATL smart COM pointers and the ATL::CAxWindow class for COM objects and components. I find the smart pointers particularly easy to use.
http://www.murrayc.com/learning/windows/usecomfromatl.shtml
http://76.105.92.243/notes/atlcom.html#import
http://msdn.microsoft.com/en-us/library/yx242b61%28VS.80%29.aspx
It would help if you gave a little more information about what you're exactly doing. Do you know what interfaces the object implements, etc?
In general though, the API that you can Google for more specific help is CoCreateInstance. You'll need to pass it the GUID of the object you want to play with. All COM objects implement the IUnknown interface, and you can query for any others it might have. So some sample pseudocode to get you started might look something like:
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
CoCreateInstance( CLSID,
ptrIUnknown,
ClassCxt, // generally CLSCTX_INPROC_SERVER,
riid , // reference id
(void **)&pRequest); // the interface that corresponds to the riid
Here you can query additional interfaces using the IUnknown interface you got from the ptrIUnknown.
Then clean up with
CoUninitialize()
Don Box's Essential COM is a great book on this topic. Also, just for testing how your COM object works, using something like VBScript makes this super easy. Also, it's probably worthy of note that the GUID of the class ID is stored in a somewhat unusual way, so if you're just ripping a GUID from the registry, you might have some trouble figuring out the ordering. That's probably for a different question though.
Try using #import from Visual C++. This will create smart pointer wrappers for the interfaces.