My COM server implemented in Visual C++ uses a ton of other C++ code. That other C++ code sometimes wraps code in __try-__except and translates structured exceptions into custom C++ exceptions. This part I cannot change.
No method of my COM server should let those exceptions propagate through the COM boundary so it has to catch and translate them into HRESULTs. Those custom C++ exceptions contain the original error code which was obtained during translation - it's something like EXCEPTION_ACCESS_VIOLATION. The question is how I craft an appropriate HRESULT value so that the client has as much information as possible as to what happened (and perhaps decide to restart the server (and itself in case of inproc) after seeing an access violation).
Suppose it was EXCEPTION_ACCESS_VIOLATION which is defined in WinBase.h
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
and the latter is defined in WinNT.h
#define STATUS_ACCESS_VIOLATION ((DWORD)0xC0000005L)
I could use HRESULT_FROM_WIN32() to translate that code into HRESULT assuming that it was a Win32 error in the first place.
Do I use HRESULT_FROM_WIN32() here or do I use any other way to do the translation?
You are supposed to return HRESULT code, where you choose appropriate code to indicate status of operation. It does not have to be a failure code, but you typically want to show something that satisfies FAILED(...) macro, e.g. E_FAIL, or DISP_E_EXCEPTION or HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION).
It is highly unlikely that callers compare against specific exception-related HRESULT, so specific failure code makes sense rather for diagnostic. Also, as you complete handling the exception before exiting from COM method, there is no need to return specific HRESULT code because no additional actions are required or necessary.
To provide additional information, it is possible to use ISupportErrorInfo, IErrorInfo and friends. Callers can retrieve free text description and many popular environments to this automatically, so for example .NET caller will have this additional information on exception message rather than standard message generated from HRESULT code.
ATL offers AtlReportError to wrap SetErrorInfo API, which also suggests on generating HRESULT code:
... If hRes is zero, then the first four versions of AtlReportError return DISP_E_EXCEPTION. The last two versions return the result of the macro MAKE_HRESULT( 1, FACILITY_ITF, nID ).
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.
As stated in the documentation for the IDirectDraw7::SetCooperativeLevel method, it states
You must use LoadLibrary to explicitly link to Ddraw.dll and then use GetProcAddress to access the SetCooperativeLevel method.
in the remarks. However when I attempt to do so (code below), it fails to work. Am I doing something wrong?
typedef HRESULT (*pSetCooperativeLevelFunc)(HWND, DWORD);
HMODULE ddrawLib = LoadLibrary(L"ddraw.dll");
pSetCooperativeLevelFunc SCL = (pSetCooperativeLevelFunc) GetProcAddress(
ddrawLib,
"SetCooperativeLevel"
);
if (SCL == NULL) {
// this happens
int error = GetLastError(); // 127 (ERROR_PROC_NOT_FOUND)
printf("Error getting SetCooperativeLevel function address: %i", error);
}
There is no exported SetCooperativeLevel function in ddraw.dll. Use DUMPBIN utility and check it yourself. You can get DirectDrawCreate/DirectDrawCreateEx and similar functions using GetProcAddress, but you can't extract individual methods of COM object.
Article is quite ridiculous and doesn't make sense. Perhaps it was supposed to tell you to get DirectDrawCreate from ddraw.dll or something like that, but there's little reason to do that.
Link with ddraw.lib, call DirectDrawCreate and access methods provided by IDirectDraw7 interface.
P.S. If you aren't familiar with dumpbin, I'd suggest to learn at least basic usage of this utility.
I think that's a documentation bug. It's been a long time since I used DirectDraw7, but I don't recall having to load it dynamically. It was just a method of the IDirectDraw7 interface and called like any other method.
Since DX9, ddraw.lib was completely removed from the SDK, so you need to call LoadLibrary/GetProcAddress to call DirectDrawCreate or DirectDrawEnumerate. Unfortunately MSDN got it wrong, and added the GetProcAddress remark to EVERY DirectDraw function, even the COM interfaces' methods.
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?
after successful coInitialize and cocreateinstance (COM server registration is perfect).. When i access a method in class it returns the error:
"First-chance exception in XYZ.exe (OLEAUT32.DLL): 0xC0000005: Access Violation".
By step by step debugging i found it gives this error while calling
// make the call
SCODE sc = m_lpDispatch->Invoke(dwDispID, IID_NULL, 0, wFlags, &dispparams, pvarResult, &excepInfo, &nArgErr);
in OLEDIST2.CPP file.
please help
thanks for your time.
the problem is solved at my end.
Problem lies in importing the type library (tlb) of the COM server in to my client application. Because of which, object gets a corrupted pointer. when a member function is called it gives ACCESS VOILATION error.
I actually imported the typelibrary in my Visual C++ application using "CLASS WIZARD" as mentioned # MSDN link:
http://msdn.microsoft.com/en-us/library/aa279228(VS.60).aspx
Which actually caused the above problem.
Later I found by importing typelibrary using simple #import "xyz.tlb"
it generates two files .tlh and .tli files which also contains all the classes and member function definitions.
When i used these files in my project it worked.
Sorry for bothering you......
thanks and regards
sandeep r.
Right now I can think of following checks:
Just check if you have a chance to
debug and see the value of
m_lpDispatch. Whether the value is
proper?( not NULL)
Just check whether accidentally
CoUninitialize() has been called ?
Just check whether COM object is
properly reference counted ?
Based on your code, the only thing that immediately jumps out is that m_lpDispatch could be NULL or an invalid pointer. Nothing else in that line should lead to an access violation as it doesn't actually access any memory.
If you can confirm that m_lpDispatch is indeed a valid variable it's possible it's a method being called by Invoke that's causing the problem. If so then you might want to turn on first chance exception handling for access violations in Visual Studio.
Go to: Debug -> Exceptions -> Win32 Exceptions
Check "Access Violation"
Then run your scenario under the debugger. It will cause visual studio to break at the point of the access violation. That should make it a bit more obvious where the actual problem is.