Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

511 lines
13 KiB

  1. #include "common.hpp"
  2. /*****************************************************************************
  3. *
  4. * privcom.c
  5. *
  6. * Copyright (c) 2000 Microsoft Corporation. All Rights Reserved.
  7. *
  8. * Abstract:
  9. *
  10. * Functions that sort-of duplicate what OLE does.
  11. *
  12. * Adapted from dinput\dx8\dll\dioledup.c
  13. *
  14. *****************************************************************************/
  15. typedef LPUNKNOWN PUNK;
  16. typedef LPVOID PV, *PPV;
  17. typedef CONST VOID *PCV;
  18. typedef REFIID RIID;
  19. typedef CONST GUID *PCGUID;
  20. /*
  21. * Convert an object (X) to a count of bytes (cb).
  22. */
  23. #define cbX(X) sizeof(X)
  24. /*
  25. * Convert an array name (A) to a generic count (c).
  26. */
  27. #define cA(a) (cbX(a)/cbX(a[0]))
  28. /*
  29. * Convert a count of X's (cx) into a count of bytes
  30. * and vice versa.
  31. */
  32. #define cbCxX(cx, X) ((cx) * cbX(X))
  33. #define cxCbX(cb, X) ((cb) / cbX(X))
  34. /*
  35. * Convert a count of chars (cch), tchars (ctch), wchars (cwch),
  36. * or dwords (cdw) into a count of bytes, and vice versa.
  37. */
  38. #define cbCch(cch) cbCxX( cch, CHAR)
  39. #define cbCwch(cwch) cbCxX(cwch, WCHAR)
  40. #define cbCtch(ctch) cbCxX(ctch, TCHAR)
  41. #define cbCdw(cdw) cbCxX( cdw, DWORD)
  42. #define cchCb(cb) cxCbX(cb, CHAR)
  43. #define cwchCb(cb) cxCbX(cb, WCHAR)
  44. #define ctchCb(cb) cxCbX(cb, TCHAR)
  45. #define cdwCb(cb) cxCbX(cb, DWORD)
  46. // yay
  47. #define ctchGuid (1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)
  48. /*****************************************************************************
  49. *
  50. * _ParseHex
  51. *
  52. * Parse a hex string encoding cb bytes (at most 4), then
  53. * expect the tchDelim to appear afterwards. If chDelim is 0,
  54. * then no delimiter is expected.
  55. *
  56. * Store the result into the indicated LPBYTE (using only the
  57. * size requested), updating it, and return a pointer to the
  58. * next unparsed character, or 0 on error.
  59. *
  60. * If the incoming pointer is also 0, then return 0 immediately.
  61. *
  62. *****************************************************************************/
  63. LPCTSTR
  64. _ParseHex(LPCTSTR ptsz, LPBYTE *ppb, int cb, TCHAR tchDelim)
  65. {
  66. if(ptsz)
  67. {
  68. int i = cb * 2;
  69. DWORD dwParse = 0;
  70. do
  71. {
  72. DWORD uch;
  73. uch = (TBYTE)*ptsz - TEXT('0');
  74. if(uch < 10)
  75. { /* a decimal digit */
  76. } else
  77. {
  78. uch = (*ptsz | 0x20) - TEXT('a');
  79. if(uch < 6)
  80. { /* a hex digit */
  81. uch += 10;
  82. } else
  83. {
  84. return 0; /* Parse error */
  85. }
  86. }
  87. dwParse = (dwParse << 4) + uch;
  88. ptsz++;
  89. } while(--i);
  90. if(tchDelim && *ptsz++ != tchDelim) return 0; /* Parse error */
  91. for(i = 0; i < cb; i++)
  92. {
  93. (*ppb)[i] = ((LPBYTE)&dwParse)[i];
  94. }
  95. *ppb += cb;
  96. }
  97. return ptsz;
  98. }
  99. /*****************************************************************************
  100. *
  101. * @doc INTERNAL
  102. *
  103. * @func BOOL | ParseGUID |
  104. *
  105. * Take a string and convert it into a GUID, return success/failure.
  106. *
  107. * @parm OUT LPGUID | lpGUID |
  108. *
  109. * Receives the parsed GUID on success.
  110. *
  111. * @parm IN LPCTSTR | ptsz |
  112. *
  113. * The string to parse. The format is
  114. *
  115. * { <lt>dword<gt> - <lt>word<gt> - <lt>word<gt>
  116. * - <lt>byte<gt> <lt>byte<gt>
  117. * - <lt>byte<gt> <lt>byte<gt> <lt>byte<gt>
  118. * <lt>byte<gt> <lt>byte<gt> <lt>byte<gt> }
  119. *
  120. * @returns
  121. *
  122. * Returns zero if <p ptszGUID> is not a valid GUID.
  123. *
  124. *
  125. * @comm
  126. *
  127. * Stolen from TweakUI.
  128. *
  129. *****************************************************************************/
  130. BOOL
  131. ParseGUID(LPGUID pguid, LPCTSTR ptsz)
  132. {
  133. if(lstrlen(ptsz) == ctchGuid - 1 && *ptsz == TEXT('{'))
  134. {
  135. ptsz++;
  136. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 4, TEXT('-'));
  137. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-'));
  138. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-'));
  139. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  140. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('-'));
  141. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  142. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  143. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  144. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  145. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 );
  146. ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('}'));
  147. return (BOOL)(UINT_PTR)ptsz;
  148. } else
  149. {
  150. return 0;
  151. }
  152. }
  153. /*****************************************************************************
  154. *
  155. * @doc INTERNAL
  156. *
  157. * @func LONG | RegQueryString |
  158. *
  159. * Wrapper for <f RegQueryValueEx> that reads a
  160. * string value from the registry. An annoying quirk
  161. * is that on Windows NT, the returned string might
  162. * not end in a null terminator, so we might need to add
  163. * one manually.
  164. *
  165. * @parm IN HKEY | hk |
  166. *
  167. * Parent registry key.
  168. *
  169. * @parm LPCTSTR | ptszValue |
  170. *
  171. * Value name.
  172. *
  173. * @parm LPTSTR | ptsz |
  174. *
  175. * Output buffer.
  176. *
  177. * @parm DWORD | ctchBuf |
  178. *
  179. * Size of output buffer.
  180. *
  181. * @returns
  182. *
  183. * Registry error code.
  184. *
  185. *****************************************************************************/
  186. LONG
  187. RegQueryString(HKEY hk, LPCTSTR ptszValue, LPTSTR ptszBuf, DWORD ctchBuf)
  188. {
  189. LONG lRc;
  190. DWORD reg;
  191. #ifdef UNICODE
  192. DWORD cb;
  193. /*
  194. * NT quirk: Non-null terminated strings can exist.
  195. */
  196. cb = cbCtch(ctchBuf);
  197. lRc = RegQueryValueEx(hk, ptszValue, 0, &reg, (LPBYTE)(PV)ptszBuf, &cb);
  198. if(lRc == ERROR_SUCCESS)
  199. {
  200. if(reg == REG_SZ)
  201. {
  202. /*
  203. * Check the last character. If it is not NULL, then
  204. * append a NULL if there is room.
  205. */
  206. DWORD ctch = ctchCb(cb);
  207. if(ctch == 0)
  208. {
  209. ptszBuf[ctch] = TEXT('\0');
  210. } else if(ptszBuf[ctch-1] != TEXT('\0'))
  211. {
  212. if(ctch < ctchBuf)
  213. {
  214. ptszBuf[ctch] = TEXT('\0');
  215. } else
  216. {
  217. lRc = ERROR_MORE_DATA;
  218. }
  219. }
  220. } else
  221. {
  222. lRc = ERROR_INVALID_DATA;
  223. }
  224. }
  225. #else
  226. /*
  227. * This code is executed only on Win95, so we don't have to worry
  228. * about the NT quirk.
  229. */
  230. lRc = RegQueryValueEx(hk, ptszValue, 0, &reg, (LPBYTE)(PV)ptszBuf, &ctchBuf);
  231. if(lRc == ERROR_SUCCESS && reg != REG_SZ)
  232. {
  233. lRc = ERROR_INVALID_DATA;
  234. }
  235. #endif
  236. return lRc;
  237. }
  238. /*****************************************************************************
  239. *
  240. * @doc INTERNAL
  241. *
  242. * @func HRESULT | _CreateInstance |
  243. *
  244. * Worker function for <f DICoCreateInstance>.
  245. *
  246. * @parm REFCLSID | rclsid |
  247. *
  248. * The <t CLSID> to create.
  249. *
  250. * @parm LPCTSTR | ptszDll |
  251. *
  252. * The name of the DLL to load.
  253. *
  254. * @parm LPUNKNOWN | punkOuter |
  255. *
  256. * Controlling unknown for aggregation.
  257. *
  258. * @parm RIID | riid |
  259. *
  260. * Interface to obtain.
  261. *
  262. * @parm PPV | ppvOut |
  263. *
  264. * Receives a pointer to the created object if successful.
  265. *
  266. * @parm HINSTANCE * | phinst |
  267. *
  268. * Receives the instance handle of the in-proc DLL that was
  269. * loaded. <f FreeLibrary> this DLL when you are finished
  270. * with the object.
  271. *
  272. * Note that since we don't implement a binder, this means
  273. * that you cannot give the returned pointer away to anybody
  274. * you don't control; otherwise, you won't know when to
  275. * free the DLL.
  276. *
  277. * @returns
  278. *
  279. * Standard OLE status code.
  280. *
  281. *****************************************************************************/
  282. HRESULT
  283. _CreateInprocObject(BOOL bInstance, REFCLSID rclsid, LPCTSTR ptszDll, LPUNKNOWN punkOuter,
  284. REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
  285. {
  286. HRESULT hres;
  287. HINSTANCE hinst;
  288. hinst = LoadLibrary(ptszDll);
  289. if (hinst) {
  290. LPFNGETCLASSOBJECT DllGetClassObject;
  291. DllGetClassObject = (LPFNGETCLASSOBJECT)
  292. GetProcAddress(hinst, "DllGetClassObject");
  293. if (DllGetClassObject) {
  294. IClassFactory *pcf;
  295. if (bInstance)
  296. hres = DllGetClassObject(rclsid, IID_IClassFactory, (LPVOID *)&pcf);
  297. else
  298. {
  299. hres = DllGetClassObject(rclsid, riid, ppvOut);
  300. if (FAILED(hres))
  301. *ppvOut = NULL;
  302. }
  303. if (SUCCEEDED(hres) && bInstance) {
  304. hres = pcf->CreateInstance(punkOuter, riid, ppvOut);
  305. pcf->Release();
  306. /*
  307. * People forget to adhere to
  308. * the OLE spec, which requires that *ppvOut be
  309. * set to zero on failure.
  310. */
  311. if (FAILED(hres)) {
  312. /* if (*ppvOut) {
  313. RPF("ERROR! CoCreateInstance: %s forgot to zero "
  314. "out *ppvOut on failure path", ptszDll);
  315. }*/
  316. *ppvOut = 0;
  317. }
  318. }
  319. } else {
  320. /*
  321. * DLL does not export GetClassObject.
  322. */
  323. hres = REGDB_E_CLASSNOTREG;
  324. }
  325. if (SUCCEEDED(hres)) {
  326. *phinst = hinst;
  327. } else {
  328. FreeLibrary(hinst);
  329. }
  330. } else {
  331. /*
  332. * DLL does not exist.
  333. */
  334. hres = REGDB_E_CLASSNOTREG;
  335. }
  336. return hres;
  337. }
  338. /*****************************************************************************
  339. *
  340. * @doc INTERNAL
  341. *
  342. * @func HRESULT | DICoCreateInstance |
  343. *
  344. * Private version of CoCreateInstance that doesn't use OLE.
  345. *
  346. * @parm LPTSTR | ptszClsid |
  347. *
  348. * The string version of the <t CLSID> to create.
  349. *
  350. * @parm LPUNKNOWN | punkOuter |
  351. *
  352. * Controlling unknown for aggregation.
  353. *
  354. * @parm RIID | riid |
  355. *
  356. * Interface to obtain.
  357. *
  358. * @parm PPV | ppvOut |
  359. *
  360. * Receives a pointer to the created object if successful.
  361. *
  362. * @parm HINSTANCE * | phinst |
  363. *
  364. * Receives the instance handle of the in-proc DLL that was
  365. * loaded. <f FreeLibrary> this DLL when you are finished
  366. * with the object.
  367. *
  368. * Note that since we don't implement a binder, this means
  369. * that you cannot give the returned pointer away to anybody
  370. * you don't control; otherwise, you won't know when to
  371. * free the DLL.
  372. *
  373. * @returns
  374. *
  375. * Standard OLE status code.
  376. *
  377. *****************************************************************************/
  378. STDMETHODIMP
  379. CreateInprocObject(BOOL bInstance, LPCTSTR ptszClsid, LPUNKNOWN punkOuter,
  380. REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
  381. {
  382. HRESULT hres;
  383. CLSID clsid;
  384. *ppvOut = 0;
  385. *phinst = 0;
  386. if (ParseGUID(&clsid, ptszClsid)) {
  387. HKEY hk;
  388. LONG lRc;
  389. TCHAR tszKey[ctchGuid + 40]; /* 40 is more than enough */
  390. /*
  391. * Look up the CLSID in HKEY_CLASSES_ROOT.
  392. */
  393. wsprintf(tszKey, TEXT("CLSID\\%s\\InProcServer32"), ptszClsid);
  394. lRc = RegOpenKeyEx(HKEY_CLASSES_ROOT, tszKey, 0,
  395. KEY_QUERY_VALUE, &hk);
  396. if (lRc == ERROR_SUCCESS) {
  397. TCHAR tszDll[MAX_PATH];
  398. DWORD cb;
  399. cb = cbX(tszDll);
  400. lRc = RegQueryValue(hk, 0, tszDll, (PLONG)&cb);
  401. if (lRc == ERROR_SUCCESS) {
  402. TCHAR tszModel[20]; /* more than enough */
  403. lRc = RegQueryString(hk, TEXT("ThreadingModel"),
  404. tszModel, cA(tszModel));
  405. if (lRc == ERROR_SUCCESS &&
  406. ((lstrcmpi(tszModel, TEXT("Both"))==0x0) ||
  407. (lstrcmpi(tszModel, TEXT("Free"))==0x0))) {
  408. hres = _CreateInprocObject(bInstance, clsid, tszDll, punkOuter,
  409. riid, ppvOut, phinst);
  410. } else {
  411. /*
  412. * No threading model or bad threading model.
  413. */
  414. hres = REGDB_E_CLASSNOTREG;
  415. }
  416. } else {
  417. /*
  418. * No InprocServer32.
  419. */
  420. hres = REGDB_E_CLASSNOTREG;
  421. }
  422. RegCloseKey(hk);
  423. } else {
  424. /*
  425. * CLSID not registered.
  426. */
  427. hres = REGDB_E_CLASSNOTREG;
  428. }
  429. } else {
  430. /*
  431. * Invalid CLSID string.
  432. */
  433. hres = REGDB_E_CLASSNOTREG;
  434. }
  435. return hres;
  436. }
  437. HRESULT
  438. PrivCreateInstance(REFCLSID ptszClsid, LPUNKNOWN punkOuter, DWORD dwClsContext,
  439. REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
  440. {
  441. if (dwClsContext != CLSCTX_INPROC_SERVER || phinst == NULL)
  442. return E_INVALIDARG;
  443. return CreateInprocObject(TRUE, GUIDSTR(ptszClsid), punkOuter, riid, ppvOut, phinst);
  444. }
  445. HRESULT
  446. PrivGetClassObject(REFCLSID ptszClsid, DWORD dwClsContext, LPVOID pReserved,
  447. REFIID riid, LPVOID *ppvOut, HINSTANCE *phinst)
  448. {
  449. if (dwClsContext != CLSCTX_INPROC_SERVER || pReserved != NULL || phinst == NULL)
  450. return E_INVALIDARG;
  451. return CreateInprocObject(FALSE, GUIDSTR(ptszClsid), NULL, riid, ppvOut, phinst);
  452. }