/////////////////////////////////////////////////////////////////////// // dynamic vtable sink.cpp - client that builds a sink vtbl // // dynamically, e.g., at run-time // // // // Eddon & Eddon, Inside Distributed COM, authors: // // Jim Fawcett, CSE791 - Distributed Objects, Cosmetic Modifications // /////////////////////////////////////////////////////////////////////// // client.cpp #define _WIN32_DCOM #include #include #include #include "Component\component.h" // This sink can implement any interface at runtime // In C++, struct == public class struct CStink // class for the generic sink object { // The First item must be a pointer to the vtbl of the sink object // Here we can see the binary standard that defines COM void (__stdcall **pVtbl)(); // Declared as a pointer to one or more function pointers // Next comes the object's data long m_cRef; // reference counting variable }; // Non-static methods in a class always have an implicit 'this' argument // that points to the current object // Since these methods are not part of a class we make believe... // Every method in the interface must pretend to have a 'this' argument // as all callers will provide it ULONG __stdcall CStink_AddRef(CStink* me) { // me is a stand-in for 'this', a reserved keyword in C++ // Everything must be explicitly accessed through the pointer to object // Now we can appreciate what C++ does automatically return ++me->m_cRef; } ULONG __stdcall CStink_Release(CStink* me) { if(--me->m_cRef != 0) return me->m_cRef; delete me; // We can even use delete return 0; } // HRESULT __stdcall CStink_QueryInterface(CStink* me, REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IOutGoing) *ppv = &me->pVtbl; // Here's a pointer to our interface else { *ppv = NULL; return E_NOINTERFACE; } CStink_AddRef(me); return S_OK; } // This method could be generated dynamically based on type information HRESULT __stdcall GotMessage(CStink *me, int Message) { MessageBeep(MB_OK); cout << "StinkGotMessage() is " << (char)Message << endl; return S_OK; } //----< entry point >---------------------------------------------------- void main() { cout << "Client: Calling CoInitialize()" << endl; CoInitializeEx(NULL, COINIT_MULTITHREADED); IUnknown* pUnknown; cout << "Client: Calling CoCreateInstance() " << endl; CoCreateInstance( CLSID_InsideDCOM,NULL,CLSCTX_LOCAL_SERVER,IID_IUnknown,(void**)&pUnknown ); // IConnectionPointContainer* pConnectionPointContainer; HRESULT hr = pUnknown->QueryInterface( IID_IConnectionPointContainer, (void**)&pConnectionPointContainer ); if(FAILED(hr)) { cout << "Not a connectable object, going to exit now" << endl; return; } IConnectionPoint* pConnectionPoint; hr = pConnectionPointContainer ->FindConnectionPoint(IID_IOutGoing, &pConnectionPoint); cout << "FindConnectionPoint returns " << hr << " pConnectionPoint = " << pConnectionPoint << endl; // Instantiate the object, sort of... CStink* mySink = new CStink; int num_methods = 4; // 3 for IUnknown + number of methods in outgoing interfaces // Allocate memory for the vtbl and cast the returned void* to a // pointer to a bunch of function pointers void (__stdcall **IStink)() = (void (__stdcall **)())CoTaskMemAlloc(num_methods * sizeof(void*)); // 4 bytes per pointer // Initialize the vtbl to point to our implementations // These three always come first! (in this order) *(IStink + 0) = (void (__stdcall *)())CStink_QueryInterface; *(IStink + 1) = (void (__stdcall *)())CStink_AddRef; *(IStink + 2) = (void (__stdcall *)())CStink_Release; // Now add any additional methods to the vtbl based on the // available type information for the outgoing interface *(IStink + 3) = (void (__stdcall *)())GotMessage; // IOutGoing only has one additional method // Give the sink a brain mySink->pVtbl = IStink; // Initialize the sink's reference counter mySink->m_cRef = 0; // DWORD dwCookie; hr = pConnectionPoint->Advise((IUnknown*)mySink, &dwCookie); cout << "Client: IConnectionPoint::Advise returned " << hr << endl; cout << "Press any key to exit" << endl; _getch(); pConnectionPoint->Unadvise(dwCookie); // Now free the vtbl, the sink has probably already deleted itself CoTaskMemFree(IStink); pConnectionPoint->Release(); pConnectionPointContainer->Release(); cout << "Client: Calling Release() for pUnknown" << endl; hr = pUnknown->Release(); cout << "Client: Calling CoUninitialize()" << endl; CoUninitialize(); }