#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 * * { dword - word - word * - byte byte * - byte byte byte * byte byte byte } * * @returns * * Returns zero if

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 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 . * * @parm REFCLSID | rclsid | * * The 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. 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 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. 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); }