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.

1941 lines
52 KiB

  1. /*****************************************************************************\
  2. FILE: util.cpp
  3. DESCRIPTION:
  4. Shared stuff that operates on all classes.
  5. BryanSt 4/4/2000 (Bryan Starbuck)
  6. Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
  7. \*****************************************************************************/
  8. #include "priv.h"
  9. #include <atlbase.h> // USES_CONVERSION
  10. #include "util.h"
  11. #include <comdef.h>
  12. #define DECL_CRTFREE
  13. #include <crtfree.h>
  14. /////////////////////////////////////////////////////////////////////
  15. // String Helpers
  16. /////////////////////////////////////////////////////////////////////
  17. HINSTANCE g_hinst; // My instance handle
  18. HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
  19. #ifdef DEBUG
  20. DWORD g_TLSliStopWatchStartHi = 0;
  21. DWORD g_TLSliStopWatchStartLo = 0;
  22. LARGE_INTEGER g_liStopWatchFreq = {0};
  23. #endif // DEBUG
  24. /////////////////////////////////////////////////////////////////////
  25. // Debug Timing Helpers
  26. /////////////////////////////////////////////////////////////////////
  27. #ifdef DEBUG
  28. void DebugStartWatch(void)
  29. {
  30. LARGE_INTEGER liStopWatchStart;
  31. liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
  32. liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
  33. // ASSERT(!liStopWatchStart.QuadPart); // If you hit this, then the stopwatch is nested.
  34. QueryPerformanceFrequency(&g_liStopWatchFreq);
  35. QueryPerformanceCounter(&liStopWatchStart);
  36. TlsSetValue(g_TLSliStopWatchStartHi, IntToPtr(liStopWatchStart.HighPart));
  37. TlsSetValue(g_TLSliStopWatchStartLo, IntToPtr(liStopWatchStart.LowPart));
  38. }
  39. DWORD DebugStopWatch(void)
  40. {
  41. LARGE_INTEGER liDiff;
  42. LARGE_INTEGER liStopWatchStart;
  43. QueryPerformanceCounter(&liDiff);
  44. liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
  45. liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
  46. liDiff.QuadPart -= liStopWatchStart.QuadPart;
  47. ASSERT(0 != g_liStopWatchFreq.QuadPart); // I don't like to fault with div 0.
  48. DWORD dwTime = (DWORD)((liDiff.QuadPart * 1000) / g_liStopWatchFreq.QuadPart);
  49. TlsSetValue(g_TLSliStopWatchStartHi, (LPVOID) 0);
  50. TlsSetValue(g_TLSliStopWatchStartLo, (LPVOID) 0);
  51. return dwTime;
  52. }
  53. #endif // DEBUG
  54. /////////////////////////////////////////////////////////////////////
  55. // String Helpers
  56. /////////////////////////////////////////////////////////////////////
  57. #undef SysAllocStringA
  58. BSTR SysAllocStringA(LPCSTR pszStr)
  59. {
  60. BSTR bstrOut = NULL;
  61. if (pszStr)
  62. {
  63. DWORD cchSize = (lstrlenA(pszStr) + 1);
  64. LPWSTR pwszThunkTemp = (LPWSTR) LocalAlloc(LPTR, (sizeof(pwszThunkTemp[0]) * cchSize)); // assumes INFOTIPSIZE number of chars max
  65. if (pwszThunkTemp)
  66. {
  67. SHAnsiToUnicode(pszStr, pwszThunkTemp, cchSize);
  68. bstrOut = SysAllocString(pwszThunkTemp);
  69. LocalFree(pwszThunkTemp);
  70. }
  71. }
  72. return bstrOut;
  73. }
  74. HRESULT HrSysAllocStringA(IN LPCSTR pszSource, OUT BSTR * pbstrDest)
  75. {
  76. HRESULT hr = S_OK;
  77. if (pbstrDest)
  78. {
  79. *pbstrDest = SysAllocStringA(pszSource);
  80. if (pszSource)
  81. {
  82. if (*pbstrDest)
  83. hr = S_OK;
  84. else
  85. hr = E_OUTOFMEMORY;
  86. }
  87. }
  88. return hr;
  89. }
  90. HRESULT HrSysAllocStringW(IN const OLECHAR * pwzSource, OUT BSTR * pbstrDest)
  91. {
  92. HRESULT hr = S_OK;
  93. if (pbstrDest)
  94. {
  95. *pbstrDest = SysAllocString(pwzSource);
  96. if (pwzSource)
  97. {
  98. if (*pbstrDest)
  99. hr = S_OK;
  100. else
  101. hr = E_OUTOFMEMORY;
  102. }
  103. }
  104. return hr;
  105. }
  106. LPSTR AllocStringFromBStr(BSTR bstr)
  107. {
  108. USES_CONVERSION; // atlbase.h
  109. char *a = W2A((bstr ? bstr : L""));
  110. int len = 1 + lstrlenA(a);
  111. char *p = (char *)LocalAlloc(LPTR, len);
  112. if (p)
  113. {
  114. StrCpyA(p, a);
  115. }
  116. return p;
  117. }
  118. HRESULT BSTRFromStream(IStream * pStream, BSTR * pbstr)
  119. {
  120. STATSTG statStg = {0};
  121. HRESULT hr = pStream->Stat(&statStg, STATFLAG_NONAME);
  122. if (S_OK == hr)
  123. {
  124. DWORD cchSize = statStg.cbSize.LowPart;
  125. *pbstr = SysAllocStringLen(NULL, cchSize + 4);
  126. if (*pbstr)
  127. {
  128. LPSTR pszTemp = (LPSTR) LocalAlloc(LPTR, sizeof(pszTemp[0]) * (cchSize + 4));
  129. if (pszTemp)
  130. {
  131. ULONG cbRead;
  132. hr = pStream->Read(pszTemp, cchSize, &cbRead);
  133. pszTemp[cchSize] = 0;
  134. SHAnsiToUnicode(pszTemp, *pbstr, (cchSize + 1));
  135. LocalFree(pszTemp);
  136. }
  137. else
  138. hr = E_OUTOFMEMORY;
  139. }
  140. else
  141. hr = E_OUTOFMEMORY;
  142. }
  143. return hr;
  144. }
  145. // --------------------------------------------------------------------------------
  146. // HrCopyStream
  147. // --------------------------------------------------------------------------------
  148. HRESULT HrCopyStream(LPSTREAM pstmIn, LPSTREAM pstmOut, ULONG *pcb)
  149. {
  150. HRESULT hr = S_OK;
  151. BYTE buf[4096];
  152. ULONG cbRead=0,
  153. cbTotal=0;
  154. do
  155. {
  156. hr = pstmIn->Read(buf, sizeof(buf), &cbRead);
  157. if (FAILED(hr) || cbRead == 0)
  158. {
  159. break;
  160. }
  161. hr = pstmOut->Write(buf, cbRead, NULL);
  162. if (FAILED(hr))
  163. {
  164. break;
  165. }
  166. cbTotal += cbRead;
  167. }
  168. while (cbRead == sizeof (buf));
  169. if (pcb && SUCCEEDED(hr))
  170. *pcb = cbTotal;
  171. return hr;
  172. }
  173. HRESULT CreateBStrVariantFromWStr(IN OUT VARIANT * pvar, IN LPCWSTR pwszString)
  174. {
  175. HRESULT hr = E_INVALIDARG;
  176. if (pvar)
  177. {
  178. pvar->bstrVal = SysAllocString(pwszString);
  179. if (pvar->bstrVal)
  180. {
  181. pvar->vt = VT_BSTR;
  182. hr = S_OK;
  183. }
  184. else
  185. {
  186. pvar->vt = VT_EMPTY;
  187. hr = E_OUTOFMEMORY;
  188. }
  189. }
  190. return hr;
  191. }
  192. HRESULT HrSysAllocString(IN const OLECHAR * pwzSource, OUT BSTR * pbstrDest)
  193. {
  194. HRESULT hr = S_OK;
  195. if (pbstrDest)
  196. {
  197. *pbstrDest = SysAllocString(pwzSource);
  198. if (pwzSource)
  199. {
  200. if (*pbstrDest)
  201. hr = S_OK;
  202. else
  203. hr = E_OUTOFMEMORY;
  204. }
  205. }
  206. return hr;
  207. }
  208. HRESULT UnEscapeHTML(BSTR bstrEscaped, BSTR * pbstrUnEscaped)
  209. {
  210. HRESULT hr = HrSysAllocString(bstrEscaped, pbstrUnEscaped);
  211. if (SUCCEEDED(hr))
  212. {
  213. // Find %xx and replace.
  214. LPWSTR pwszEscapedSequence = StrChrW(*pbstrUnEscaped, CH_HTML_ESCAPE);
  215. WCHAR wzEscaped[5] = L"0xXX";
  216. while (pwszEscapedSequence && (3 <= lstrlenW(pwszEscapedSequence)))
  217. {
  218. int nCharCode;
  219. wzEscaped[2] = pwszEscapedSequence[1];
  220. wzEscaped[3] = pwszEscapedSequence[2];
  221. StrToIntExW(wzEscaped, STIF_SUPPORT_HEX, &nCharCode);
  222. // Replace the '%' with the real char.
  223. pwszEscapedSequence[0] = (WCHAR) nCharCode;
  224. pwszEscapedSequence = CharNextW(pwszEscapedSequence); // Skip pasted the replaced char.
  225. // Over write the 0xXX value.
  226. StrCpyW(pwszEscapedSequence, &pwszEscapedSequence[2]);
  227. // Next...
  228. pwszEscapedSequence = StrChrW(pwszEscapedSequence, CH_HTML_ESCAPE);
  229. }
  230. }
  231. return hr;
  232. }
  233. /*****************************************************************************\
  234. PARAMETERS:
  235. If fBoolean is TRUE, return "True" else "False".
  236. HRESULT BOOLToString(BOOL fBoolean, BSTR * pbstrValue)
  237. {
  238. HRESULT hr = E_INVALIDARG;
  239. if (pbstrValue)
  240. {
  241. LPCWSTR pwszValue;
  242. *pbstrValue = NULL;
  243. if (TRUE == fBoolean)
  244. {
  245. pwszValue = SZ_QUERYDATA_TRUE;
  246. }
  247. else
  248. {
  249. pwszValue = SZ_QUERYDATA_FALSE;
  250. }
  251. hr = HrSysAllocString(pwszValue, pbstrValue);
  252. }
  253. return hr;
  254. }
  255. \*****************************************************************************/
  256. #define SZ_VALID_XML L"<?xml"
  257. /////////////////////////////////////////////////////////////////////
  258. // XML Related Helpers
  259. /////////////////////////////////////////////////////////////////////
  260. HRESULT XMLDOMFromBStr(BSTR bstrXML, IXMLDOMDocument ** ppXMLDoc)
  261. {
  262. HRESULT hr = E_FAIL;
  263. // We don't even want to
  264. // bother passing it to the XML DOM because they throw exceptions. These
  265. // are caught and handled but we still don't want this to happen. We try
  266. // to get XML from the web server, but we get HTML instead if the web server
  267. // fails or the web proxy returns HTML if the site isn't found.
  268. if (!StrCmpNIW(SZ_VALID_XML, bstrXML, (ARRAYSIZE(SZ_VALID_XML) - 1)))
  269. {
  270. hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IXMLDOMDocument, ppXMLDoc));
  271. if (SUCCEEDED(hr))
  272. {
  273. VARIANT_BOOL fIsSuccessful;
  274. // NOTE: This will throw an 0xE0000001 exception in MSXML if the XML is invalid.
  275. // This is not good but there isn't much we can do about it. The problem is
  276. // that web proxies give back HTML which fails to parse.
  277. hr = (*ppXMLDoc)->loadXML(bstrXML, &fIsSuccessful);
  278. if (SUCCEEDED(hr))
  279. {
  280. if (VARIANT_TRUE != fIsSuccessful)
  281. {
  282. hr = E_FAIL;
  283. }
  284. }
  285. }
  286. if (FAILED(hr))
  287. {
  288. (*ppXMLDoc)->Release();
  289. *ppXMLDoc = NULL;
  290. }
  291. }
  292. return hr;
  293. }
  294. HRESULT XMLBStrFromDOM(IXMLDOMDocument * pXMLDoc, BSTR * pbstrXML)
  295. {
  296. IStream * pStream;
  297. HRESULT hr = pXMLDoc->QueryInterface(IID_PPV_ARG(IStream, &pStream)); // check the return value
  298. if (S_OK == hr)
  299. {
  300. hr = BSTRFromStream(pStream, pbstrXML);
  301. pStream->Release();
  302. }
  303. return hr;
  304. }
  305. HRESULT XMLAppendElement(IXMLDOMElement * pXMLElementRoot, IXMLDOMElement * pXMLElementToAppend)
  306. {
  307. IXMLDOMNode * pXMLNodeRoot;
  308. HRESULT hr = pXMLElementRoot->QueryInterface(IID_PPV_ARG(IXMLDOMNode, &pXMLNodeRoot));
  309. if (EVAL(SUCCEEDED(hr)))
  310. {
  311. IXMLDOMNode * pXMLNodeToAppend;
  312. hr = pXMLElementToAppend->QueryInterface(IID_PPV_ARG(IXMLDOMNode, &pXMLNodeToAppend));
  313. if (EVAL(SUCCEEDED(hr)))
  314. {
  315. hr = pXMLNodeRoot->appendChild(pXMLNodeToAppend, NULL);
  316. pXMLNodeToAppend->Release();
  317. }
  318. pXMLNodeRoot->Release();
  319. }
  320. return hr;
  321. }
  322. HRESULT XMLDOMFromFile(IN LPCWSTR pwzPath, OUT IXMLDOMDocument ** ppXMLDOMDoc)
  323. {
  324. HRESULT hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IXMLDOMDocument, ppXMLDOMDoc));
  325. if (SUCCEEDED(hr))
  326. {
  327. VARIANT xmlSource;
  328. xmlSource.vt = VT_BSTR;
  329. xmlSource.bstrVal = SysAllocString(pwzPath);
  330. if (xmlSource.bstrVal)
  331. {
  332. VARIANT_BOOL fIsSuccessful = VARIANT_TRUE;
  333. hr = (*ppXMLDOMDoc)->load(xmlSource, &fIsSuccessful);
  334. if ((S_FALSE == hr) || (VARIANT_FALSE == fIsSuccessful))
  335. {
  336. // This happens when the file isn't a valid XML file.
  337. hr = E_FAIL;
  338. }
  339. VariantClear(&xmlSource);
  340. }
  341. if (FAILED(hr))
  342. {
  343. ATOMICRELEASE(*ppXMLDOMDoc);
  344. }
  345. }
  346. return hr;
  347. }
  348. HRESULT XMLElem_VerifyTagName(IN IXMLDOMElement * pXMLElementMessage, IN LPCWSTR pwszTagName)
  349. {
  350. BSTR bstrTagName;
  351. HRESULT hr = pXMLElementMessage->get_tagName(&bstrTagName);
  352. if (S_FALSE == hr)
  353. {
  354. hr = E_FAIL;
  355. }
  356. else if (SUCCEEDED(hr))
  357. {
  358. if (!bstrTagName || !pwszTagName || StrCmpIW(bstrTagName, pwszTagName))
  359. {
  360. hr = E_FAIL;
  361. }
  362. SysFreeString(bstrTagName);
  363. }
  364. return hr;
  365. }
  366. HRESULT XMLElem_GetElementsByTagName(IN IXMLDOMElement * pXMLElementMessage, IN LPCWSTR pwszTagName, OUT IXMLDOMNodeList ** ppNodeList)
  367. {
  368. BSTR bstrTagName = SysAllocString(pwszTagName);
  369. HRESULT hr = E_OUTOFMEMORY;
  370. *ppNodeList = NULL;
  371. if (bstrTagName)
  372. {
  373. hr = pXMLElementMessage->getElementsByTagName(bstrTagName, ppNodeList);
  374. if (S_FALSE == hr)
  375. {
  376. hr = E_FAIL;
  377. }
  378. SysFreeString(bstrTagName);
  379. }
  380. return hr;
  381. }
  382. HRESULT XMLNode_GetAttributeValue(IN IXMLDOMNode * pXMLNode, IN LPCWSTR pwszAttributeName, OUT BSTR * pbstrValue)
  383. {
  384. BSTR bstrAttributeName = SysAllocString(pwszAttributeName);
  385. HRESULT hr = E_OUTOFMEMORY;
  386. *pbstrValue = NULL;
  387. if (bstrAttributeName)
  388. {
  389. IXMLDOMNamedNodeMap * pNodeAttributes;
  390. hr = pXMLNode->get_attributes(&pNodeAttributes);
  391. if (S_FALSE == hr) hr = E_FAIL;
  392. if (SUCCEEDED(hr))
  393. {
  394. IXMLDOMNode * pTypeAttribute;
  395. hr = pNodeAttributes->getNamedItem(bstrAttributeName, &pTypeAttribute);
  396. if (S_FALSE == hr) hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  397. if (SUCCEEDED(hr))
  398. {
  399. VARIANT varAtribValue = {0};
  400. hr = pTypeAttribute->get_nodeValue(&varAtribValue);
  401. if (S_FALSE == hr) hr = E_FAIL;
  402. if (SUCCEEDED(hr) && (VT_BSTR == varAtribValue.vt))
  403. {
  404. *pbstrValue = SysAllocString(varAtribValue.bstrVal);
  405. }
  406. VariantClear(&varAtribValue);
  407. pTypeAttribute->Release();
  408. }
  409. pNodeAttributes->Release();
  410. }
  411. SysFreeString(bstrAttributeName);
  412. }
  413. return hr;
  414. }
  415. HRESULT XMLNode_GetChildTag(IN IXMLDOMNode * pXMLNode, IN LPCWSTR pwszTagName, OUT IXMLDOMNode ** ppChildNode)
  416. {
  417. HRESULT hr = E_INVALIDARG;
  418. *ppChildNode = NULL;
  419. if (pXMLNode)
  420. {
  421. IXMLDOMElement * pXMLElement;
  422. hr = pXMLNode->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pXMLElement));
  423. if (SUCCEEDED(hr))
  424. {
  425. IXMLDOMNodeList * pNodeList;
  426. hr = XMLElem_GetElementsByTagName(pXMLElement, pwszTagName, &pNodeList);
  427. if (SUCCEEDED(hr))
  428. {
  429. hr = XMLNodeList_GetChild(pNodeList, 0, ppChildNode);
  430. pNodeList->Release();
  431. }
  432. pXMLElement->Release();
  433. }
  434. }
  435. return hr;
  436. }
  437. HRESULT XMLNode_GetTagText(IN IXMLDOMNode * pXMLNode, OUT BSTR * pbstrValue)
  438. {
  439. DOMNodeType nodeType = NODE_TEXT;
  440. HRESULT hr = pXMLNode->get_nodeType(&nodeType);
  441. *pbstrValue = NULL;
  442. if (S_FALSE == hr) hr = E_FAIL;
  443. if (SUCCEEDED(hr))
  444. {
  445. if (NODE_TEXT == nodeType)
  446. {
  447. VARIANT varAtribValue = {0};
  448. hr = pXMLNode->get_nodeValue(&varAtribValue);
  449. if (S_FALSE == hr) hr = E_FAIL;
  450. if (SUCCEEDED(hr) && (VT_BSTR == varAtribValue.vt))
  451. {
  452. *pbstrValue = SysAllocString(varAtribValue.bstrVal);
  453. }
  454. VariantClear(&varAtribValue);
  455. }
  456. else
  457. {
  458. hr = pXMLNode->get_text(pbstrValue);
  459. }
  460. }
  461. return hr;
  462. }
  463. HRESULT XMLNodeList_GetChild(IN IXMLDOMNodeList * pNodeList, IN DWORD dwIndex, OUT IXMLDOMNode ** ppXMLChildNode)
  464. {
  465. HRESULT hr = pNodeList->get_item(dwIndex, ppXMLChildNode);
  466. if (S_FALSE == hr)
  467. {
  468. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  469. }
  470. return hr;
  471. }
  472. HRESULT XMLNode_GetChildTagTextValue(IN IXMLDOMNode * pXMLNode, IN BSTR bstrChildTag, OUT BSTR * pbstrValue)
  473. {
  474. IXMLDOMNode * pNodeType;
  475. HRESULT hr = XMLNode_GetChildTag(pXMLNode, bstrChildTag, &pNodeType);
  476. if (SUCCEEDED(hr))
  477. {
  478. hr = XMLNode_GetTagText(pNodeType, pbstrValue);
  479. pNodeType->Release();
  480. }
  481. return hr;
  482. }
  483. HRESULT XMLNode_GetChildTagTextValueToBool(IN IXMLDOMNode * pXMLNode, IN BSTR bstrChildTag, OUT BOOL * pfBoolean)
  484. {
  485. BSTR bstr;
  486. HRESULT hr = XMLNode_GetChildTagTextValue(pXMLNode, bstrChildTag, &bstr);
  487. if (SUCCEEDED(hr))
  488. {
  489. if (!StrCmpIW(bstr, L"on"))
  490. {
  491. *pfBoolean = TRUE;
  492. }
  493. else
  494. {
  495. *pfBoolean = FALSE;
  496. }
  497. SysFreeString(bstr);
  498. }
  499. return hr;
  500. }
  501. BOOL XML_IsChildTagTextEqual(IN IXMLDOMNode * pXMLNode, IN BSTR bstrChildTag, IN BSTR bstrText)
  502. {
  503. BOOL fIsChildTagTextEqual = FALSE;
  504. BSTR bstrChildText;
  505. HRESULT hr = XMLNode_GetChildTagTextValue(pXMLNode, bstrChildTag, &bstrChildText);
  506. if (SUCCEEDED(hr))
  507. {
  508. // Is this <TYPE>email</TYPE>?
  509. if (!StrCmpIW(bstrChildText, bstrText))
  510. {
  511. // No, so keep looking.
  512. fIsChildTagTextEqual = TRUE;
  513. }
  514. SysFreeString(bstrChildText);
  515. }
  516. return fIsChildTagTextEqual;
  517. }
  518. /////////////////////////////////////////////////////////////////////
  519. // Wininet Wrapping Helpers
  520. /////////////////////////////////////////////////////////////////////
  521. #define EMPTYSTR_FOR_NULL(str) ((!str) ? TEXT("") : (str))
  522. /*****************************************************************************\
  523. FUNCTION: InternetConnectWrap
  524. DESCRIPTION:
  525. PERF Notes:
  526. [Direct Net Connection]
  527. To: shapitst <Down the Hall>: 144ms - 250ms (Min: 2; Max: 1,667ms)
  528. To: rigel.cyberpass.net <San Diego, CA>: 717ms - 1006ms
  529. To: ftp.rz.uni-frankfurt.de <Germany>: 2609ms - 14,012ms
  530. COMMON ERROR VALUES:
  531. These are the return values in these different cases:
  532. ERROR_INTERNET_NAME_NOT_RESOLVED: No Proxy & DNS Lookup failed.
  533. ERROR_INTERNET_CANNOT_CONNECT: Some Auth Proxies and Netscape's Web/Auth Proxy
  534. ERROR_INTERNET_NAME_NOT_RESOLVED: Web Proxy
  535. ERROR_INTERNET_TIMEOUT: Invalid or Web Proxy blocked IP Address
  536. ERROR_INTERNET_INCORRECT_PASSWORD: IIS & UNIX, UserName may not exist or password for the user may be incorrect on.
  537. ERROR_INTERNET_LOGIN_FAILURE: Too many Users on IIS.
  538. ERROR_INTERNET_INCORRECT_USER_NAME: I haven't seen it.
  539. ERROR_INTERNET_EXTENDED_ERROR: yahoo.com exists, but ftp.yahoo.com doesn't.
  540. \*****************************************************************************/
  541. HRESULT InternetConnectWrap(HINTERNET hInternet, BOOL fAssertOnFailure, LPCTSTR pszServerName, INTERNET_PORT nServerPort,
  542. LPCTSTR pszUserName, LPCTSTR pszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle)
  543. {
  544. HRESULT hr = S_OK;
  545. DWORD dwError = 0;
  546. DEBUG_CODE(DebugStartWatch());
  547. *phFileHandle = InternetConnect(hInternet, pszServerName, nServerPort, pszUserName, pszPassword, dwService, dwFlags, dwContext);
  548. if (!*phFileHandle)
  549. {
  550. dwError = GetLastError();
  551. hr = HRESULT_FROM_WIN32(dwError);
  552. }
  553. DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetConnect(%#08lx, \"%ls\", \"%ls\", \"%ls\") returned %u. Time=%lums", hInternet, pszServerName, EMPTYSTR_FOR_NULL(pszUserName), EMPTYSTR_FOR_NULL(pszPassword), dwError, DebugStopWatch()));
  554. if (fAssertOnFailure)
  555. {
  556. }
  557. return hr;
  558. }
  559. /*****************************************************************************\
  560. FUNCTION: InternetOpenWrap
  561. DESCRIPTION:
  562. PERF Notes:
  563. [Direct Net Connection]
  564. Destination not applicable. 677-907ms
  565. \*****************************************************************************/
  566. HRESULT InternetOpenWrap(LPCTSTR pszAgent, DWORD dwAccessType, LPCTSTR pszProxy, LPCTSTR pszProxyBypass, DWORD dwFlags, HINTERNET * phFileHandle)
  567. {
  568. HRESULT hr = S_OK;
  569. DWORD dwError = 0;
  570. DEBUG_CODE(DebugStartWatch());
  571. *phFileHandle = InternetOpen(pszAgent, dwAccessType, pszProxy, pszProxyBypass, dwFlags);
  572. if (!*phFileHandle)
  573. {
  574. dwError = GetLastError();
  575. hr = HRESULT_FROM_WIN32(dwError);
  576. }
  577. DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetOpen(\"%ls\") returned %u. Time=%lums", pszAgent, dwError, DebugStopWatch()));
  578. return hr;
  579. }
  580. HRESULT InternetCloseHandleWrap(HINTERNET hInternet)
  581. {
  582. HRESULT hr = S_OK;
  583. DWORD dwError = 0;
  584. DEBUG_CODE(DebugStartWatch());
  585. if (!InternetCloseHandle(hInternet))
  586. {
  587. dwError = GetLastError();
  588. hr = HRESULT_FROM_WIN32(dwError);
  589. }
  590. DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetCloseHandle(%#08lx) returned %u. Time=%lums", hInternet, dwError, DebugStopWatch()));
  591. return hr;
  592. }
  593. /*****************************************************************************\
  594. FUNCTION: InternetOpenUrlWrap
  595. DESCRIPTION:
  596. PERF Notes:
  597. [Direct Net Connection]
  598. To: shapitst <Down the Hall>: 29ms
  599. To: rigel.cyberpass.net <San Diego, CA>: ???????
  600. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  601. \*****************************************************************************/
  602. HRESULT InternetOpenUrlWrap(HINTERNET hInternet, LPCTSTR pszUrl, LPCTSTR pszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle)
  603. {
  604. HRESULT hr = S_OK;
  605. DWORD dwError = 0;
  606. DEBUG_CODE(DebugStartWatch());
  607. *phFileHandle = InternetOpenUrl(hInternet, pszUrl, pszHeaders, dwHeadersLength, dwFlags, dwContext);
  608. if (!*phFileHandle)
  609. {
  610. dwError = GetLastError();
  611. hr = HRESULT_FROM_WIN32(dwError);
  612. }
  613. DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetOpenUrl(%#08lx, \"%ls\") returned %u. Time=%lums", hInternet, pszUrl, dwError, DebugStopWatch()));
  614. return hr;
  615. }
  616. HRESULT HttpOpenRequestWrap(IN HINTERNET hConnect, IN LPCSTR lpszVerb, IN LPCSTR lpszObjectName, IN LPCSTR lpszVersion,
  617. IN LPCSTR lpszReferer, IN LPCSTR FAR * lpszAcceptTypes, IN DWORD dwFlags, IN DWORD_PTR dwContext,
  618. LPDWORD pdwNumberOfBytesRead, HINTERNET * phConnectionHandle)
  619. {
  620. HRESULT hr = S_OK;
  621. DWORD dwError = 0;
  622. DEBUG_CODE(DebugStartWatch());
  623. *phConnectionHandle = HttpOpenRequestA(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, lpszAcceptTypes, dwFlags, dwContext);
  624. if (!*phConnectionHandle)
  625. {
  626. dwError = GetLastError();
  627. hr = HRESULT_FROM_WIN32(dwError);
  628. }
  629. DEBUG_CODE(TraceMsg(TF_WMTHEME, "HttpOpenRequest(%#08lx, \"%ls\") returned %u. Time=%lums", *phConnectionHandle, lpszVerb, dwError, DebugStopWatch()));
  630. return hr;
  631. }
  632. HRESULT HttpSendRequestWrap(IN HINTERNET hRequest, IN LPCSTR lpszHeaders, IN DWORD dwHeadersLength, IN LPVOID lpOptional, DWORD dwOptionalLength)
  633. {
  634. HRESULT hr = S_OK;
  635. DWORD dwError = 0;
  636. if (!HttpSendRequestA(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength))
  637. {
  638. dwError = GetLastError();
  639. hr = HRESULT_FROM_WIN32(dwError);
  640. }
  641. return hr;
  642. }
  643. HRESULT InternetReadFileWrap(HINTERNET hFile, LPVOID pvBuffer, DWORD dwNumberOfBytesToRead, LPDWORD pdwNumberOfBytesRead)
  644. {
  645. HRESULT hr = S_OK;
  646. DWORD dwError = 0;
  647. // DEBUG_CODE(DebugStartWatch());
  648. if (!InternetReadFile(hFile, pvBuffer, dwNumberOfBytesToRead, pdwNumberOfBytesRead))
  649. {
  650. dwError = GetLastError();
  651. hr = HRESULT_FROM_WIN32(dwError);
  652. }
  653. // DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetReadFile(%#08lx, ToRead=%d, Read=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToRead, (pdwNumberOfBytesRead ? *pdwNumberOfBytesRead : -1), dwError, DebugStopWatch()));
  654. return hr;
  655. }
  656. HRESULT CreateUrlCacheEntryWrap(IN LPCTSTR lpszUrlName, IN DWORD dwExpectedFileSize, IN LPCTSTR lpszFileExtension, OUT LPTSTR lpszFileName, IN DWORD dwReserved)
  657. {
  658. HRESULT hr = S_OK;
  659. DWORD dwError = 0;
  660. // DEBUG_CODE(DebugStartWatch());
  661. if (!CreateUrlCacheEntry(lpszUrlName, dwExpectedFileSize, lpszFileExtension, lpszFileName, dwReserved))
  662. {
  663. dwError = GetLastError();
  664. hr = HRESULT_FROM_WIN32(dwError);
  665. }
  666. // DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetReadFile(%#08lx, ToRead=%d, Read=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToRead, (pdwNumberOfBytesRead ? *pdwNumberOfBytesRead : -1), dwError, DebugStopWatch()));
  667. return hr;
  668. }
  669. HRESULT CommitUrlCacheEntryWrap(IN LPCTSTR lpszUrlName, IN LPCTSTR lpszLocalFileName, IN FILETIME ExpireTime, IN FILETIME LastModifiedTime,
  670. IN DWORD CacheEntryType, IN LPWSTR lpHeaderInfo, IN DWORD dwHeaderSize, IN LPCTSTR lpszFileExtension, IN LPCTSTR lpszOriginalUrl)
  671. {
  672. HRESULT hr = S_OK;
  673. DWORD dwError = 0;
  674. // DEBUG_CODE(DebugStartWatch());
  675. if (!CommitUrlCacheEntryW(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime, CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl))
  676. {
  677. dwError = GetLastError();
  678. hr = HRESULT_FROM_WIN32(dwError);
  679. }
  680. // DEBUG_CODE(TraceMsg(TF_WMTHEME, "InternetReadFile(%#08lx, ToRead=%d, Read=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToRead, (pdwNumberOfBytesRead ? *pdwNumberOfBytesRead : -1), dwError, DebugStopWatch()));
  681. return hr;
  682. }
  683. #define SIZE_COPY_BUFFER (32 * 1024) // 32k
  684. HRESULT InternetReadIntoBSTR(HINTERNET hInternetRead, OUT BSTR * pbstrXML)
  685. {
  686. BYTE byteBuffer[SIZE_COPY_BUFFER];
  687. DWORD cbRead = SIZE_COPY_BUFFER;
  688. DWORD cchSize = 0;
  689. HRESULT hr = S_OK;
  690. *pbstrXML = NULL;
  691. while (SUCCEEDED(hr) && cbRead)
  692. {
  693. hr = InternetReadFileWrap(hInternetRead, byteBuffer, SIZEOF(byteBuffer), &cbRead);
  694. if (SUCCEEDED(hr) && cbRead)
  695. {
  696. BSTR bstrOld = *pbstrXML;
  697. BSTR bstrEnd;
  698. // The string may not be terminated.
  699. byteBuffer[cbRead] = 0;
  700. cchSize += ARRAYSIZE(byteBuffer);
  701. *pbstrXML = SysAllocStringLen(NULL, cchSize);
  702. if (*pbstrXML)
  703. {
  704. if (bstrOld)
  705. {
  706. StrCpy(*pbstrXML, bstrOld);
  707. }
  708. else
  709. {
  710. (*pbstrXML)[0] = 0;
  711. }
  712. bstrEnd = *pbstrXML + lstrlenW(*pbstrXML);
  713. SHAnsiToUnicode((LPCSTR) byteBuffer, bstrEnd, ARRAYSIZE(byteBuffer));
  714. }
  715. else
  716. {
  717. hr = E_OUTOFMEMORY;
  718. }
  719. SysFreeString(bstrOld);
  720. }
  721. }
  722. return hr;
  723. }
  724. /////////////////////////////////////////////////////////////////////
  725. // File System Wrapping Helpers
  726. /////////////////////////////////////////////////////////////////////
  727. HRESULT CreateFileHrWrap(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  728. DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, HANDLE * phFileHandle)
  729. {
  730. HRESULT hr = S_OK;
  731. HANDLE hTemp = NULL;
  732. DWORD dwError = 0;
  733. if (!phFileHandle)
  734. phFileHandle = &hTemp;
  735. *phFileHandle = CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
  736. if (!*phFileHandle)
  737. {
  738. dwError = GetLastError();
  739. hr = HRESULT_FROM_WIN32(dwError);
  740. }
  741. if (hTemp)
  742. CloseHandle(hTemp);
  743. return hr;
  744. }
  745. HRESULT WriteFileWrap(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
  746. {
  747. HRESULT hr = S_OK;
  748. DWORD dwError = 0;
  749. if (!WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped))
  750. {
  751. dwError = GetLastError();
  752. hr = HRESULT_FROM_WIN32(dwError);
  753. }
  754. return hr;
  755. }
  756. HRESULT DeleteFileHrWrap(LPCWSTR pszPath)
  757. {
  758. HRESULT hr = S_OK;
  759. DWORD dwError = 0;
  760. if (!DeleteFileW(pszPath))
  761. {
  762. dwError = GetLastError();
  763. hr = HRESULT_FROM_WIN32(dwError);
  764. }
  765. return hr;
  766. }
  767. HRESULT GetPrivateProfileStringHrWrap(LPCWSTR lpAppName, LPCWSTR lpKeyName, LPCWSTR lpDefault, LPWSTR lpReturnedString, DWORD nSize, LPCWSTR lpFileName)
  768. {
  769. HRESULT hr = S_OK;
  770. DWORD chGot = GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName);
  771. // What else can indicate an error value?
  772. if (0 == chGot)
  773. {
  774. hr = HRESULT_FROM_WIN32(GetLastError());
  775. if (SUCCEEDED(hr))
  776. hr = E_FAIL;
  777. }
  778. return hr;
  779. }
  780. /////////////////////////////////////////////////////////////////////
  781. // Registry Helpers
  782. /////////////////////////////////////////////////////////////////////
  783. HRESULT HrRegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
  784. {
  785. DWORD dwError = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
  786. return HRESULT_FROM_WIN32(dwError);
  787. }
  788. HRESULT HrRegCreateKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved, LPTSTR lpClass, DWORD dwOptions,
  789. REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition)
  790. {
  791. DWORD dwError = RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
  792. return HRESULT_FROM_WIN32(dwError);
  793. }
  794. HRESULT HrRegQueryValueEx(IN HKEY hKey, IN LPCTSTR lpValueName, IN LPDWORD lpReserved, IN LPDWORD lpType, IN LPBYTE lpData, IN LPDWORD lpcbData)
  795. {
  796. DWORD dwError = RegQueryValueEx(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
  797. return HRESULT_FROM_WIN32(dwError);
  798. }
  799. HRESULT HrRegSetValueEx(IN HKEY hKey, IN LPCTSTR lpValueName, IN DWORD dwReserved, IN DWORD dwType, IN CONST BYTE *lpData, IN DWORD cbData)
  800. {
  801. DWORD dwError = RegSetValueEx(hKey, lpValueName, dwReserved, dwType, lpData, cbData);
  802. return HRESULT_FROM_WIN32(dwError);
  803. }
  804. HRESULT HrRegEnumKey(HKEY hKey, DWORD dwIndex, LPTSTR lpName, DWORD cbName)
  805. {
  806. DWORD dwError = RegEnumKey(hKey, dwIndex, lpName, cbName);
  807. return HRESULT_FROM_WIN32(dwError);
  808. }
  809. HRESULT HrRegEnumValue(HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcValueName, LPDWORD lpReserved,
  810. LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
  811. {
  812. DWORD dwError = RegEnumValue(hKey, dwIndex, lpValueName, lpcValueName, lpReserved, lpType, lpData, lpcbData);
  813. return HRESULT_FROM_WIN32(dwError);
  814. }
  815. HRESULT HrRegQueryInfoKey(HKEY hKey, LPTSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen,
  816. LPDWORD lpcMaxClassLen, LPDWORD lpcValues, LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime)
  817. {
  818. DWORD dwError = RegQueryInfoKey(hKey, lpClass, lpcClass, lpReserved, lpcSubKeys, lpcMaxSubKeyLen,
  819. lpcMaxClassLen, lpcValues, lpcMaxValueNameLen, lpcMaxValueLen, lpcbSecurityDescriptor, lpftLastWriteTime);
  820. return HRESULT_FROM_WIN32(dwError);
  821. }
  822. HRESULT HrBStrRegQueryValue(IN HKEY hKey, IN LPCTSTR lpValueName, OUT BSTR * pbstr)
  823. {
  824. TCHAR szValue[MAX_PATH];
  825. DWORD dwType;
  826. DWORD cbSize = sizeof(szValue);
  827. HRESULT hr = HrRegQueryValueEx(hKey, lpValueName, 0, &dwType, (BYTE *)szValue, &cbSize);
  828. *pbstr = NULL;
  829. if (SUCCEEDED(hr))
  830. {
  831. hr = HrSysAllocStringW(szValue, pbstr);
  832. }
  833. return hr;
  834. }
  835. HRESULT HrSHGetValue(IN HKEY hKey, IN LPCTSTR pszSubKey, OPTIONAL IN LPCTSTR pszValue, OPTIONAL OUT LPDWORD pdwType,
  836. OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData)
  837. {
  838. DWORD dwError = SHGetValue(hKey, pszSubKey, pszValue, pdwType, pvData, pcbData);
  839. return HRESULT_FROM_WIN32(dwError);
  840. }
  841. HRESULT HrSHSetValue(IN HKEY hkey, IN LPCTSTR pszSubKey, OPTIONAL IN LPCTSTR pszValue, DWORD dwType, OPTIONAL OUT LPVOID pvData, IN DWORD cbData)
  842. {
  843. DWORD dwError = SHSetValue(hkey, pszSubKey, pszValue, dwType, pvData, cbData);
  844. return HRESULT_FROM_WIN32(dwError);
  845. }
  846. HRESULT HrRegSetValueString(IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName, OUT LPCWSTR pszPath)
  847. {
  848. DWORD cbSize = ((lstrlenW(pszPath) + 1) * sizeof(pszPath[0]));
  849. return HrSHSetValue(hKey, pszSubKey, pszValueName, REG_SZ, (BYTE *)pszPath, cbSize);
  850. }
  851. HRESULT HrRegDeleteValue(IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName)
  852. {
  853. HRESULT hr = S_OK;
  854. HKEY hKeySub = hKey;
  855. if (pszSubKey)
  856. {
  857. hr = HrRegOpenKeyEx(hKey, pszSubKey, 0, KEY_WRITE, &hKeySub);
  858. }
  859. if (SUCCEEDED(hr))
  860. {
  861. DWORD dwError = RegDeleteValue(hKeySub, pszValueName);
  862. hr = HRESULT_FROM_WIN32(dwError);
  863. }
  864. if (hKeySub == hKey)
  865. {
  866. RegCloseKey(hKeySub);
  867. }
  868. return hr;
  869. }
  870. DWORD HrRegGetDWORD(HKEY hKey, LPCWSTR szKey, LPCWSTR szValue, DWORD dwDefault)
  871. {
  872. DWORD dwResult = dwDefault;
  873. DWORD cbSize = sizeof(dwResult);
  874. DWORD dwType;
  875. DWORD dwError = SHGetValue(hKey, szKey, szValue, &dwType, &dwResult, &cbSize);
  876. if ((ERROR_SUCCESS == dwError) ||
  877. ((REG_DWORD != dwType) && (REG_BINARY != dwType)))
  878. {
  879. return dwDefault;
  880. }
  881. return dwResult;
  882. }
  883. /////////////////////////////////////////////////////////////////////
  884. // Palette Helpers
  885. /////////////////////////////////////////////////////////////////////
  886. COLORREF GetNearestPaletteColor(HPALETTE hpal, COLORREF rgb)
  887. {
  888. PALETTEENTRY pe;
  889. GetPaletteEntries(hpal, GetNearestPaletteIndex(hpal, rgb & 0x00FFFFFF), 1, &pe);
  890. return RGB(pe.peRed, pe.peGreen, pe.peBlue);
  891. }
  892. BOOL IsPaletteColor(HPALETTE hpal, COLORREF rgb)
  893. {
  894. return GetNearestPaletteColor(hpal, rgb) == (rgb & 0xFFFFFF);
  895. }
  896. /////////////////////////////////////////////////////////////////////
  897. // Other Helpers
  898. /////////////////////////////////////////////////////////////////////
  899. HRESULT HrRewindStream(IStream * pstm)
  900. {
  901. LARGE_INTEGER liOrigin = {0,0};
  902. return pstm->Seek(liOrigin, STREAM_SEEK_SET, NULL);
  903. }
  904. #define SET_FLAG(dwAllFlags, dwFlag) ((dwAllFlags) |= (dwFlag))
  905. #define IS_FLAG_SET(dwAllFlags, dwFlag) ((BOOL)((dwAllFlags) & (dwFlag)))
  906. // PERFPERF
  907. // This routine used to copy 512 bytes at a time, but that had a major negative perf impact.
  908. // I have measured a 2-3x speedup in copy times by increasing this buffer size to 16k.
  909. // Yes, its a lot of stack, but it is memory well spent. -saml
  910. #define STREAM_COPY_BUF_SIZE 16384
  911. #define STREAM_PROGRESS_INTERVAL (100*1024/STREAM_COPY_BUF_SIZE) // display progress after this many blocks
  912. HRESULT StreamCopyWithProgress(IStream *pstmFrom, IStream *pstmTo, ULARGE_INTEGER cb, PROGRESSINFO * ppi)
  913. {
  914. BYTE buf[STREAM_COPY_BUF_SIZE];
  915. ULONG cbRead;
  916. HRESULT hres = NOERROR;
  917. int nSection = 0; // How many buffer sizes have we copied?
  918. ULARGE_INTEGER uliNewCompleted;
  919. if (ppi)
  920. {
  921. uliNewCompleted.QuadPart = ppi->uliBytesCompleted.QuadPart;
  922. }
  923. while (cb.QuadPart)
  924. {
  925. if (ppi && ppi->ppd)
  926. {
  927. if (0 == (nSection % STREAM_PROGRESS_INTERVAL))
  928. {
  929. EVAL(SUCCEEDED(ppi->ppd->SetProgress64(uliNewCompleted.QuadPart, ppi->uliBytesTotal.QuadPart)));
  930. if (ppi->ppd->HasUserCancelled())
  931. {
  932. hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  933. break;
  934. }
  935. }
  936. }
  937. hres = pstmFrom->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
  938. if (FAILED(hres) || (cbRead == 0))
  939. {
  940. // sometimes we are just done.
  941. if (SUCCEEDED(hres))
  942. hres = S_OK;
  943. break;
  944. }
  945. if (ppi)
  946. {
  947. uliNewCompleted.QuadPart += (ULONGLONG) cbRead;
  948. }
  949. cb.QuadPart -= cbRead;
  950. hres = pstmTo->Write(buf, cbRead, &cbRead);
  951. if (FAILED(hres) || (cbRead == 0))
  952. break;
  953. nSection++;
  954. }
  955. return hres;
  956. }
  957. /*
  958. // These are needed for COM/COM+ interop
  959. void __stdcall
  960. _com_raise_error(HRESULT hr, IErrorInfo* perrinfo) throw(_com_error)
  961. {
  962. throw _com_error(hr, perrinfo);
  963. }
  964. void __stdcall
  965. _com_issue_error(HRESULT hr) throw(_com_error)
  966. {
  967. _com_raise_error(hr, NULL);
  968. }
  969. void __stdcall
  970. _com_issue_errorex(HRESULT hr, IUnknown* punk, REFIID riid) throw(_com_error)
  971. {
  972. IErrorInfo* perrinfo = NULL;
  973. if (punk == NULL) {
  974. goto exeunt;
  975. }
  976. ISupportErrorInfo* psei;
  977. if (FAILED(punk->QueryInterface(__uuidof(ISupportErrorInfo),
  978. (void**)&psei))) {
  979. goto exeunt;
  980. }
  981. HRESULT hrSupportsErrorInfo;
  982. hrSupportsErrorInfo = psei->InterfaceSupportsErrorInfo(riid);
  983. psei->Release();
  984. if (hrSupportsErrorInfo != S_OK) {
  985. goto exeunt;
  986. }
  987. if (GetErrorInfo(0, &perrinfo) != S_OK) {
  988. perrinfo = NULL;
  989. }
  990. exeunt:
  991. _com_raise_error(hr, perrinfo);
  992. }
  993. */
  994. // needed by smtpserv:
  995. HRESULT HrByteToStream(LPSTREAM *lppstm, LPBYTE lpb, ULONG cb)
  996. {
  997. // Locals
  998. HRESULT hr=S_OK;
  999. LARGE_INTEGER liOrigin = {0,0};
  1000. // Create H Global Stream
  1001. hr = CreateStreamOnHGlobal (NULL, TRUE, lppstm);
  1002. if (FAILED(hr))
  1003. goto exit;
  1004. // Write String
  1005. hr = (*lppstm)->Write (lpb, cb, NULL);
  1006. if (FAILED(hr))
  1007. goto exit;
  1008. // Rewind the steam
  1009. hr = (*lppstm)->Seek(liOrigin, STREAM_SEEK_SET, NULL);
  1010. if (FAILED(hr))
  1011. goto exit;
  1012. exit:
  1013. // Done
  1014. return hr;
  1015. }
  1016. const char szDayOfWeekArray[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
  1017. const char szMonthOfYearArray[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ;
  1018. void GetDateString(char * szSentDateString, ULONG stringLen)
  1019. {
  1020. // Sent Date
  1021. SYSTEMTIME stSentTime;
  1022. CHAR szMonth[10], szWeekDay[12] ;
  1023. GetSystemTime(&stSentTime);
  1024. lstrcpynA(szWeekDay, szDayOfWeekArray[stSentTime.wDayOfWeek], ARRAYSIZE(szWeekDay)) ;
  1025. lstrcpynA(szMonth, szMonthOfYearArray[stSentTime.wMonth-1], ARRAYSIZE(szMonth)) ;
  1026. wnsprintfA(szSentDateString, stringLen, "%s, %u %s %u %2d:%02d:%02d ", (LPSTR) szWeekDay, stSentTime.wDay,
  1027. (LPSTR) szMonth, stSentTime.wYear, stSentTime.wHour,
  1028. stSentTime.wMinute, stSentTime.wSecond) ;
  1029. }
  1030. /*****************************************************************************\
  1031. PARAMETERS:
  1032. RETURN: Win32 HRESULT (Not Script Safe).
  1033. SUCCEEDED(hr) for OK and out params filled in.
  1034. FAILED(hr) for all errors.
  1035. \*****************************************************************************/
  1036. HRESULT GetQueryStringValue(BSTR bstrURL, LPCWSTR pwszValue, LPWSTR pwszData, int cchSizeData)
  1037. {
  1038. HRESULT hr = E_FAIL;
  1039. LPCWSTR pwszIterate = bstrURL;
  1040. pwszIterate = StrChrW(pwszIterate, L'?'); // Advance to Query part of URL.
  1041. while (pwszIterate && pwszIterate[0])
  1042. {
  1043. pwszIterate++; // Start at first value
  1044. LPCWSTR pwszEndOfValue = StrChrW(pwszIterate, L'=');
  1045. if (!pwszEndOfValue)
  1046. break;
  1047. int cchValueSize = (INT)(UINT)(pwszEndOfValue - pwszIterate);
  1048. if (0 == StrCmpNIW(pwszValue, pwszIterate, cchValueSize))
  1049. {
  1050. int cchSizeToCopy = cchSizeData; // Copy rest of line by default.
  1051. pwszIterate = StrChrW(pwszEndOfValue, L'&');
  1052. if (pwszIterate)
  1053. {
  1054. cchSizeToCopy = (INT)(UINT)(pwszIterate - pwszEndOfValue);
  1055. }
  1056. // It matches, now get the Data.
  1057. StrCpyNW(pwszData, (pwszEndOfValue + 1), cchSizeToCopy);
  1058. hr = S_OK;
  1059. break;
  1060. }
  1061. else
  1062. {
  1063. pwszIterate = StrChrW(pwszEndOfValue, L'&');
  1064. }
  1065. }
  1066. return hr;
  1067. }
  1068. // BUGBUG: This makes this object ways safe. When the MailApps security design is
  1069. // complete, this needs to be removed for the permanate security solution.
  1070. HRESULT MarkObjectSafe(IUnknown * punk)
  1071. {
  1072. HRESULT hr = S_OK;
  1073. IObjectSafety * pos;
  1074. hr = punk->QueryInterface(IID_PPV_ARG(IObjectSafety, &pos));
  1075. if (SUCCEEDED(hr))
  1076. {
  1077. // BUGBUG: This makes this object ways safe. When the MailApps security design is
  1078. // complete, this needs to be removed for the permanate solution.
  1079. pos->SetInterfaceSafetyOptions(IID_IDispatch, (INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA), 0);
  1080. pos->Release();
  1081. }
  1082. return hr;
  1083. }
  1084. BOOL _InitComCtl32()
  1085. {
  1086. static BOOL fInitialized = FALSE;
  1087. if (!fInitialized)
  1088. {
  1089. INITCOMMONCONTROLSEX icc;
  1090. icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
  1091. icc.dwICC = (ICC_ANIMATE_CLASS | ICC_USEREX_CLASSES | ICC_COOL_CLASSES | ICC_INTERNET_CLASSES | ICC_PAGESCROLLER_CLASS | ICC_NATIVEFNTCTL_CLASS | ICC_LISTVIEW_CLASSES);
  1092. fInitialized = InitCommonControlsEx(&icc);
  1093. }
  1094. return fInitialized;
  1095. }
  1096. HRESULT HrShellExecute(HWND hwnd, LPCTSTR lpVerb, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)
  1097. {
  1098. ULARGE_INTEGER uiResult;
  1099. uiResult.QuadPart = (ULONGLONG) ShellExecute(hwnd, lpVerb, lpFile, lpParameters, lpDirectory, nShowCmd);
  1100. if (32 < uiResult.QuadPart)
  1101. {
  1102. uiResult.LowPart = ERROR_SUCCESS;
  1103. }
  1104. return HRESULT_FROM_WIN32(uiResult.LowPart);
  1105. }
  1106. HRESULT StrReplaceToken(IN LPCTSTR pszToken, IN LPCTSTR pszReplaceValue, IN LPTSTR pszString, IN DWORD cchSize)
  1107. {
  1108. HRESULT hr = S_OK;
  1109. LPTSTR pszTempLastHalf = NULL;
  1110. LPTSTR pszNextToken = pszString;
  1111. while (pszNextToken = StrStrI(pszNextToken, pszToken))
  1112. {
  1113. // We found one.
  1114. LPTSTR pszPastToken = pszNextToken + lstrlen(pszToken);
  1115. Str_SetPtr(&pszTempLastHalf, pszPastToken); // Keep a copy because we will overwrite it.
  1116. pszNextToken[0] = 0; // Remove the rest of the string.
  1117. StrCatBuff(pszString, pszReplaceValue, cchSize);
  1118. StrCatBuff(pszString, pszTempLastHalf, cchSize);
  1119. pszNextToken += lstrlen(pszReplaceValue);
  1120. }
  1121. Str_SetPtr(&pszTempLastHalf, NULL);
  1122. return hr;
  1123. }
  1124. BOOL IUnknown_CompareCLSID(IN IUnknown * punk, IN CLSID clsid)
  1125. {
  1126. BOOL fIsEqual = FALSE;
  1127. if (punk)
  1128. {
  1129. CLSID clsidPageID;
  1130. HRESULT hr = IUnknown_GetClassID(punk, &clsidPageID);
  1131. if (SUCCEEDED(hr) && IsEqualCLSID(clsidPageID, clsid))
  1132. {
  1133. fIsEqual = TRUE;
  1134. }
  1135. }
  1136. return fIsEqual;
  1137. }
  1138. HRESULT IEnumUnknown_FindCLSID(IN IUnknown * punk, IN CLSID clsid, OUT IUnknown ** ppunkFound)
  1139. {
  1140. HRESULT hr = E_INVALIDARG;
  1141. if (punk && ppunkFound)
  1142. {
  1143. IEnumUnknown * pEnum;
  1144. *ppunkFound = NULL;
  1145. hr = punk->QueryInterface(IID_PPV_ARG(IEnumUnknown, &pEnum));
  1146. if (SUCCEEDED(hr))
  1147. {
  1148. IUnknown * punkToTry;
  1149. ULONG ulFetched;
  1150. pEnum->Reset();
  1151. hr = E_FAIL;
  1152. while (SUCCEEDED(pEnum->Next(1, &punkToTry, &ulFetched)) &&
  1153. (1 == ulFetched))
  1154. {
  1155. if (IUnknown_CompareCLSID(punkToTry, clsid))
  1156. {
  1157. *ppunkFound = punkToTry;
  1158. hr = S_OK;
  1159. break;
  1160. }
  1161. punkToTry->Release();
  1162. }
  1163. pEnum->Release();
  1164. }
  1165. }
  1166. return hr;
  1167. }
  1168. BYTE WINAPI MyStrToByte(LPCTSTR sz)
  1169. {
  1170. BYTE l=0;
  1171. while (*sz >= TEXT('0') && *sz <= TEXT('9'))
  1172. {
  1173. l = l*10 + (*sz++ - TEXT('0'));
  1174. }
  1175. return l;
  1176. }
  1177. COLORREF ConvertColor(LPTSTR pszColor)
  1178. {
  1179. BYTE RGBTemp[3];
  1180. LPTSTR pszTemp = pszColor;
  1181. UINT i;
  1182. if (!pszColor || !*pszColor)
  1183. {
  1184. return RGB(0,0,0);
  1185. }
  1186. for (i =0; i < 3; i++)
  1187. {
  1188. // Remove leading spaces
  1189. while (*pszTemp == TEXT(' '))
  1190. {
  1191. pszTemp++;
  1192. }
  1193. // Set pszColor to the beginning of the number
  1194. pszColor = pszTemp;
  1195. // Find the end of the number and null terminate
  1196. while ((*pszTemp) && (*pszTemp != TEXT(' ')))
  1197. {
  1198. pszTemp++;
  1199. }
  1200. if (*pszTemp != TEXT('\0'))
  1201. {
  1202. *pszTemp = TEXT('\0');
  1203. }
  1204. pszTemp++;
  1205. RGBTemp[i] = MyStrToByte(pszColor);
  1206. }
  1207. return (RGB(RGBTemp[0], RGBTemp[1], RGBTemp[2]));
  1208. }
  1209. // Paremeters:
  1210. // hwndOwner -- owner window
  1211. // idTemplate -- specifies template (e.g., "Can't open %2%s\n\n%1%s")
  1212. // hr -- specifies the HRESULT error code
  1213. // pszParam -- specifies the 2nd parameter to idTemplate
  1214. // dwFlags -- flags for MessageBox
  1215. UINT ErrorMessageBox(HWND hwndOwner, LPCTSTR pszTitle, UINT idTemplate, HRESULT hr, LPCTSTR pszParam, UINT dwFlags)
  1216. {
  1217. UINT idRet = IDCANCEL;
  1218. TCHAR szErrNumString[MAX_PATH * 2];
  1219. TCHAR szTemplate[MAX_PATH * 2];
  1220. TCHAR szErrMsg[MAX_PATH * 2];
  1221. if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, szErrNumString, ARRAYSIZE(szErrNumString), NULL))
  1222. {
  1223. AssertMsg((NULL != pszParam), TEXT("We must get a valid error value from FormatMessage or a detailed message must be provided."));
  1224. szErrNumString[0] = 0;
  1225. wnsprintf(szErrNumString, ARRAYSIZE(szErrNumString), TEXT(" hr=%#08lx"), hr);
  1226. }
  1227. LoadString(HINST_THISDLL, idTemplate, szTemplate, ARRAYSIZE(szTemplate));
  1228. if (pszParam)
  1229. {
  1230. wnsprintf(szErrMsg, ARRAYSIZE(szErrMsg), szTemplate, szErrNumString, pszParam);
  1231. }
  1232. else
  1233. {
  1234. wnsprintf(szErrMsg, ARRAYSIZE(szErrMsg), szTemplate, szErrNumString);
  1235. }
  1236. return MessageBox(hwndOwner, szErrMsg, pszTitle, (MB_OK | MB_ICONERROR));
  1237. }
  1238. BOOL IsOSNT(void)
  1239. {
  1240. OSVERSIONINFOA osVerInfoA;
  1241. osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
  1242. if (!GetVersionExA(&osVerInfoA))
  1243. return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
  1244. return (VER_PLATFORM_WIN32_NT == osVerInfoA.dwPlatformId);
  1245. }
  1246. DWORD GetOSVer(void)
  1247. {
  1248. OSVERSIONINFOA osVerInfoA;
  1249. osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
  1250. if (!GetVersionExA(&osVerInfoA))
  1251. return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
  1252. return osVerInfoA.dwMajorVersion;
  1253. }
  1254. void LogStatus(LPCSTR pszMessage, ...)
  1255. {
  1256. static int nLogOn = -1;
  1257. va_list vaParamList;
  1258. va_start(vaParamList, pszMessage);
  1259. if (-1 == nLogOn)
  1260. {
  1261. nLogOn = 1;//(SHRegGetBoolUSValue(SZ_THEMES, SZ_REGVALUE_LOGINFO, FALSE, FALSE) ? 1 : 0);
  1262. }
  1263. if (1 == nLogOn)
  1264. {
  1265. if (INVALID_HANDLE_VALUE == g_hLogFile)
  1266. {
  1267. TCHAR szPath[MAX_PATH];
  1268. if (GetWindowsDirectory(szPath, ARRAYSIZE(szPath)))
  1269. {
  1270. PathAppend(szPath, TEXT("Theme.log"));
  1271. g_hLogFile = CreateFile(szPath, (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1272. if (INVALID_HANDLE_VALUE != g_hLogFile)
  1273. {
  1274. CHAR szTimeDate[MAX_PATH];
  1275. FILETIME ftCurrent;
  1276. SYSTEMTIME stCurrent;
  1277. DWORD cbWritten;
  1278. GetLocalTime(&stCurrent);
  1279. SystemTimeToFileTime(&stCurrent, &ftCurrent);
  1280. SHFormatDateTimeA(&ftCurrent, NULL, szTimeDate, ARRAYSIZE(szTimeDate));
  1281. StrCatBuffA(szTimeDate, "\r\n", ARRAYSIZE(szTimeDate));
  1282. WriteFile(g_hLogFile, szTimeDate, lstrlenA(szTimeDate), &cbWritten, NULL);
  1283. }
  1284. }
  1285. }
  1286. if (INVALID_HANDLE_VALUE != g_hLogFile)
  1287. {
  1288. CHAR szMessage[4000];
  1289. DWORD cbWritten;
  1290. wvsprintfA(szMessage, pszMessage, vaParamList);
  1291. WriteFile(g_hLogFile, szMessage, lstrlenA(szMessage), &cbWritten, NULL);
  1292. }
  1293. }
  1294. va_end(vaParamList);
  1295. }
  1296. #define SzFromInt(sz, n) (wsprintf((LPTSTR)sz, (LPTSTR)TEXT("%d"), n), (LPTSTR)sz)
  1297. int WritePrivateProfileInt(LPCTSTR szApp, LPCTSTR szKey, int nDefault, LPCTSTR pszFileName)
  1298. {
  1299. CHAR sz[7];
  1300. return WritePrivateProfileString(szApp, szKey, SzFromInt(sz, nDefault), pszFileName);
  1301. }
  1302. #define SZ_RESOURCEDIR L"Resources"
  1303. HRESULT SHGetResourcePath(IN LPWSTR pszPath, IN DWORD cchSize)
  1304. {
  1305. return SHGetFolderPath(NULL, CSIDL_RESOURCES_LOCALIZED | CSIDL_FLAG_CREATE, NULL, 0, pszPath);
  1306. }
  1307. #define SZ_RESOURCEDIR_TOKEN TEXT("%ResourceDir%")
  1308. HRESULT ExpandResourceDir(IN LPWSTR pszPath, IN DWORD cchSize)
  1309. {
  1310. HRESULT hr = S_OK;
  1311. LPCTSTR pszToken = StrStrW(pszPath, SZ_RESOURCEDIR_TOKEN);
  1312. // Do we have stuff to replace?
  1313. if (pszToken)
  1314. {
  1315. // Yes, so get the replacement value.
  1316. WCHAR szResourceDir[MAX_PATH];
  1317. hr = SHGetResourcePath(szResourceDir, ARRAYSIZE(szResourceDir));
  1318. if (SUCCEEDED(hr))
  1319. {
  1320. hr = StrReplaceToken(SZ_RESOURCEDIR_TOKEN, szResourceDir, pszPath, cchSize);
  1321. }
  1322. }
  1323. return hr;
  1324. }
  1325. STDAPI SHPropertyBag_WritePunk(IN IPropertyBag * pPropertyPage, IN LPCWSTR pwzPropName, IN IUnknown * punk)
  1326. {
  1327. HRESULT hr = E_INVALIDARG;
  1328. if (pPropertyPage && pwzPropName)
  1329. {
  1330. VARIANT va;
  1331. va.vt = VT_UNKNOWN;
  1332. va.punkVal = punk;
  1333. hr = pPropertyPage->Write(pwzPropName, &va);
  1334. }
  1335. return hr;
  1336. }
  1337. STDAPI SHPropertyBag_ReadByRef(IN IPropertyBag * pPropertyPage, IN LPCWSTR pwzPropName, IN void * p, IN SIZE_T cbSize)
  1338. {
  1339. HRESULT hr = E_INVALIDARG;
  1340. if (pPropertyPage && pwzPropName && p)
  1341. {
  1342. VARIANT va;
  1343. hr = pPropertyPage->Read(pwzPropName, &va, NULL);
  1344. if (SUCCEEDED(hr))
  1345. {
  1346. if ((VT_BYREF == va.vt) && va.byref)
  1347. {
  1348. CopyMemory(p, va.byref, cbSize);
  1349. }
  1350. else
  1351. {
  1352. hr = E_FAIL;
  1353. }
  1354. }
  1355. }
  1356. return hr;
  1357. }
  1358. STDAPI SHPropertyBag_WriteByRef(IN IPropertyBag * pPropertyPage, IN LPCWSTR pwzPropName, IN void * p)
  1359. {
  1360. HRESULT hr = E_INVALIDARG;
  1361. if (pPropertyPage && pwzPropName && p)
  1362. {
  1363. VARIANT va;
  1364. va.vt = VT_BYREF;
  1365. va.byref = p;
  1366. hr = pPropertyPage->Write(pwzPropName, &va);
  1367. }
  1368. return hr;
  1369. }
  1370. HRESULT MyReleaseStgMedium( LPSTGMEDIUM pmedium )
  1371. {
  1372. if( pmedium->pUnkForRelease )
  1373. {
  1374. pmedium->pUnkForRelease->Release( );
  1375. }
  1376. else
  1377. {
  1378. switch( pmedium->tymed )
  1379. {
  1380. case TYMED_HGLOBAL:
  1381. GlobalFree( pmedium->hGlobal );
  1382. break;
  1383. case TYMED_ISTORAGE:
  1384. case TYMED_ISTREAM:
  1385. //
  1386. // hack, the stream and storage overlap eachother in the union
  1387. // so this works for both.
  1388. //
  1389. pmedium->pstm->Release( );
  1390. break;
  1391. default:
  1392. //
  1393. // Assert( 0 ); // unknown type
  1394. // Not fullly implemented.
  1395. MessageBeep( 0 );
  1396. break;
  1397. }
  1398. }
  1399. return NOERROR;
  1400. }
  1401. /*----------------------------------------------------------
  1402. Purpose: Gets a file list from an IDataObject. Allocates
  1403. ppszList to appropriate size and fills it with
  1404. a null-terminated list of paths. It is double-null
  1405. terminated.
  1406. If ppszList is NULL, then simply get the count of files.
  1407. Call DataObj_FreeList to free the ppszList.
  1408. Returns: standard
  1409. S_OK if the objects are inside a briefcase
  1410. S_FALSE if not
  1411. Cond: --
  1412. */
  1413. HRESULT PUBLIC DataObj_QueryFileList(
  1414. LPDATAOBJECT pdtobj,
  1415. LPTSTR * ppszList, // List of files (may be NULL)
  1416. LPUINT puCount) // Count of files
  1417. {
  1418. HRESULT hres = ResultFromScode(E_FAIL);
  1419. FORMATETC fmteHdrop = {(CLIPFORMAT)CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1420. STGMEDIUM medium;
  1421. ASSERT(pdtobj);
  1422. ASSERT(puCount);
  1423. // Or does it support hdrops?
  1424. //
  1425. hres = pdtobj->GetData(&fmteHdrop, &medium);
  1426. if (SUCCEEDED(hres))
  1427. {
  1428. // Yup
  1429. HDROP hdrop = (HDROP)medium.hGlobal;
  1430. UINT cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
  1431. UINT cchSize = 0;
  1432. UINT i;
  1433. *puCount = cFiles;
  1434. hres = ResultFromScode(S_FALSE);
  1435. if (ppszList)
  1436. {
  1437. // Determine size we need to allocate
  1438. for (i = 0; i < cFiles; i++)
  1439. {
  1440. cchSize += DragQueryFile(hdrop, i, NULL, 0) + 1;
  1441. }
  1442. cchSize++; // for extra null
  1443. *ppszList = (LPTSTR)LocalAlloc(LPTR, CbFromCch(cchSize));
  1444. if (*ppszList)
  1445. {
  1446. LPTSTR psz = *ppszList;
  1447. UINT cch;
  1448. // Translate the hdrop into our file list format.
  1449. // We know that they really are the same format,
  1450. // but to maintain the abstraction layer, we
  1451. // pretend we don't.
  1452. for (i = 0; i < cFiles; i++)
  1453. {
  1454. cch = DragQueryFile(hdrop, i, psz, cchSize) + 1;
  1455. psz += cch;
  1456. cchSize -= cch;
  1457. }
  1458. *psz = TEXT('\0'); // extra null
  1459. }
  1460. else
  1461. {
  1462. hres = ResultFromScode(E_OUTOFMEMORY);
  1463. }
  1464. }
  1465. MyReleaseStgMedium(&medium);
  1466. goto Leave;
  1467. }
  1468. // FEATURE: do we need to query for CF_TEXT?
  1469. Leave:
  1470. return hres;
  1471. }
  1472. /*----------------------------------------------------------
  1473. Purpose: Frees a file list that was allocated by DataObj_QueryFileList.
  1474. Returns: --
  1475. Cond: --
  1476. */
  1477. void PUBLIC DataObj_FreeList(
  1478. LPTSTR pszList)
  1479. {
  1480. LocalFree(pszList);
  1481. }