/* - CLSFACT.CPP - * Microsoft NetMeeting * Network Audio Control DLL * Generic class factory * * Revision History: * * When Who What * -------- ------------------ --------------------------------------- * 2.6.97 Yoram Yaacovi Copied from qosfact.cpp * Added handling of CInstallCodecs * 2.27.97 Yoram Yaacovi Added DllRegisterServer and DllUnregisterServer * * Functions: * DllGetClassObject * DllCanUnloadNow * DllRegisterServer * DllUnregisterServer * CClassFactory::QueryInterface * CClassFactory::AddRef * CClassFactory::Release * CClassFactory::CreateInstance * CClassFactory::LockServer * CreateClassFactory * * * Object types supported: * CQoS * CInstallCodecs * * Notes: * To add support for manufacturing objects of other types, change: * DllGetClassObject * DllCanUnloadNow * Add the CLSID and description to aObjectInfo * */ #include int g_cObjects = 0; // A general object count. Used for LockServer. EXTERN_C int g_cQoSObjects; // QoS object count. Public in qos\qos.cpp EXTERN_C int g_cICObjects; // CInstallCodecs object count. Public in inscodec.cpp EXTERN_C HINSTANCE g_hInst; // global module instance // Untested code for registering COM objects in the NAC // when enabled, DllRegisterServer and DllUnregisterServer should be exported // in nac.def #define GUID_STR_LEN 40 typedef struct { const CLSID *pclsid; char szDescription[MAX_PATH]; } OBJECT_INFO; static OBJECT_INFO aObjectInfo[]= {&CLSID_QoS, TEXT("Microsoft NetMeeting Quality of Service"), &CLSID_InstallCodecs, TEXT("Microsoft NetMeeting Installable Codecs"), NULL, TEXT("")}; // Internal helper functions BOOL DeleteKeyAndSubKeys(HKEY hkIn, LPTSTR pszSubKey); BOOL UnregisterUnknownObject(const CLSID *prclsid); BOOL RegisterUnknownObject(LPCTSTR pszObjectName, const CLSID *prclsid); /*************************************************************************** Name : DllGetClassObject Purpose : Standard COM entry point to create a COM object Parameters: Returns : HRESULT Comment : ***************************************************************************/ STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv) { HRESULT hr; CClassFactory *pObj; *ppv = 0; // find out object of what class we need to create and instantiate // the class factory with the correct create function if (CLSID_QoS == rclsid) { DBG_SAVE_FILE_LINE pObj = new CClassFactory(CreateQoS); } else if (CLSID_InstallCodecs == rclsid) { DBG_SAVE_FILE_LINE pObj = new CClassFactory(CreateInstallCodecs); } else { hr = CLASS_E_CLASSNOTAVAILABLE; goto out; } if (!pObj) { hr = E_OUTOFMEMORY; goto out; } hr = pObj->QueryInterface(riid, ppv); if (FAILED(hr)) delete pObj; out: return hr; } /*************************************************************************** Name : DllCanUnloadNow Purpose : Standard COM entry point tell a DLL it can unload Parameters: Returns : HRESULT Comment : ***************************************************************************/ STDAPI DllCanUnloadNow () { HRESULT hr=S_OK; int vcObjects = g_cObjects + g_cQoSObjects + g_cICObjects; return (vcObjects == 0 ? S_OK : S_FALSE); } /*************************************************************************** Name : DllRegisterServer Purpose : Standard COM entry point to register a COM server Parameters: Returns : HRESULT Comment : ***************************************************************************/ STDAPI DllRegisterServer(void) { ULONG i=0; HRESULT hr=NOERROR; while ((aObjectInfo[i].pclsid != NULL) && (lstrlen(aObjectInfo[i].szDescription) != 0)) { if (!RegisterUnknownObject(aObjectInfo[i].szDescription, aObjectInfo[i].pclsid)) { hr = E_FAIL; goto out; } // next server to register i++; } out: return hr; } /*************************************************************************** Name : DllUnregisterServer Purpose : Standard COM entry point to unregister a COM server Parameters: Returns : HRESULT Comment : ***************************************************************************/ STDAPI DllUnregisterServer(void) { ULONG i=0; HRESULT hr=NOERROR; while ((aObjectInfo[i].pclsid != NULL) && (lstrlen(aObjectInfo[i].szDescription) != 0)) { if (!UnregisterUnknownObject(aObjectInfo[i].pclsid)) { hr = E_FAIL; goto out; } // next server to register i++; } out: return hr; } /*************************************************************************** ClassFactory: Generic implementation ***************************************************************************/ CClassFactory::CClassFactory(PFNCREATE pfnCreate) { m_cRef=0; m_pfnCreate = pfnCreate; return; } CClassFactory::~CClassFactory(void) { return; } /*************************************************************************** IUnknown Methods for CClassFactory ***************************************************************************/ HRESULT CClassFactory::QueryInterface (REFIID riid, void **ppv) { HRESULT hr=NOERROR; #ifdef DEBUG // parameter validation if (IsBadReadPtr(&riid, (UINT) sizeof(IID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } if (IsBadWritePtr(ppv, sizeof(LPVOID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } #endif // DEBUG *ppv = 0; if (IID_IUnknown == riid || IID_IClassFactory == riid) { *ppv = this; } else { hr = ResultFromScode(E_NOINTERFACE); goto out; } ((IUnknown *)*ppv)->AddRef(); out: return hr; } ULONG CClassFactory::AddRef (void) { return ++m_cRef; } ULONG CClassFactory::Release (void) { // if the cRef is already 0 (shouldn't happen), assert, but let it through ASSERT(m_cRef); if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } /*************************************************************************** Name : CreateInstance Purpose : Standard COM class factory entry point which creates the object that this class factory knows to create Parameters: Returns : HRESULT Comment : ***************************************************************************/ HRESULT CClassFactory::CreateInstance ( IUnknown *punkOuter, REFIID riid, void **ppv) { DEBUGMSG(ZONE_VERBOSE,("CClassFactory::CreateInstance\n")); return (m_pfnCreate)(punkOuter, riid, ppv); } /*************************************************************************** Name : LockServer Purpose : Standard COM class factory entry point which will prevent the server from shutting down. Necessary when the caller keeps the class factory (through CoGetClassObject) instead of calling CoCreateInstance. Parameters: Returns : HRESULT Comment : ***************************************************************************/ HRESULT CClassFactory::LockServer (BOOL flock) { if (flock) ++g_cObjects; else --g_cObjects; return NOERROR; } /*************************************************************************** Helper functions ***************************************************************************/ /*************************************************************************** Name : StringFromGuid Purpose : Creates a string out of a GUID Parameters: riid - [in] clsid to make string out of. pszBuf - [in] buffer in which to place resultant GUID Returns : int - number of chars written out Comment : ***************************************************************************/ int StringFromGuid(const CLSID *priid, LPTSTR pszBuf) { return wsprintf(pszBuf, TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"), priid->Data1, priid->Data2, priid->Data3, priid->Data4[0], priid->Data4[1], priid->Data4[2], priid->Data4[3], priid->Data4[4], priid->Data4[5], priid->Data4[6], priid->Data4[7]); } /*************************************************************************** Name : RegisterUnknownObject Purpose : Registers a simple CoCreatable object We add the following information to the registry: HKEY_CLASSES_ROOT\CLSID\ = Object HKEY_CLASSES_ROOT\CLSID\\InprocServer32 = HKEY_CLASSES_ROOT\CLSID\\InprocServer32 @ThreadingModel = Apartment Parameters: pszObjectName - [in] Object Name prclsid - [in] pointer to the CLSID of the object Returns : BOOL - FALSE means couldn't register it all Comment : ***************************************************************************/ BOOL RegisterUnknownObject(LPCTSTR pszObjectName, const CLSID *prclsid) { HKEY hk = NULL, hkSub = NULL; TCHAR szGuidStr[GUID_STR_LEN]; DWORD dwPathLen, dwDummy; TCHAR szScratch[MAX_PATH]; BOOL bRet = FALSE; long l; // clean out any garbage UnregisterUnknownObject(prclsid); if (!StringFromGuid(prclsid, szGuidStr)) goto out; // CLSID/ wsprintf(szScratch, TEXT("CLSID\\%s"), szGuidStr); l = RegCreateKeyEx(HKEY_CLASSES_ROOT, szScratch, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hk, &dwDummy); if (l != ERROR_SUCCESS) goto out; // CLSID/: class name wsprintf(szScratch, TEXT("%s Object"), pszObjectName); l = RegSetValueEx(hk, NULL, 0, REG_SZ, (BYTE *)szScratch, (lstrlen(szScratch) + 1)*sizeof(TCHAR)); if (l != ERROR_SUCCESS) goto out; // CLSID//InprocServer32 l = RegCreateKeyEx(hk, TEXT("InprocServer32"), 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkSub, &dwDummy); if (l != ERROR_SUCCESS) goto out; // CLSID//InprocServer32: dwPathLen = GetModuleFileName(g_hInst, szScratch, sizeof(szScratch)/sizeof(TCHAR)); if (!dwPathLen) goto out; l = RegSetValueEx(hkSub, NULL, 0, REG_SZ, (BYTE *)szScratch, (dwPathLen + 1)*sizeof(TCHAR)); if (l != ERROR_SUCCESS) goto out; // CLSID//InprocServer32: ThreadingModel = Apartment l = RegSetValueEx(hkSub, TEXT("ThreadingModel"), 0, REG_SZ, (BYTE *)TEXT("Apartment"), sizeof(TEXT("Apartment"))); if (l != ERROR_SUCCESS) goto out; bRet = TRUE; out: // clean the keys if we failed somewhere if (!bRet) UnregisterUnknownObject(prclsid); if (hk) RegCloseKey(hk); if (hkSub) RegCloseKey(hkSub); return bRet; } /*************************************************************************** Name : UnregisterUnknownObject Purpose : cleans up all the stuff that RegisterUnknownObject puts in the registry. Parameters: prclsid - [in] pointer to the CLSID of the object Returns : BOOL - FALSE means couldn't register it all Comment : ***************************************************************************/ BOOL UnregisterUnknownObject(const CLSID *prclsid) { TCHAR szScratch[MAX_PATH]; HKEY hk=NULL; BOOL f; long l; BOOL bRet = FALSE; // delete everybody of the form // HKEY_CLASSES_ROOT\CLSID\ [\] * // if (!StringFromGuid(prclsid, szScratch)) goto out; l = RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("CLSID"), 0, KEY_ALL_ACCESS, &hk); if (l != ERROR_SUCCESS) goto out; // Delete the object key and subkeys bRet = DeleteKeyAndSubKeys(hk, szScratch); out: if (hk) RegCloseKey(hk); return bRet; } /*************************************************************************** Name : DeleteKeyAndSubKeys Purpose : delete's a key and all of it's subkeys. Parameters: hkIn - [in] delete the descendant specified pszSubKey - [in] i'm the descendant specified Returns : BOOL - TRUE = OK Comment : Despite the win32 docs claiming it does, RegDeleteKey doesn't seem to work with sub-keys under windows 95. This function is recursive. ***************************************************************************/ BOOL DeleteKeyAndSubKeys(HKEY hkIn, LPTSTR pszSubKey) { HKEY hk; TCHAR szTmp[MAX_PATH]; DWORD dwTmpSize; long l; BOOL f; int x; l = RegOpenKeyEx(hkIn, pszSubKey, 0, KEY_ALL_ACCESS, &hk); if (l != ERROR_SUCCESS) return FALSE; // loop through all subkeys, blowing them away. // f = TRUE; x = 0; while (f) { dwTmpSize = MAX_PATH; l = RegEnumKeyEx(hk, x, szTmp, &dwTmpSize, 0, NULL, NULL, NULL); if (l != ERROR_SUCCESS) break; f = DeleteKeyAndSubKeys(hk, szTmp); x++; } // there are no subkeys left, [or we'll just generate an error and return FALSE]. // let's go blow this dude away. // RegCloseKey(hk); l = RegDeleteKey(hkIn, pszSubKey); return (l == ERROR_SUCCESS) ? TRUE : FALSE; }