512 lines
13 KiB
C++
512 lines
13 KiB
C++
#include "common.hpp"
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* privcom.c
|
|
*
|
|
* Copyright (c) 2000 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Functions that sort-of duplicate what OLE does.
|
|
*
|
|
* Adapted from dinput\dx8\dll\dioledup.c
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
typedef LPUNKNOWN PUNK;
|
|
typedef LPVOID PV, *PPV;
|
|
typedef CONST VOID *PCV;
|
|
typedef REFIID RIID;
|
|
typedef CONST GUID *PCGUID;
|
|
|
|
/*
|
|
* Convert an object (X) to a count of bytes (cb).
|
|
*/
|
|
#define cbX(X) sizeof(X)
|
|
|
|
/*
|
|
* Convert an array name (A) to a generic count (c).
|
|
*/
|
|
#define cA(a) (cbX(a)/cbX(a[0]))
|
|
|
|
/*
|
|
* Convert a count of X's (cx) into a count of bytes
|
|
* and vice versa.
|
|
*/
|
|
#define cbCxX(cx, X) ((cx) * cbX(X))
|
|
#define cxCbX(cb, X) ((cb) / cbX(X))
|
|
|
|
/*
|
|
* Convert a count of chars (cch), tchars (ctch), wchars (cwch),
|
|
* or dwords (cdw) into a count of bytes, and vice versa.
|
|
*/
|
|
#define cbCch(cch) cbCxX( cch, CHAR)
|
|
#define cbCwch(cwch) cbCxX(cwch, WCHAR)
|
|
#define cbCtch(ctch) cbCxX(ctch, TCHAR)
|
|
#define cbCdw(cdw) cbCxX( cdw, DWORD)
|
|
|
|
#define cchCb(cb) cxCbX(cb, CHAR)
|
|
#define cwchCb(cb) cxCbX(cb, WCHAR)
|
|
#define ctchCb(cb) cxCbX(cb, TCHAR)
|
|
#define cdwCb(cb) cxCbX(cb, DWORD)
|
|
|
|
// yay
|
|
#define ctchGuid (1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* _ParseHex
|
|
*
|
|
* Parse a hex string encoding cb bytes (at most 4), then
|
|
* expect the tchDelim to appear afterwards. If chDelim is 0,
|
|
* then no delimiter is expected.
|
|
*
|
|
* Store the result into the indicated LPBYTE (using only the
|
|
* size requested), updating it, and return a pointer to the
|
|
* next unparsed character, or 0 on error.
|
|
*
|
|
* If the incoming pointer is also 0, then return 0 immediately.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LPCTSTR
|
|
_ParseHex(LPCTSTR ptsz, LPBYTE *ppb, int cb, TCHAR tchDelim)
|
|
{
|
|
if(ptsz)
|
|
{
|
|
int i = cb * 2;
|
|
DWORD dwParse = 0;
|
|
|
|
do
|
|
{
|
|
DWORD uch;
|
|
uch = (TBYTE)*ptsz - TEXT('0');
|
|
if(uch < 10)
|
|
{ /* a decimal digit */
|
|
} else
|
|
{
|
|
uch = (*ptsz | 0x20) - TEXT('a');
|
|
if(uch < 6)
|
|
{ /* a hex digit */
|
|
uch += 10;
|
|
} else
|
|
{
|
|
return 0; /* Parse error */
|
|
}
|
|
}
|
|
dwParse = (dwParse << 4) + uch;
|
|
ptsz++;
|
|
} while(--i);
|
|
|
|
if(tchDelim && *ptsz++ != tchDelim) return 0; /* Parse error */
|
|
|
|
for(i = 0; i < cb; i++)
|
|
{
|
|
(*ppb)[i] = ((LPBYTE)&dwParse)[i];
|
|
}
|
|
*ppb += cb;
|
|
}
|
|
return ptsz;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func BOOL | ParseGUID |
|
|
*
|
|
* Take a string and convert it into a GUID, return success/failure.
|
|
*
|
|
* @parm OUT LPGUID | lpGUID |
|
|
*
|
|
* Receives the parsed GUID on success.
|
|
*
|
|
* @parm IN LPCTSTR | ptsz |
|
|
*
|
|
* The string to parse. The format is
|
|
*
|
|
* { <lt>dword<gt> - <lt>word<gt> - <lt>word<gt>
|
|
* - <lt>byte<gt> <lt>byte<gt>
|
|
* - <lt>byte<gt> <lt>byte<gt> <lt>byte<gt>
|
|
* <lt>byte<gt> <lt>byte<gt> <lt>byte<gt> }
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns zero if <p ptszGUID> is not a valid GUID.
|
|
*
|
|
*
|
|
* @comm
|
|
*
|
|
* Stolen from TweakUI.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL
|
|
ParseGUID(LPGUID pguid, LPCTSTR ptsz)
|
|
{
|
|
if(lstrlen(ptsz) == ctchGuid - 1 && *ptsz == TEXT('{'))
|
|
{
|
|
ptsz++;
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 4, TEXT('-'));
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-'));
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-'));
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('-'));
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
|
|
ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('}'));
|
|
return (BOOL)(UINT_PTR)ptsz;
|
|
} else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func LONG | RegQueryString |
|
|
*
|
|
* Wrapper for <f RegQueryValueEx> that reads a
|
|
* string value from the registry. An annoying quirk
|
|
* is that on Windows NT, the returned string might
|
|
* not end in a null terminator, so we might need to add
|
|
* one manually.
|
|
*
|
|
* @parm IN HKEY | hk |
|
|
*
|
|
* Parent registry key.
|
|
*
|
|
* @parm LPCTSTR | ptszValue |
|
|
*
|
|
* Value name.
|
|
*
|
|
* @parm LPTSTR | ptsz |
|
|
*
|
|
* Output buffer.
|
|
*
|
|
* @parm DWORD | ctchBuf |
|
|
*
|
|
* Size of output buffer.
|
|
*
|
|
* @returns
|
|
*
|
|
* Registry error code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LONG
|
|
RegQueryString(HKEY hk, LPCTSTR ptszValue, LPTSTR ptszBuf, DWORD ctchBuf)
|
|
{
|
|
LONG lRc;
|
|
DWORD reg;
|
|
|
|
#ifdef UNICODE
|
|
DWORD cb;
|
|
|
|
/*
|
|
* NT quirk: Non-null terminated strings can exist.
|
|
*/
|
|
cb = cbCtch(ctchBuf);
|
|
lRc = RegQueryValueEx(hk, ptszValue, 0, ®, (LPBYTE)(PV)ptszBuf, &cb);
|
|
if(lRc == ERROR_SUCCESS)
|
|
{
|
|
if(reg == REG_SZ)
|
|
{
|
|
/*
|
|
* Check the last character. If it is not NULL, then
|
|
* append a NULL if there is room.
|
|
*/
|
|
DWORD ctch = ctchCb(cb);
|
|
if(ctch == 0)
|
|
{
|
|
ptszBuf[ctch] = TEXT('\0');
|
|
} else if(ptszBuf[ctch-1] != TEXT('\0'))
|
|
{
|
|
if(ctch < ctchBuf)
|
|
{
|
|
ptszBuf[ctch] = TEXT('\0');
|
|
} else
|
|
{
|
|
lRc = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
lRc = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
/*
|
|
* This code is executed only on Win95, so we don't have to worry
|
|
* about the NT quirk.
|
|
*/
|
|
|
|
lRc = RegQueryValueEx(hk, ptszValue, 0, ®, (LPBYTE)(PV)ptszBuf, &ctchBuf);
|
|
|
|
if(lRc == ERROR_SUCCESS && reg != REG_SZ)
|
|
{
|
|
lRc = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
return lRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | _CreateInstance |
|
|
*
|
|
* Worker function for <f DICoCreateInstance>.
|
|
*
|
|
* @parm REFCLSID | rclsid |
|
|
*
|
|
* The <t CLSID> to create.
|
|
*
|
|
* @parm LPCTSTR | ptszDll |
|
|
*
|
|
* The name of the DLL to load.
|
|
*
|
|
* @parm LPUNKNOWN | punkOuter |
|
|
*
|
|
* Controlling unknown for aggregation.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* Interface to obtain.
|
|
*
|
|
* @parm PPV | ppvOut |
|
|
*
|
|
* Receives a pointer to the created object if successful.
|
|
*
|
|
* @parm HINSTANCE * | phinst |
|
|
*
|
|
* Receives the instance handle of the in-proc DLL that was
|
|
* loaded. <f FreeLibrary> this DLL when you are finished
|
|
* with the object.
|
|
*
|
|
* Note that since we don't implement a binder, this means
|
|
* that you cannot give the returned pointer away to anybody
|
|
* you don't control; otherwise, you won't know when to
|
|
* free the DLL.
|
|
*
|
|
* @returns
|
|
*
|
|
* Standard OLE status code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT
|
|
_CreateInprocObject(BOOL bInstance, REFCLSID rclsid, LPCTSTR ptszDll, LPUNKNOWN punkOuter,
|
|
REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
|
|
{
|
|
HRESULT hres;
|
|
HINSTANCE hinst;
|
|
|
|
hinst = LoadLibrary(ptszDll);
|
|
if (hinst) {
|
|
LPFNGETCLASSOBJECT DllGetClassObject;
|
|
|
|
DllGetClassObject = (LPFNGETCLASSOBJECT)
|
|
GetProcAddress(hinst, "DllGetClassObject");
|
|
|
|
if (DllGetClassObject) {
|
|
IClassFactory *pcf;
|
|
|
|
if (bInstance)
|
|
hres = DllGetClassObject(rclsid, IID_IClassFactory, (LPVOID *)&pcf);
|
|
else
|
|
{
|
|
hres = DllGetClassObject(rclsid, riid, ppvOut);
|
|
if (FAILED(hres))
|
|
*ppvOut = NULL;
|
|
}
|
|
if (SUCCEEDED(hres) && bInstance) {
|
|
hres = pcf->CreateInstance(punkOuter, riid, ppvOut);
|
|
pcf->Release();
|
|
|
|
/*
|
|
* People forget to adhere to
|
|
* the OLE spec, which requires that *ppvOut be
|
|
* set to zero on failure.
|
|
*/
|
|
if (FAILED(hres)) {
|
|
/* if (*ppvOut) {
|
|
RPF("ERROR! CoCreateInstance: %s forgot to zero "
|
|
"out *ppvOut on failure path", ptszDll);
|
|
}*/
|
|
*ppvOut = 0;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
/*
|
|
* DLL does not export GetClassObject.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
*phinst = hinst;
|
|
} else {
|
|
FreeLibrary(hinst);
|
|
}
|
|
} else {
|
|
/*
|
|
* DLL does not exist.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | DICoCreateInstance |
|
|
*
|
|
* Private version of CoCreateInstance that doesn't use OLE.
|
|
*
|
|
* @parm LPTSTR | ptszClsid |
|
|
*
|
|
* The string version of the <t CLSID> to create.
|
|
*
|
|
* @parm LPUNKNOWN | punkOuter |
|
|
*
|
|
* Controlling unknown for aggregation.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* Interface to obtain.
|
|
*
|
|
* @parm PPV | ppvOut |
|
|
*
|
|
* Receives a pointer to the created object if successful.
|
|
*
|
|
* @parm HINSTANCE * | phinst |
|
|
*
|
|
* Receives the instance handle of the in-proc DLL that was
|
|
* loaded. <f FreeLibrary> this DLL when you are finished
|
|
* with the object.
|
|
*
|
|
* Note that since we don't implement a binder, this means
|
|
* that you cannot give the returned pointer away to anybody
|
|
* you don't control; otherwise, you won't know when to
|
|
* free the DLL.
|
|
*
|
|
* @returns
|
|
*
|
|
* Standard OLE status code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CreateInprocObject(BOOL bInstance, LPCTSTR ptszClsid, LPUNKNOWN punkOuter,
|
|
REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
|
|
{
|
|
HRESULT hres;
|
|
CLSID clsid;
|
|
|
|
*ppvOut = 0;
|
|
*phinst = 0;
|
|
|
|
if (ParseGUID(&clsid, ptszClsid)) {
|
|
HKEY hk;
|
|
LONG lRc;
|
|
TCHAR tszKey[ctchGuid + 40]; /* 40 is more than enough */
|
|
|
|
/*
|
|
* Look up the CLSID in HKEY_CLASSES_ROOT.
|
|
*/
|
|
wsprintf(tszKey, TEXT("CLSID\\%s\\InProcServer32"), ptszClsid);
|
|
|
|
lRc = RegOpenKeyEx(HKEY_CLASSES_ROOT, tszKey, 0,
|
|
KEY_QUERY_VALUE, &hk);
|
|
if (lRc == ERROR_SUCCESS) {
|
|
TCHAR tszDll[MAX_PATH];
|
|
DWORD cb;
|
|
|
|
cb = cbX(tszDll);
|
|
lRc = RegQueryValue(hk, 0, tszDll, (PLONG)&cb);
|
|
|
|
if (lRc == ERROR_SUCCESS) {
|
|
TCHAR tszModel[20]; /* more than enough */
|
|
|
|
lRc = RegQueryString(hk, TEXT("ThreadingModel"),
|
|
tszModel, cA(tszModel));
|
|
if (lRc == ERROR_SUCCESS &&
|
|
((lstrcmpi(tszModel, TEXT("Both"))==0x0) ||
|
|
(lstrcmpi(tszModel, TEXT("Free"))==0x0))) {
|
|
|
|
hres = _CreateInprocObject(bInstance, clsid, tszDll, punkOuter,
|
|
riid, ppvOut, phinst);
|
|
|
|
} else {
|
|
/*
|
|
* No threading model or bad threading model.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
} else {
|
|
/*
|
|
* No InprocServer32.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
|
|
} else {
|
|
/*
|
|
* CLSID not registered.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
} else {
|
|
/*
|
|
* Invalid CLSID string.
|
|
*/
|
|
hres = REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PrivCreateInstance(REFCLSID ptszClsid, LPUNKNOWN punkOuter, DWORD dwClsContext,
|
|
REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
|
|
{
|
|
if (dwClsContext != CLSCTX_INPROC_SERVER || phinst == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return CreateInprocObject(TRUE, GUIDSTR(ptszClsid), punkOuter, riid, ppvOut, phinst);
|
|
}
|
|
|
|
HRESULT
|
|
PrivGetClassObject(REFCLSID ptszClsid, DWORD dwClsContext, LPVOID pReserved,
|
|
REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
|
|
{
|
|
if (dwClsContext != CLSCTX_INPROC_SERVER || pReserved != NULL || phinst == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return CreateInprocObject(FALSE, GUIDSTR(ptszClsid), NULL, riid, ppvOut, phinst);
|
|
}
|