Leaked source code of windows server 2003
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.

688 lines
20 KiB

  1. #include "private.h"
  2. #include <urlmon.h>
  3. #include <wininet.h>
  4. #include <msxml.h>
  5. #include "cdfagent.h"
  6. #include "cdlabsc.h"
  7. #include "cdlagent.h"
  8. #include <urlmon.h>
  9. #include <subsmgr.h>
  10. #include "subsmgrp.h"
  11. #include <mluisupp.h>
  12. HRESULT GetXMLAttribute(IXMLElement *pItem, LPCWSTR pwszAttribute, VARIANT *pvRet);
  13. CCDLAgent::CCDLAgent()
  14. : m_pCCDLAgentBSC(NULL)
  15. , m_szCDF(NULL)
  16. , m_bAcceptSoftware(FALSE)
  17. {
  18. m_sdi.cbSize = sizeof(SOFTDISTINFO);
  19. m_bSilentMode = TRUE;
  20. }
  21. CCDLAgent::~CCDLAgent()
  22. {
  23. SAFERELEASE(m_pSoftDistElement);
  24. SAFERELEASE(m_pSoftDistExt);
  25. CRunDeliveryAgent::SafeRelease(m_pAgent);
  26. SAFEFREEOLESTR(m_szCDF);
  27. SAFEFREEBSTR(m_szErrorText);
  28. SAFEDELETE(m_sdi.szAbstract);
  29. SAFEDELETE(m_sdi.szTitle);
  30. SAFEDELETE(m_sdi.szHREF);
  31. SAFEFREEOLESTR(m_szDistUnit);
  32. }
  33. HRESULT CCDLAgent::StartOperation()
  34. {
  35. HRESULT hr = S_OK, hr2;
  36. // unknown pointers
  37. IUnknown *punk = NULL;
  38. IServiceProvider *pSP;
  39. m_pSoftDistElement = NULL;
  40. if (FAILED(ReadOLESTR(m_pSubscriptionItem, c_szPropURL, &m_szURL)))
  41. {
  42. hr = E_INVALIDARG;
  43. goto Failed;
  44. }
  45. hr2 = E_FAIL;
  46. if (SUCCEEDED(m_pAgentEvents->QueryInterface(IID_IServiceProvider, (void **)&pSP)) && pSP)
  47. {
  48. hr2 = pSP->QueryService(CLSID_XMLDocument, IID_IXMLElement, (void **)&punk);
  49. pSP->Release();
  50. }
  51. if (FAILED(hr2) || !punk)
  52. {
  53. // We are processing a request to pull a CAB, probably from Web Crawler agent.
  54. if (FAILED(ReadOLESTR(m_pSubscriptionItem, L"DistUnit", &m_szDistUnit)) ||
  55. FAILED(ReadDWORD(m_pSubscriptionItem, L"VersionMS",&m_dwVersionMS)) ||
  56. FAILED(ReadDWORD(m_pSubscriptionItem, L"VersionLS", &m_dwVersionLS)))
  57. {
  58. hr = E_INVALIDARG;
  59. goto Failed;
  60. }
  61. m_pSoftDistElement = NULL;
  62. }
  63. else
  64. {
  65. if (FAILED(punk->QueryInterface(IID_IXMLElement, (void **)&m_pSoftDistElement)))
  66. {
  67. SAFERELEASE(punk);
  68. hr = E_INVALIDARG;
  69. goto Failed;
  70. }
  71. SAFERELEASE(punk);
  72. Assert(m_pSoftDistElement);
  73. }
  74. ReadDWORD(m_pSubscriptionItem, c_szPropCrawlMaxSize, &m_dwMaxSizeKB);
  75. ReadDWORD(m_pSubscriptionItem, c_szPropChannelFlags, &m_dwChannelFlags);
  76. ReadDWORD(m_pSubscriptionItem, c_szPropAgentFlags, &m_dwAgentFlags);
  77. hr = CDeliveryAgent::StartOperation();
  78. return hr;
  79. Failed:
  80. SetEndStatus(hr);
  81. SendUpdateNone();
  82. return hr;
  83. }
  84. HRESULT CCDLAgent::StartDownload()
  85. {
  86. IBindCtx *pbc = NULL;
  87. HRESULT hr = S_OK;
  88. LPWSTR szCodeBase;
  89. DWORD dwSize;
  90. BOOL bCleanUpNow = FALSE;
  91. DWORD dwPolicy = 0;
  92. DWORD dwContext = 0;
  93. IInternetSecurityManager * pism = NULL;
  94. if (FAILED(GetEndStatus())) {
  95. hr = GetEndStatus();
  96. goto Exit;
  97. }
  98. hr = CoCreateInstance(CLSID_SoftDistExt, NULL, CLSCTX_INPROC_SERVER, IID_ISoftDistExt, (void **)&m_pSoftDistExt);
  99. if (FAILED(hr))
  100. goto Exit;
  101. // Process SOFTDIST tag structure if present.
  102. if (m_pSoftDistElement != NULL) {
  103. dwPolicy = 0xFFFF0000;
  104. if (FAILED(CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
  105. IID_IInternetSecurityManager, (void**)&pism)) || !pism)
  106. {
  107. hr = E_ACCESSDENIED;
  108. goto Exit;
  109. }
  110. hr = pism->ProcessUrlAction(m_szURL, URLACTION_CHANNEL_SOFTDIST_PERMISSIONS,
  111. (BYTE *)&dwPolicy, sizeof(dwPolicy),
  112. (BYTE *)&dwContext, sizeof(dwContext), PUAF_NOUI, 0);
  113. pism->Release();
  114. if (FAILED(hr))
  115. {
  116. goto Exit;
  117. }
  118. dwPolicy &= 0xFFFF0000;
  119. if (dwPolicy != URLPOLICY_CHANNEL_SOFTDIST_PROHIBIT
  120. && dwPolicy != URLPOLICY_CHANNEL_SOFTDIST_PRECACHE
  121. && dwPolicy != URLPOLICY_CHANNEL_SOFTDIST_AUTOINSTALL)
  122. {
  123. hr = E_INVALIDARG;
  124. goto Exit;
  125. }
  126. if (dwPolicy == URLPOLICY_CHANNEL_SOFTDIST_PROHIBIT)
  127. {
  128. hr = E_ACCESSDENIED;
  129. goto Exit;
  130. }
  131. hr = m_pSoftDistExt->ProcessSoftDist(m_szCDF, m_pSoftDistElement, &m_sdi);
  132. if (m_sdi.dwFlags & SOFTDIST_FLAG_DELETE_SUBSCRIPTION) {
  133. ISubscriptionMgr *pSubMgr = NULL;
  134. hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubMgr);
  135. if (SUCCEEDED(hr))
  136. {
  137. hr = pSubMgr->DeleteSubscription(m_szURL,NULL);
  138. pSubMgr->Release();
  139. }
  140. hr = S_FALSE;
  141. }
  142. // Send email & update software?
  143. if (hr == S_OK) {
  144. if (m_sdi.dwFlags) {
  145. m_bSendEmail = TRUE;
  146. } else {
  147. // no usage flag and no restriction implies no email.
  148. m_bSendEmail = FALSE;
  149. }
  150. if (m_sdi.dwFlags & SOFTDIST_FLAG_USAGE_AUTOINSTALL) {
  151. m_bAcceptSoftware = (dwPolicy == URLPOLICY_CHANNEL_SOFTDIST_AUTOINSTALL) ? TRUE : FALSE;
  152. m_bSilentMode = FALSE;
  153. } else if (m_sdi.dwFlags & SOFTDIST_FLAG_USAGE_PRECACHE) {
  154. // to get here, we must have precache or autoinstall policy permissions
  155. m_bAcceptSoftware = TRUE;
  156. } else {
  157. m_bAcceptSoftware = FALSE;
  158. }
  159. } else {
  160. m_bSendEmail = FALSE;
  161. m_bAcceptSoftware = FALSE;
  162. bCleanUpNow = TRUE;
  163. }
  164. // Do only code download from here on.
  165. if (!m_bAcceptSoftware ||
  166. !((m_dwChannelFlags & CHANNEL_AGENT_PRECACHE_SOME) ||
  167. (m_dwChannelFlags & CHANNEL_AGENT_PRECACHE_ALL)) ) {
  168. // No caching allowed, return immediately.
  169. bCleanUpNow = TRUE;
  170. goto Exit;
  171. } else {
  172. if (m_dwChannelFlags & CHANNEL_AGENT_PRECACHE_ALL) {
  173. m_dwMaxSizeKB = 0;
  174. }
  175. }
  176. }
  177. m_pCCDLAgentBSC = new CDLAgentBSC(this, m_dwMaxSizeKB, m_bSilentMode, m_szCDF);
  178. if (m_pCCDLAgentBSC == NULL) {
  179. hr = E_OUTOFMEMORY;
  180. goto Exit;
  181. }
  182. // attempt to use AsyncInstallDistributionUnit
  183. hr = CreateBindCtx(0, &pbc);
  184. if (FAILED(hr)) {
  185. goto Exit;
  186. }
  187. hr = RegisterBindStatusCallback(pbc, m_pCCDLAgentBSC, NULL, 0);
  188. if (FAILED(hr)) {
  189. goto Exit;
  190. }
  191. if (m_pSoftDistElement != NULL) {
  192. hr = m_pSoftDistExt->AsyncInstallDistributionUnit(pbc, NULL, 0, NULL);
  193. if (hr == S_OK) {
  194. SendUpdateNone();
  195. }
  196. } else {
  197. CODEBASEHOLD *pcbh = new CODEBASEHOLD;
  198. if (pcbh == NULL) {
  199. hr = E_OUTOFMEMORY;
  200. goto Exit;
  201. }
  202. pcbh->cbSize = sizeof(CODEBASEHOLD);
  203. pcbh->szDistUnit = m_szDistUnit;
  204. pcbh->szCodeBase = m_szURL;
  205. pcbh->dwVersionMS = m_dwVersionMS;
  206. pcbh->dwVersionLS = m_dwVersionLS;
  207. pcbh->dwStyle = 0;
  208. // Since notification is likely from web crawler and we only support MSICD we
  209. // don't fire a notification back.
  210. hr = m_pSoftDistExt->AsyncInstallDistributionUnit(pbc, NULL, 0, pcbh);
  211. if (hr == S_OK) {
  212. SendUpdateNone();
  213. }
  214. SAFEDELETE(pcbh);
  215. goto Exit;
  216. }
  217. if (hr != E_NOTIMPL) {
  218. // May have succeeded or failed, either way, we are out of here.
  219. goto Exit;
  220. }
  221. hr = m_pSoftDistExt->GetFirstCodeBase(&szCodeBase, &dwSize);
  222. if (SUCCEEDED(hr) && szCodeBase) {
  223. hr = StartNextDownload(szCodeBase,dwSize);
  224. SAFEDELETE(szCodeBase);
  225. } else {
  226. // no CODEBASE, return OK
  227. bCleanUpNow = TRUE;
  228. hr = S_OK;
  229. }
  230. Exit:
  231. // In case of SOFTDIST tag we work asychronously and send an END_REPORT back immediately. If we were called
  232. // to install a particular CAB then CleanUp is called by CDLABSC::OnStopBinding and report is sent back then.
  233. SAFERELEASE(pbc);
  234. if (FAILED(hr) || bCleanUpNow)
  235. {
  236. SetEndStatus(hr);
  237. CleanUp();
  238. }
  239. return hr;
  240. }
  241. HRESULT CCDLAgent::StartNextDownload(LPWSTR wzCodeBase, DWORD dwSize)
  242. {
  243. HRESULT hr = E_FAIL;
  244. DWORD dwTemp = 0;
  245. ISubscriptionItem *pItem;
  246. if (m_dwMaxSizeKB && (dwSize > m_dwMaxSizeKB))
  247. {
  248. hr = INET_E_AGENT_MAX_SIZE_EXCEEDED;
  249. goto Exit;
  250. }
  251. else
  252. {
  253. // Any other type of INSTALL protocol.
  254. // Send notification to WebCrawl agent to crawl the codebase. This should force it in the
  255. // case. Only do this if there is any chance the DL will not overflow the cache.
  256. // Note this will only download the CAB file and not any dependencies inside the CAB. They
  257. // should be included as separate CONFIG entries.
  258. if (m_dwMaxSizeKB && ((m_dwCurSize>>10) > m_dwMaxSizeKB))
  259. {
  260. // We've exceeded our maximum download KB limit and can't continue.
  261. hr = INET_E_AGENT_MAX_SIZE_EXCEEDED;
  262. goto Exit;
  263. }
  264. if (FAILED(hr = DoCloneSubscriptionItem(m_pSubscriptionItem, NULL, &pItem)) || !pItem)
  265. {
  266. goto Exit;
  267. }
  268. dwTemp = DELIVERY_AGENT_FLAG_NO_BROADCAST;
  269. WriteDWORD(pItem, c_szPropAgentFlags, dwTemp);
  270. WriteOLESTR(pItem, c_szPropURL, wzCodeBase);
  271. if (m_dwMaxSizeKB)
  272. {
  273. // KB limit for us to pull.
  274. WriteDWORD(pItem, c_szPropCrawlMaxSize, m_dwMaxSizeKB - (m_dwCurSize>>10));
  275. }
  276. WriteDWORD(pItem, c_szPropCrawlLevels, 0);
  277. m_dwCurSize += dwSize;
  278. m_pAgent = new CRunDeliveryAgent();
  279. if (m_pAgent)
  280. hr = m_pAgent->Init((CRunDeliveryAgentSink *)this, pItem, CLSID_WebCrawlerAgent);
  281. pItem->Release();
  282. if (m_pAgent && SUCCEEDED(hr))
  283. {
  284. hr = m_pAgent->StartAgent();
  285. if (hr == E_PENDING)
  286. {
  287. hr = S_OK;
  288. }
  289. else
  290. {
  291. DBG_WARN("StartNextDownload in CDL agent failed!");
  292. hr = E_FAIL;
  293. }
  294. }
  295. else
  296. {
  297. hr = E_OUTOFMEMORY;
  298. }
  299. }
  300. Exit:
  301. return hr;
  302. }
  303. HRESULT CCDLAgent::OnAgentEnd(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
  304. long lSizeDownloaded, HRESULT hrResult, LPCWSTR wszResult,
  305. BOOL fSynchronous)
  306. {
  307. HRESULT hr = S_OK;
  308. BOOL fDone = FALSE;
  309. LPWSTR wzCodeBase = NULL;
  310. DWORD dwSize;
  311. ASSERT(m_pAgent != NULL);
  312. if (fSynchronous)
  313. {
  314. // We must have failed. Let StartNextDownload return failure.
  315. return S_OK;
  316. }
  317. CRunDeliveryAgent::SafeRelease(m_pAgent);
  318. if (SUCCEEDED(hrResult))
  319. {
  320. hr = m_pSoftDistExt->GetNextCodeBase(&wzCodeBase, &dwSize);
  321. if (SUCCEEDED(hr) && wzCodeBase)
  322. {
  323. hr = StartNextDownload(wzCodeBase, dwSize);
  324. SAFEDELETE(wzCodeBase);
  325. if (FAILED(hr)) {
  326. // we are done
  327. fDone = TRUE;
  328. }
  329. } else {
  330. // no more codebases to crawl
  331. hr = S_OK;
  332. fDone = TRUE;
  333. }
  334. }
  335. else
  336. {
  337. hr = hrResult;
  338. fDone = TRUE;
  339. }
  340. if (fDone) {
  341. SetEndStatus(hr);
  342. CleanUp();
  343. }
  344. return hr;
  345. }
  346. void CCDLAgent::CleanUp()
  347. {
  348. if (m_pCCDLAgentBSC != NULL) {
  349. m_pCCDLAgentBSC->Release();
  350. }
  351. m_pCCDLAgentBSC = NULL;
  352. CDeliveryAgent::CleanUp();
  353. }
  354. void CCDLAgent::SetErrorEndText(LPCWSTR szErrorText)
  355. {
  356. if (szErrorText)
  357. m_szErrorText = SysAllocString(szErrorText);
  358. }
  359. HRESULT CCDLAgent::AgentAbort(DWORD dwFlags)
  360. {
  361. HRESULT hr = S_OK;
  362. if (m_pCCDLAgentBSC != NULL )
  363. {
  364. hr = m_pCCDLAgentBSC->Abort();
  365. }
  366. return hr;
  367. }
  368. HRESULT CCDLAgent::AgentPause(DWORD dwFlags)
  369. {
  370. HRESULT hr = S_OK;
  371. if (m_pCCDLAgentBSC != NULL )
  372. {
  373. hr = m_pCCDLAgentBSC->Pause();
  374. }
  375. return hr;
  376. }
  377. HRESULT CCDLAgent::AgentResume(DWORD dwFlags)
  378. {
  379. HRESULT hr = S_OK;
  380. if (m_pCCDLAgentBSC != NULL )
  381. {
  382. hr = m_pCCDLAgentBSC->Resume();
  383. }
  384. return hr;
  385. }
  386. HRESULT CCDLAgent::ModifyUpdateEnd(ISubscriptionItem *pEndItem, UINT *puiRes)
  387. {
  388. VARIANT vHref;
  389. ASSERT(pEndItem);
  390. // The END_REPORT is sent for both functionalities of CDL agent (SOFTDIST and Pull single CAB).
  391. // customize our end status string
  392. switch (GetEndStatus())
  393. {
  394. case E_OUTOFMEMORY : *puiRes = IDS_AGNT_STATUS_SIZELIMIT; break;
  395. case E_FAIL : *puiRes = IDS_CRAWL_STATUS_NOT_OK; break;
  396. case S_FALSE : *puiRes = IDS_CRAWL_STATUS_UNCHANGED; break;
  397. case INET_S_AGENT_PART_FAIL : *puiRes = IDS_CRAWL_STATUS_MOSTLYOK; break;
  398. // This is actually a success code from URLMON
  399. case HRESULT_FROM_WIN32(ERROR_CANCELLED)
  400. : SetEndStatus(S_OK);
  401. *puiRes = IDS_CRAWL_STATUS_OK; break;
  402. case TRUST_E_FAIL : SetEndStatus(TRUST_E_SUBJECT_NOT_TRUSTED);
  403. case TRUST_E_SUBJECT_NOT_TRUSTED :
  404. case HRESULT_FROM_WIN32(ERROR_IO_INCOMPLETE) : SetEndStatus(S_OK);
  405. // fall through
  406. case S_OK : *puiRes = IDS_CRAWL_STATUS_OK; break;
  407. default : *puiRes = IDS_CRAWL_STATUS_NOT_OK; break;
  408. break;
  409. }
  410. // force gleam on this channel if we got S_OK on precaching bits
  411. if (SUCCEEDED(GetEndStatus()) && (GetEndStatus() != S_FALSE)) {
  412. WriteDWORD(pEndItem, c_szPropEnableShortcutGleam, 1);
  413. }
  414. // If we are sending email the status must be S_OK, we incorporate the error
  415. // message into the text body for reporting.
  416. if (m_bSendEmail) {
  417. VariantInit(&vHref);
  418. WriteDWORD(pEndItem, c_szPropEmailFlags, MAILAGENT_FLAG_CUSTOM_MSG);
  419. // This must exist or m_bSendEmail would never have been set in first place.
  420. GetXMLAttribute(m_pSoftDistElement, L"HREF", &vHref);
  421. WriteOLESTR(pEndItem, c_szPropURL, vHref.bstrVal);
  422. VariantClear(&vHref);
  423. if (m_sdi.szTitle) {
  424. BSTR bstrTitle = SysAllocString(m_sdi.szTitle);
  425. if (bstrTitle)
  426. WriteOLESTR(pEndItem, c_szPropEmailTitle, m_sdi.szTitle);
  427. SAFEFREEBSTR(bstrTitle);
  428. }
  429. if (FAILED(GetEndStatus()) && !m_szErrorText) {
  430. m_szErrorText = GetErrorMessage(GetEndStatus());
  431. }
  432. if (m_sdi.szAbstract) {
  433. BSTR bstrAbstract = SysAllocString(m_sdi.szAbstract);
  434. if (bstrAbstract != NULL) {
  435. if (m_szErrorText) {
  436. //This is wrecking havoc with the email message, some resource strings
  437. //have a 'CR/LF' tacked on the end. We kill any that exist.
  438. DWORD dwLen = lstrlenW(m_szErrorText)-1;
  439. while (dwLen > 0 &&
  440. (m_szErrorText[dwLen] == 0x0a
  441. || m_szErrorText[dwLen] == 0x0d
  442. || m_szErrorText[dwLen] == L'.'))
  443. {
  444. m_szErrorText[dwLen] = L'\0';
  445. dwLen--;
  446. }
  447. // BUGBUG - needs cleanup!
  448. CHAR szPrefixMsg[MAX_PATH], szFormattedPrefixMsg[MAX_PATH*2];
  449. if (MLLoadStringA(IDS_CDLAGENT_ERROR_EMAIL, szPrefixMsg, ARRAYSIZE(szPrefixMsg))>0) {
  450. LPWSTR wszNewAbstract = NULL;
  451. LPSTR szNewAbstract = NULL;
  452. wnsprintfA(szFormattedPrefixMsg,
  453. ARRAYSIZE(szFormattedPrefixMsg),
  454. szPrefixMsg,
  455. m_szErrorText);
  456. DWORD dwNewLen = lstrlenA(szFormattedPrefixMsg) + lstrlenW(bstrAbstract) + 4;
  457. szNewAbstract = (LPSTR)LocalAlloc(0,dwNewLen*sizeof(CHAR));
  458. if (szNewAbstract) {
  459. wnsprintfA(szNewAbstract,
  460. dwNewLen*sizeof(CHAR),
  461. "%s%ws",
  462. szFormattedPrefixMsg,
  463. bstrAbstract);
  464. dwNewLen = lstrlenA(szNewAbstract) + 1;
  465. wszNewAbstract = (LPWSTR)LocalAlloc(0,dwNewLen*sizeof(WCHAR));
  466. if (wszNewAbstract &&
  467. (MultiByteToWideChar(CP_ACP, 0, szNewAbstract, -1, wszNewAbstract, dwNewLen)>0)) {
  468. SAFEFREEBSTR(bstrAbstract);
  469. bstrAbstract = SysAllocString(wszNewAbstract);
  470. }
  471. if (wszNewAbstract)
  472. LocalFree(wszNewAbstract);
  473. LocalFree(szNewAbstract);
  474. }
  475. }
  476. }
  477. WriteOLESTR(pEndItem, c_szPropEmailAbstract, bstrAbstract);
  478. SAFEFREEBSTR(bstrAbstract);
  479. }
  480. }
  481. // because user is notified of error we don't pass it on anywhere else
  482. SetEndStatus(S_OK);
  483. WriteSCODE(pEndItem, c_szPropStatusCode, S_OK);
  484. }
  485. ClearAgentFlag(DELIVERY_AGENT_FLAG_NO_BROADCAST);
  486. return CDeliveryAgent::ModifyUpdateEnd(pEndItem, puiRes);
  487. }
  488. LPWSTR CCDLAgent::GetErrorMessage(HRESULT hr)
  489. {
  490. LPSTR szBuf = NULL;
  491. LPWSTR wszBuf = NULL;
  492. DWORD dwLen;
  493. DWORD dwResource = 0;
  494. if (SUCCEEDED(hr))
  495. return NULL;
  496. dwLen = FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
  497. hr, 0, (LPTSTR)&szBuf, 0, NULL);
  498. if (!dwLen) {
  499. // NOTE: If out of memory we return NULL.
  500. if (SUCCEEDED(hr))
  501. dwResource = IDS_CDLAGENT_SUCCESS;
  502. else if (hr == TRUST_E_SUBJECT_NOT_TRUSTED)
  503. dwResource = IDS_CDLAGENT_TRUST_ERROR;
  504. else
  505. dwResource = IDS_CDLAGENT_FAILURE;
  506. // We know strings will fit into max_path
  507. WCHAR szTmp[MAX_PATH];
  508. if (MLLoadStringW(dwResource, szTmp, MAX_PATH)>0) {
  509. wszBuf = SysAllocString(szTmp);
  510. }
  511. } else {
  512. WCHAR wszTemp[MAX_PATH];
  513. if (MultiByteToWideChar(CP_ACP, 0, szBuf, -1, wszTemp, ARRAYSIZE(wszTemp))>0) {
  514. wszBuf = SysAllocString(wszTemp);
  515. } else
  516. wszBuf = NULL;
  517. SAFEDELETE(szBuf);
  518. }
  519. return wszBuf;
  520. }