/////////////////////////////////////////////////////////////// // local.cpp - server for Interfaces, an MTA component // // ver 1.2 // // // // Platform: Dell XPs B1000r, Windows 2000 Professional // // Application: Demonstration for CSE791 - Distrib. Objs // // Author: Jim Fawcett, Syracuse Univ, CST 2-187 // // (315) 443-3948, jfawcett@twcny.rr.com // // Derived From: Eddon & Eddon, Inside Distributed COM, // // Microsoft Press, 1998 // /////////////////////////////////////////////////////////////// #define _WIN32_DCOM #include #include // For cout #include // For setw(...) #include #include "registry.h" // For registry functions #include "Component\component.h" // Generated by MIDL using namespace std; CRITICAL_SECTION iocs; long g_cComponents = 0; long g_cServerLocks = 0; HANDLE g_hEvent; //----< synchronized output >---------------------------------- template void safeOut(const T &t) { EnterCriticalSection(&iocs); cout << t; cout.flush(); LeaveCriticalSection(&iocs); } // //----< component declaration >-------------------------------- class CInterfaces : public IBaseTypes, public IStrings, public IPtrs, public IStruct, public IArrays { public: // IUnknown HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // IBaseTypes HRESULT __stdcall BaseTypes(INT x, DOUBLE y, WCHAR *z); // IStrings HRESULT __stdcall Strings(WCHAR *sIn, WCHAR **sOut); HRESULT __stdcall BStringIn(BSTR bstr); HRESULT __stdcall BStringOut(BSTR *pBstr); // IPtrs HRESULT __stdcall PutPtrRef(INT *pIn); HRESULT __stdcall PutPtrUnique(INT *pInOut); HRESULT __stdcall PutPtrPtr(INT *pIn1, INT *pIn2); // IStruct HRESULT __stdcall PutStruct(DemStruct *pDS); HRESULT __stdcall GetStruct(DemStruct *pDS); // IArrays HRESULT __stdcall FixedArray(INT Arr[]); HRESULT __stdcall ConformantArray(INT size, DOUBLE *pD); HRESULT __stdcall OpenArray(int size, int len, int first, int *pInt); HRESULT __stdcall TwoDimArray(INT rows, INT cols, DOUBLE ***pppD); CInterfaces() : m_cRef(1) { safeOut("\n Cinterfaces::CInterfaces()"); InterlockedIncrement(&g_cComponents); } ~CInterfaces() { safeOut("\n Cinterfaces::~CInterfaces()"); InterlockedDecrement(&g_cComponents); } private: LONG m_cRef; }; // //----< component AddRef >------------------------------------- ULONG CInterfaces::AddRef() { safeOut("\n CInterfaces::AddRef(): m_cRef = "); InterlockedIncrement(&m_cRef); safeOut(m_cRef); return m_cRef; } //----< component Release >------------------------------------ ULONG CInterfaces::Release() { safeOut("\n CInterfaces::Release(): m_cRef = "); InterlockedDecrement(&m_cRef); safeOut(m_cRef); if(m_cRef != 0) return m_cRef; SetEvent(g_hEvent); // Server main is waiting on this event delete this; return 0; } // //----< component QueryInterface >----------------------------- HRESULT CInterfaces::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IUnknown returning "); safeOut(this); *ppv = static_cast(this); } else if(riid == IID_IBaseTypes) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IBaseTypes returning "); safeOut(this); *ppv = static_cast(this); } else if(riid == IID_IStrings) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IStrings returning "); *ppv = static_cast(this); safeOut(*ppv); } else if(riid == IID_IPtrs) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IPtrs returning "); *ppv = static_cast(this); safeOut(*ppv); } else if(riid == IID_IStruct) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IStruct returning "); *ppv = static_cast(this); safeOut(*ppv); } else if(riid == IID_IArrays) { safeOut("\n CInterfaces::QueryInterface() "); safeOut("for IArrays returning "); *ppv = static_cast(this); safeOut(*ppv); } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } // //----< demonstrate some COM base type arguments >------------- HRESULT CInterfaces::BaseTypes(INT x, DOUBLE y, WCHAR *z) { // Demonstration for primitive types: // client sends in an INT and Double // client gets back a WCHAR safeOut("\n\n CInterfaces::BaseTypes(INT, DOUBLE, WCHAR*)"); DWORD ID = GetCurrentThreadId(); safeOut("\n BaseTypes:: thread ID is: "); safeOut(ID); WCHAR wch = L'a'; CHAR ch = wch; *z = wch; safeOut("\n received INT x = "); safeOut(x); safeOut("\n received DOUBLE y = "); safeOut(y); safeOut("\n sending WCHAR z = "); safeOut(ch); safeOut("\n"); return S_OK; } //----< demonstrate WCHAR string arguments >------------------- HRESULT CInterfaces::Strings(WCHAR *sIn, WCHAR **sOut) { // Demonstrates transmission of wide character strings // and conversion to and from strings of ASCII chars // - client allocates and frees memory for [in] strings // - server allocates memory for [out] strings // client must free that memory safeOut("\n\n CInterfaces::Strings(WCHAR *sIn, WCHAR **sOut)"); DWORD ID = GetCurrentThreadId(); safeOut("\n Strings:: thread ID is: "); safeOut(ID); const int MAX = 50; char buffer[MAX]; wcstombs(buffer,sIn,MAX); safeOut("\n received sIn = "); safeOut(buffer); safeOut("\n allocating memory for returned string"); WCHAR *temp = L"a string from component"; *sOut = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR)*wcslen(temp)+1); if(*sOut == NULL) { safeOut("\n memory alloc failure in Strings()\n"); return E_OUTOFMEMORY; } wcscpy(*sOut,temp); wcstombs(buffer,*sOut,MAX); safeOut("\n sending WCHAR *sOut = "); safeOut(buffer); return S_OK; } // //----< demonstrate use of BSTRs >----------------------------- HRESULT CInterfaces::BStringIn(BSTR bstr) { safeOut("\n\n CInterfaces::BStringIn(BSTR bstr)"); OLECHAR *pWC = (OLECHAR*)bstr; int numChar = wcslen(pWC); char *buffer = new char[2*numChar+1]; wcstombs(buffer,pWC,numChar+1); safeOut("\n BSTR value: "); safeOut(buffer); delete [] buffer; return S_OK; } //----< demonstrate use of BSTRs >----------------------------- HRESULT CInterfaces::BStringOut(BSTR *pBstr) { safeOut("\n\n CInterfaces::BStringOut(BSTR *pBstr)"); *pBstr = SysAllocString(OLESTR("This BSTR created by component")); OLECHAR *pWC = (OLECHAR*)(*pBstr); int numChar = wcslen(pWC); char *buffer = new char[2*numChar+1]; wcstombs(buffer,pWC,numChar+1); safeOut("\n BSTR value: "); safeOut(buffer); safeOut("\n"); delete [] buffer; return S_OK; } // //----< demonstrate ref pointer argument >--------------------- HRESULT CInterfaces::PutPtrRef(INT *pIn) { // - ref pointers must be non-null and point to allocated // memory in the client's address space. // - If a null pointer is sent, a marshaling error will // be returned to the caller. // - ref pointers must not be reset by the server's method safeOut("\n\n CInterfaces::PutPtrRef([in,out,ref] INT *pIn)"); DWORD ID = GetCurrentThreadId(); safeOut("\n PutPtrRef:: thread ID is: "); safeOut(ID); safeOut("\n received ref pointer pointing to value: "); safeOut(*pIn); safeOut("."); *pIn = 2*(*pIn); safeOut("\n comp: returning doubled value: "); safeOut(*pIn); safeOut("\n"); return S_OK; } // //----< demonstrate unique pointer argument >------------------ HRESULT CInterfaces::PutPtrUnique(INT *pInOut) { // Unique pointers may be null so the component semantics // should detect a null pointer and react accordingly. // Unique pointers may be reset by the server's method safeOut("\n CInterfaces::PutPtrUnique([in,unique] INT *pInOut)"); DWORD ID = GetCurrentThreadId(); safeOut("\n PutPtrUnique:: thread ID is: "); safeOut(ID); if(pInOut) { safeOut("\n received unique pointer pointing to value: "); safeOut(*pInOut); safeOut("."); } else { safeOut("\n received unique pointer with null value."); } safeOut("\n"); return S_OK; } // //----< demonstrate ptr pointer >------------------------------ HRESULT CInterfaces::PutPtrPtr(INT *pIn1, INT *pIn2) { // ptr pointers may be aliased to an object pointed to by // another pointer argument. The marshaler will detect // identical addresses and only marshal one object. safeOut("\n CInterfaces::PutPtrPtr([in,out,ptr] INT *pIn1,"); safeOut("[in,out,ptr] INT *pIn2)"); DWORD ID = GetCurrentThreadId(); safeOut("\n PutPtrPtr:: thread ID is: "); safeOut(ID); if(pIn1 != pIn2) { safeOut("\n received ptr pointers attached to unique objects."); safeOut("\n values pointed to are "); safeOut(*pIn1); safeOut(" and "); safeOut(*pIn2); safeOut("."); *pIn1 = 2*(*pIn1); *pIn2 = 3*(*pIn2); safeOut("\n returning modified values."); } else { safeOut("\n received aliased ptr pointers "); safeOut("attached to a common object: "); safeOut(*pIn1); safeOut("."); safeOut("\n returning modified value."); *pIn1 = 4*(*pIn1); } safeOut("\n"); return S_OK; } // //----< demonstrate input pointer to structure argument >------ HRESULT CInterfaces::PutStruct(/*[in]*/DemStruct *pDS) { // top level pointers like pDS require the client to allocate memory // to initialize before calling and dispose of after returning. // Embedded pointers like the array pointer in DemStruct if used with // [in] parameters are still managed by the client. // typedef struct tagDemStruct { // int len; // [unique] char *array; // } DemStruct; safeOut("\n\n CInterfaces::PutStruct([in] DemStruct *pDS"); DWORD ID = GetCurrentThreadId(); safeOut("\n PutStruct:: thread ID is: "); safeOut(ID); safeOut("\n received pointer DemStruct *pDS."); safeOut("\n pDS->len = "); safeOut(pDS->len); safeOut(", pDS->array = \""); safeOut(pDS->array); safeOut("\n"); return S_OK; } //----< demonstrate output pointer to structure argument >----- HRESULT CInterfaces::GetStruct(/*[out]*/DemStruct *pDS) { // top level pointers like pDS require the client to allocate memory // to initialize before calling and dispose of after returning. // Embedded pointers like the array pointer in DemStruct if used with // [out] parameters use memory allocated by the component, using // CoTaskMemAlloc() and deallocated by the client using CoTaskMemFree(). // typedef struct tagDemStruct { // int len; // [unique] char *array; // } DemStruct; safeOut("\n CInterfaces::GetStruct([out] DemStruct *pDS"); DWORD ID = GetCurrentThreadId(); safeOut("\n GetStruct:: thread ID is: "); safeOut(ID); safeOut("\n sending pointer DemStruct *pDS."); pDS->len = 30; pDS->array = static_cast(CoTaskMemAlloc(pDS->len)); strcpy( reinterpret_cast(pDS->array), "string allocated by component" ); safeOut("\n pDS->len = "); safeOut(pDS->len); safeOut(", pDS->array = \""); safeOut(pDS->array); safeOut("\"\f\n"); return S_OK; } // //----< demonstrate fixed array argument >--------------------- HRESULT CInterfaces::FixedArray(int Arr[]) { // Static arrays require no special handling. Interface assumes // that both client and component know size of array. safeOut("\n\n CInterfaces::FixedArray([in,out] INT Arr[5]"); DWORD ID = GetCurrentThreadId(); safeOut("\n FixedArray:: thread ID is: "); safeOut(ID); safeOut("\n received fixed array of known size: "); int i; for(i=0; i<5; i++) { safeOut(Arr[i]); safeOut(" "); } safeOut("\n"); return S_OK; } //----< demonstrate conformant array argument >---------------- HRESULT CInterfaces::ConformantArray(int size, double *pD) { // Conformant arrays allow client to specify // array size at run-time. safeOut("\n CInterfaces::ConformantArray([in] INT size,"); safeOut("[in, size_is(size)] DOUBLE *pDS)"); DWORD ID = GetCurrentThreadId(); safeOut("\n ConformantArray:: thread ID is: "); safeOut(ID); safeOut("\n received conformant array of size: "); safeOut(size); safeOut("\n "); int i; for(i=0; i---------------------- HRESULT CInterfaces::OpenArray(int size, int len, int first, int *pInt) { // Open arrays allow client to send part of an array. // Which part can be specified at run-time safeOut("\n CInterfaces::OpenArray("); safeOut("\n [in] INT size, [in] INT len, [in] INT first,"); safeOut("\n [in,size_is(size),length_is(len),first_is(first)] INT *pInt"); safeOut("\n )"); DWORD ID = GetCurrentThreadId(); safeOut("\n OpenArray:: thread ID is: "); safeOut(ID); safeOut("\n received open array of size "); safeOut(size); safeOut(". filling from "); safeOut(first); safeOut(" to "); safeOut(first+len-1); safeOut(".\n "); int i; for(i=0; i----------- /* HRESULT CInterfaces::TwoDimArray(int rows, int cols, double ***pppD) { // two dimensional array argument is specified as in C safeOut("\n received request for ("); safeOut(rows); safeOut(","); safeOut(cols); safeOut(") array\n"); double **ppD = (double**)CoTaskMemAlloc(rows*sizeof(double*)); pppD = &ppD; if(ppD == NULL) return E_OUTOFMEMORY; int i; for(i=0; i---------------------------- class CFactory : public IClassFactory { public: // IUnknown ULONG __stdcall AddRef(); ULONG __stdcall Release(); HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); // IClassFactory HRESULT __stdcall CreateInstance( IUnknown *pUnknownOuter, REFIID riid, void** ppv ); HRESULT __stdcall LockServer(BOOL bLock); CFactory() : m_cRef(1) { } ~CFactory() { } private: LONG m_cRef; }; //----< class factory AddRef >--------------------------------- ULONG CFactory::AddRef() { safeOut("\n CFactory::AddRef(): m_cRef = "); InterlockedIncrement(&m_cRef); safeOut(m_cRef); return m_cRef; } //----< class factory Release >-------------------------------- ULONG CFactory::Release() { safeOut("\n CFactory::Release(): m_cRef = "); InterlockedDecrement(&m_cRef); safeOut(m_cRef); if(m_cRef != 0) return m_cRef; delete this; return 0; } // //----< class factory QueryInterface >------------------------- HRESULT CFactory::QueryInterface(REFIID riid, void** ppv) { if((riid == IID_IUnknown) || (riid == IID_IClassFactory)) { safeOut("\n CFactory::QueryInterface() "); safeOut("for IUnknown or IClassFactory "); safeOut(this); *ppv = (IClassFactory*)this; } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } //----< create instance of component >------------------------- HRESULT CFactory:: CreateInstance(IUnknown *pUnknownOuter, REFIID riid, void** ppv) { if(pUnknownOuter != NULL) return CLASS_E_NOAGGREGATION; CInterfaces *pInterfaces = new CInterfaces; safeOut("\n CFactory::CreateInstance() creates "); safeOut(pInterfaces); if(pInterfaces == NULL) return E_OUTOFMEMORY; // QueryInterface probably for IID_IUNKNOWN HRESULT hr = pInterfaces->QueryInterface(riid, ppv); pInterfaces->Release(); return hr; } // //---< lock server process >----------------------------------- HRESULT CFactory::LockServer(BOOL bLock) { if(bLock) InterlockedIncrement(&g_cServerLocks); else InterlockedDecrement(&g_cServerLocks); return S_OK; } //----< register component >----------------------------------- // // - load type library // - place server entries in registry // void RegisterComponent() { ITypeLib* pTypeLib; LoadTypeLibEx(L"Component.exe", REGKIND_DEFAULT, &pTypeLib); pTypeLib->Release(); RegisterServer( "Component.exe", CLSID_Interfaces, "Interfaces Demo", "Component.Interfaces", "Component.Interfaces.1", NULL ); } // //----< check for designated command line parameters >--------- // // - RegServer: registers component as LocalServer32 // - UnregServer: removes entries from registry // - Embedding causes main to: // - create class factory // - register component with COM run-time // passing pointer to class factory // - wait for termination event // - then unregister component with COM run-time // void CommandLineParameters(int argc, char** argv) { RegisterComponent(); if(argc < 2) { safeOut("\n No command line parameter, but registered anyway\n"); exit(false); } char* szToken = strtok(argv[1], "-/"); if(_stricmp(szToken, "RegServer") == 0) { RegisterComponent(); safeOut("\n RegServer\n"); exit(true); } if(_stricmp(szToken, "UnregServer") == 0) { UnRegisterTypeLib(LIBID_Component, 1, 0, LANG_NEUTRAL, SYS_WIN32); UnregisterServer( CLSID_Interfaces, "Component.Interfaces", "Component.Interfaces.1" ); safeOut("\n UnregServer\n"); exit(true); } if(_stricmp(szToken, "Embedding") != 0) { safeOut("\n Invalid command line parameter: "); safeOut(szToken); exit(false); } } // //----< process start-up entry point >------------------------- void main(int argc, char** argv) { InitializeCriticalSection(&iocs); CommandLineParameters(argc, argv); safeOut("\n Component Providing Variety of Interface Types "); safeOut("\n ================================================\n"); safeOut("\n main::CoInitializeEx(NULL,COINIT_MULTITHREADED)"); CoInitializeEx(NULL, COINIT_MULTITHREADED); DWORD ID = GetCurrentThreadId(); safeOut("\n\n main:: thread ID in server MTA is: "); safeOut(ID); safeOut("\n"); IClassFactory *pClassFactory = new CFactory(); safeOut("\n main::CoRegisterClassObject(...)"); DWORD dwRegister; CoRegisterClassObject( CLSID_Interfaces, pClassFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister ); g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); safeOut("\n main::WaitForSingleObject(g_hEvent,INFINITE)"); WaitForSingleObject(g_hEvent, INFINITE); safeOut("\n main::CoRevokeClassObject(...)"); CoRevokeClassObject(dwRegister); safeOut("\n main::pClassFactory->Release()"); pClassFactory->Release(); safeOut("\n main::CoUninitialize()"); CoUninitialize(); char ch; safeOut("\n\n strike any key to exit: "); cin.read(&ch,1); DeleteCriticalSection(&iocs); }