I'm currently trying to build a C++ library (DLL file) that interfaces with a COM component, to make it usable in Java. The idea was that I'd build a very small C++ DLL with a class that "wraps" around the COM component, and then export it using SWIG. I got pretty far by using an #import statement:
#import "ComponentName.dll"
And calling CoInitialize() and creating an instance of the component (via the IComponentNamePtr class that was generated by Visual Studio). This worked for all the normal COM method calls, which was good.
However, I can't work out how to get events working. I see there's a IComponentNameEventsPtr that complements the main "smart pointer" class, but I couldn't work out what to do to get it working
I've tried all the following to get events working:
Does anyone know how to do this? What's the easiest approach to take? My background is in C# and PHP so I don't have much experience with using COM in C++.
tldr: What's the easiest way to use COM events in a C++ DLL?
Implement the source interface in your code (using any mechanism, including perhaps generating plain C code using the midl compiler). In your external typelibrary (the one which you are consuming) Look for the interface that looks like:
[source] interface IOutGoing;
Once you implemented it, register it using Advise
on the object that sources the events (unregister it with Unadvise
)
Here is a snippet that show a typical use, assuming you went the MIDL way (with ATL/MFC you'd have to write less code but know more macros/templates)
class CSink : public IOutGoing
{
public:
// IUnknown
ULONG __stdcall AddRef();
ULONG __stdcall Release();
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);
// IOutGoing
HRESULT __stdcall GotMessage(int Message);
CSink() : m_cRef(0) { }
~CSink() { }
private:
long m_cRef;
};
IUnknown* pUnknown;
CoCreateInstance(CLSID_XXXXXXXXX, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);
IConnectionPointContainer* pConnectionPointContainer;
hr = pUnknown->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
hr = pConnectionPointContainer->FindConnectionPoint(IID_IOutGoing, &pConnectionPoint);
// Instantiate the sink object.
CSink* mySink = new CSink;
// Give the connectable object a pointer to the sink.
DWORD dwCookie;
pConnectionPoint->Advise((IUnknown*)mySink, &dwCookie);
I actually got this working myself using the Unified Event Model. Some notes from my experience in getting it working:
You need to initialise ATL otherwise a null pointer error occurs when hooking up events. If you don't want to use ATL in your project (like I didn't - My project is plain C++), something like this in dllmain.cpp works fine (initialises ATL with a dummy module):
class CDummyModule : public CAtlDllModuleT<CDummyModule> {};
CDummyModule _Module;
You need to add "embedded_idl" to the end of the #import line.
#import "ComponentName.dll" embedded_idl
If the component has any structs embedded in it, you may get some MIDL2025 errors ("expecting a type specification near") due to a bug in Visual Studio (see https://connect.microsoft.com/VisualStudio/feedback/details/333473/midl2025-migrating-an-attributed-com-project-to-vs-2008-pro-with-exported-structures and http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/03b78133-5eac-4754-b9af-fc864a9389a3). Solution is to add:
[importidl("vsbugfix.idl")];
And then add a
typedef struct StructName StructName;
For each struct that throws an error
Once that's done, you should be able to initialise COM and create an instance of your object. Ugly example code:
IComponentName blah;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
MessageBox(NULL, "Failed to initialize COM", "Hi!", 0);
throw "Failed to initialize COM";
}
try
{
hr = blah.CreateInstance("Something.Something");
if (FAILED(hr))
{
CoUninitialize();
MessageBox(NULL, "Failed to initialize the COM library!!", "Hi!", 0);
throw "Failed to initialize the COM library";
}
}
catch (...)
{
MessageBox(NULL, "Exception occured when initialising library!", "Error", 0);
CoUninitialize();
throw "Exception occured when initialising library!";
}
Once you have your COM object, you can connect events as per the MSDN "Event Handling in COM" article:
__hook(&IComponentNameEvents::OnWhatever, blah, &EventHandlerClass::OnWhatever);
Be sure to unhook all events before calling CoUninitialize() otherwise you'll get errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With