540 lines
13 KiB
C++
540 lines
13 KiB
C++
/*
|
|
- 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 <precomp.h>
|
|
|
|
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\<CLSID> = <ObjectName> Object
|
|
HKEY_CLASSES_ROOT\CLSID\<CLSID>\InprocServer32 = <path to local server>
|
|
HKEY_CLASSES_ROOT\CLSID\<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/<class-id>
|
|
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-id>: 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/<class-id>/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/<class-id>/InprocServer32:<file name>
|
|
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/<class-id>/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\<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;
|
|
}
|