pair ChatSession*>(pwszSessionName, pNew)); (*ppcs = pNew)->AddRef(); hr = S_OK; } else hr = E_OUTOFMEMORY; } } else { (*ppcs = (*it).second)->AddRef(); hr = S_OK; } Unlock(); CoTaskMemFree(pwszUser); return hr; } STDMETHODIMP ChatSessionClass::DeleteSession(const OLECHAR *pwszSessionName) { if (pwszSessionName == 0) return E_INVALIDARG; HRESULT hr = E_FAIL; OLECHAR *pwszUser = GetCaller(); if (CheckAccess(pwszUser)) { Lock(); SESSIONMAP::iterator it = m_sessions.find(pwszSessionName); if (it == m_sessions.end()) { hr = E_FAIL; } else { (*it).second->Disconnect(); (*it).second->Release(); m_sessions.erase(it); hr = S_OK; } Unlock(); } else hr = E_ACCESSDENIED; CoTaskMemFree(pwszUser); return hr; } // class SessionNamesEnumerator vector SessionNamesEnumerator::Strings(void) { if (m_pStrings) return *m_pStrings; else return *(m_pCloneSource->m_pStrings); } void SessionNamesEnumerator::Lock(void) { EnterCriticalSection(&m_csLock); } void SessionNamesEnumerator::Unlock(void) { LeaveCriticalSection(&m_csLock); } SessionNamesEnumerator::SessionNamesEnumerator( ChatSessionClass *pSessionClass) : m_cRef(0), m_pStrings(0), m_pCloneSource(0) { typedef ChatSessionClass::SESSIONMAP::iterator iterator; ChatSessionClass::SESSIONMAP &sessions = pSessionClass->m_sessions; m_pStrings = new vector pSessionClass->Lock(); for (iterator it = sessions.begin(); it != sessions.end(); it++) { m_pStrings->push_back((*it).first); } pSessionClass->Unlock(); m_cursor = Strings().begin(); InitializeCriticalSection(&m_csLock); } SessionNamesEnumerator::SessionNamesEnumerator( SessionNamesEnumerator *pCloneSource) : m_cRef(0), m_pStrings(0), m_pCloneSource(pCloneSource) { m_pCloneSource->AddRef(); m_cursor = Strings().begin(); InitializeCriticalSection(&m_csLock); } SessionNamesEnumerator::~SessionNamesEnumerator(void) { if (m_pCloneSource) m_pCloneSource->Release(); else if (m_pStrings) delete m_pStrings; DeleteCriticalSection(&m_csLock); } // IUnknown methods STDMETHODIMP SessionNamesEnumerator::QueryInterface(REFIID riid, void **ppv) { if (riid == IID_IUnknown) *ppv = static_cast else if (riid == IID_IEnumString) *ppv = static_cast else return (*ppv = 0), E_NOINTERFACE; reinterpret_cast return S_OK; } STDMETHODIMP_(ULONG) SessionNamesEnumerator::AddRef(void) { ModuleLock(); return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) SessionNamesEnumerator::Release(void) { LONG res = InterlockedDecrement(&m_cRef); if (res == 0) delete this; ModuleUnlock(); return res; } // IEnumString methods STDMETHODIMP SessionNamesEnumerator::Next(ULONG cElems, OLECHAR **rgElems, ULONG *pcFetched) { if (cElems > 1 && pcFetched == 0) return E_INVALIDARG; ULONG cActual = 0; vector Lock(); while (cActual < cElems && m_cursor != rstrings.end()) { if (rgElems[cActual] = OLESTRDUP((*m_cursor).c_str())) { m_cursor++; cActual++; } else // allocation error, unwind { while (cActual > 0) { cActual–; CoTaskMemFree(rgElems[cActual]); rgElems[cActual] = 0; } break; } } Unlock(); if (cActual) *pcFetched = cActual; return cActual == cElems ? S_OK : S_FALSE; } STDMETHODIMP SessionNamesEnumerator::Skip(ULONG cElems) { ULONG cActual = 0; vector Lock(); while (cActual < cElems && m_cursor != rstrings.end()) { m_cursor++; cActual++; } Unlock(); return cActual == cElems ? S_OK : S_FALSE; } STDMETHODIMP SessionNamesEnumerator::Reset(void) { Lock(); m_cursor = Strings().begin(); Unlock(); return S_OK; } STDMETHODIMP SessionNamesEnumerator::Clone(IEnumString **ppes) { if (ppes == 0) return E_INVALIDARG; SessionNamesEnumerator *pCloneSource = m_pCloneSource; if (pCloneSource == 0) // we are the source m_pCloneSource = this; *ppes = new SessionNamesEnumerator(pCloneSource); if (*ppes) { (*ppes)->AddRef(); return S_OK; } return E_OUTOFMEMORY; } ///////////////////////////////////////////////////// // // svc.cpp // // Copyright 1997, Don Box/Addison Wesley // // This code accompanies the book "The Component // Object Model" from Addison Wesley. Blah blah blah // // #define _WIN32_WINNT 0x403 #include #include #include #include #include «ChatSession.h» #include «../include/COMChat_i.c» #if !defined(HAVE_IID_IACCESSCONTROL) // there is a common bug is the SDK headers and libs // that causes IID_IAccessControl to be undefined. // We define it here to give the GUID linkage. DEFINE_GUID(IID_IAccessControl,0xEEDD23E0, 0x8410, 0x11CE, 0xA1, 0xC3, 0x08, 0x00, 0x2B, 0x2B, 0x8D, 0x8F); #endif // standard MTA lifetime management helpers HANDLE g_heventDone = CreateEvent(0, TRUE, FALSE, 0); void ModuleLock(void) { CoAddRefServerProcess(); } void ModuleUnlock(void) { if (CoReleaseServerProcess() == 0) SetEvent(g_heventDone); } // standard self-registration table const char *g_RegTable[][3] = { { «CLSID\\{5223A053-2441-11d1-AF4F-0060976AA886}», 0, «ChatSession» }, { «CLSID\\{5223A053-2441-11d1-AF4F-0060976AA886}», «AppId», «{5223A054-2441-11d1-AF4F-0060976AA886}» }, { «CLSID\\{5223A053-2441-11d1-AF4F-0060976AA886}\\LocalServer32», 0, (const char*)-1 // rogue value indicating file name }, { «AppID\\{5223A054-2441-11d1-AF4F-0060976AA886}», 0, «ChatSession Server» }, { «AppID\\{5223A054-2441-11d1-AF4F-0060976AA886}», «RunAs», «Domain\\ReplaceMe» }, { «AppID\\{5223A054-2441-11d1-AF4F-0060976AA886}», «Chat Admins Group», «Domain\\ReplaceMe» }, { «AppID\\{5223A054-2441-11d1-AF4F-0060976AA886}», «Chat Users Group», «Domain\\ReplaceMe» }, { «AppID\\COMChat.exe», «AppId», «{5223A054-2441-11d1-AF4F-0060976AA886}» }, }; // self-unregistration routine STDAPI UnregisterServer(void) { HRESULT hr = S_OK; int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable); for (int i = nEntries – 1; i >= 0; i–){ const char *pszKeyName = g_RegTable[i][0]; long err = RegDeleteKeyA(HKEY_CLASSES_ROOT, pszKeyName); if (err != ERROR_SUCCESS) hr = S_FALSE; } return hr; } // self-registration routine STDAPI RegisterServer(HINSTANCE hInstance = 0) { HRESULT hr = S_OK; // look up server's file name char szFileName[MAX_PATH]; GetModuleFileNameA(hInstance, szFileName, MAX_PATH); // register entries from table int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable); for (int i = 0; SUCCEEDED(hr) && i < nEntries; i++) { const char *pszKeyName = g_RegTable[i][0]; const char *pszValueName = g_RegTable[i][1]; const char *pszValue = g_RegTable[i][2]; // map rogue value to module file name if (pszValue == (const char*)-1) pszValue = szFileName; HKEY hkey; // create the key long err = RegCreateKeyA(HKEY_CLASSES_ROOT, pszKeyName, &hkey); if (err == ERROR_SUCCESS) { // set the value err = RegSetValueExA(hkey, pszValueName, 0, REG_SZ, (const BYTE*)pszValue, (strlen(pszValue) + 1)); RegCloseKey(hkey); } if (err != ERROR_SUCCESS) { // if cannot add key or value, back out and fail UnregisterServer(); hr = SELFREG_E_CLASS; } } return hr; } // these point to standard access control objects // used to protect particular methods IAccessControl *g_pacUsers = 0; IAccessControl *g_pacAdmins = 0; // this routine is called at process init time // to build access control objects and to allow // anonymous access to server by default HRESULT InitializeApplicationSecurity(void) { // load groupnames from registry static OLECHAR wszAdminsGroup[1024]; static OLECHAR wszUsersGroup[1024]; HKEY hkey; long err = RegOpenKeyEx(HKEY_CLASSES_ROOT, __TEXT(«AppID\\{5223A054-2441-11d1-AF4F-0060976AA886}»), 0, KEY_QUERY_VALUE, &hkey); if (err == ERROR_SUCCESS) { DWORD cb = sizeof(wszAdminsGroup); err = RegQueryValueExW(hkey, L"Chat Admins Group", 0, 0, (BYTE*)wszAdminsGroup, &cb); cb = sizeof(wszAdminsGroup); if (err == ERROR_SUCCESS) err = RegQueryValueExW(hkey, L"Chat Users Group", 0, 0, (BYTE*)wszUsersGroup, &cb); RegCloseKey(hkey); } if (err != ERROR_SUCCESS) return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); // declare vectors of user/groups for 2 access // control objects ACTRL_ACCESS_ENTRYW rgaaeUsers[] = { { {0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_GROUP, wszUsersGroup }, ACTRL_ACCESS_ALLOWED, COM_RIGHTS_EXECUTE, 0, NO_INHERITANCE, 0 }, }; ACTRL_ACCESS_ENTRY_LISTW aaelUsers = { sizeof(rgaaeUsers)/sizeof(*rgaaeUsers), rgaaeUsers }; ACTRL_PROPERTY_ENTRYW apeUsers = { 0, &aaelUsers, 0 }; ACTRL_ACCESSW aaUsers = { 1, &apeUsers }; ACTRL_ACCESS_ENTRYW rgaaeAdmins[] = { { {0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_GROUP, wszAdminsGroup }, ACTRL_ACCESS_ALLOWED, COM_RIGHTS_EXECUTE, 0, NO_INHERITANCE, 0 }, }; ACTRL_ACCESS_ENTRY_LISTW aaelAdmins = { sizeof(rgaaeAdmins)/sizeof(*rgaaeAdmins), rgaaeAdmins }; ACTRL_PROPERTY_ENTRYW apeAdmins = { 0, &aaelAdmins, 0 }; ACTRL_ACCESSW aaAdmins = { 1, &apeAdmins }; HRESULT hr = CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_ANONYMOUS, 0, EOAC_NONE, 0); if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_DCOMAccessControl, 0, CLSCTX_ALL, IID_IAccessControl, (void**)&g_pacUsers); if (SUCCEEDED(hr)) hr = g_pacUsers->SetAccessRights(&aaUsers); if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_DCOMAccessControl, 0, CLSCTX_ALL, IID_IAccessControl, (void**)&g_pacAdmins); if (SUCCEEDED(hr)) hr = g_pacAdmins->SetAccessRights(&aaAdmins); } if (FAILED(hr)) { if (g_pacAdmins) { g_pacAdmins->Release(); g_pacAdmins = 0; } if (g_pacUsers) { g_pacUsers->Release(); g_pacUsers = 0; } } } return hr; } // the main thread routine that simply registers the class // object and waits to die int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR szCmdParam, int) { const TCHAR *pszPrompt = __TEXT("Ensure that you have properly ") __TEXT("configured the application to ") __TEXT("run as a particular user and that ") __TEXT("you have manually changed the ") __TEXT("Users and Admins Group registry ") __TEXT(«settings under this server's AppID.»); HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hr)) return hr; // look for self-registration flags if (strstr(szCmdParam, «/UnregServer») != 0 || strstr(szCmdParam, «-UnregServer») != 0) { hr = UnregisterServer(); CoUninitialize(); return hr; } else if (strstr(szCmdParam, «/RegServer») != 0 || strstr(szCmdParam, «-RegServer») != 0) { hr = RegisterServer(); MessageBox(0, pszPrompt, __TEXT(«COMChat»), MB_SETFOREGROUND); CoUninitialize(); return hr; } // set up process security hr = InitializeApplicationSecurity(); if (SUCCEEDED(hr)) { // register class object and wait to die DWORD dwReg; static ChatSessionClass cmc; hr = CoRegisterClassObject(CLSID_ChatSession, static_cast CLSCTX_LOCAL_SERVER REGCLS_SUSPENDED|REGCLS_MULTIPLEUSE, &dwReg); if (SUCCEEDED(hr)) { hr = CoResumeClassObjects(); if (SUCCEEDED(hr)) WaitForSingleObject(g_heventDone, INFINITE); CoRevokeClassObject(dwReg); } g_pacUsers->Release(); g_pacAdmins->Release(); } if (FAILED(hr)) MessageBox(0, pszPrompt, __TEXT(«Error»), MB_SETFOREGROUND); CoUninitialize(); return 0; } Source Code COM Chat Compilable versions of the source code in Appendix B of the book. YACL Yet another COM library. Contains the various macros and C++ classes described in the book. IGlobalInterfaceTable Wrapper/Sample A simplification of apartment-independent pointer use. HostHook A custom channel hook that allows you to find out the caller's and callee's network address. APTSUR A custom surrogate that spawns distinct STA threads for each activation request. Custom Moniker Stuff I worked with some friends on a custom moniker framework. Here is some of the 1st bits and pieces. Yet Another COM Library (YACL) – Preview This is my first drop. It contains a family of preprocessor macros that automate the boilerplate activities used in COM programming. Please send comments and bug reports to [email protected] The freshest drop is always available at http://www.develop.com/dbox/yacl.zip Easily used without Wizard support Easily kept in one's head Modular (use only what you need and nothing else) Extensible Small Code Fast Code No DLL ever Compiler-friendly Platform-neutral where possible (including 16-bit Windows) Anal-rententive Smart Pointer Efficient and intuitive Unicode handling Table-driven QueryInterface Table-driven Registration Table-driven Class management Generic Class Factory implementation. Preprocessor macros for de facto IUnknown implementation techniques. Preprocessor macros for de facto module management routines. Preprocessor macros for de facto DllXXX routines. Preprocessor macros for de facto out-of-proc CRCO/Wait/CRCO sequence. Performance/size tuning Compiler/Platform testing Verify ATL/MFC interoperation Macro-ization of smart pointer for 16-bit windows Add optional exception semantics to smart pointer Map COM hresults/exception to C++ exceptions Add support for IDispatch and friends Add support for IConnectionPoint and friends Add IEnum -> stl thunks