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.

1013 lines
20 KiB

  1. // Copyright (c) 1997-2001 Microsoft Corporation
  2. //
  3. // File: common.cpp
  4. //
  5. // Synopsis: Commonly used functions
  6. //
  7. // History: 02/03/2001 JeffJon Created
  8. #include "pch.h"
  9. #include "resource.h"
  10. #include <ciodm.h>
  11. // Creates the fonts for setLargeFonts().
  12. //
  13. // hDialog - handle to a dialog to be used to retrieve a device
  14. // context.
  15. //
  16. // bigBoldFont - receives the handle of the big bold font created.
  17. void
  18. InitFonts(
  19. HWND hDialog,
  20. HFONT& bigBoldFont)
  21. {
  22. ASSERT(Win::IsWindow(hDialog));
  23. HRESULT hr = S_OK;
  24. do
  25. {
  26. NONCLIENTMETRICS ncm;
  27. memset(&ncm, 0, sizeof(ncm));
  28. ncm.cbSize = sizeof(ncm);
  29. hr = Win::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  30. BREAK_ON_FAILED_HRESULT(hr);
  31. LOGFONT bigBoldLogFont = ncm.lfMessageFont;
  32. bigBoldLogFont.lfWeight = FW_BOLD;
  33. String fontName = String::load(IDS_BIG_BOLD_FONT_NAME);
  34. // ensure null termination 260237
  35. memset(bigBoldLogFont.lfFaceName, 0, LF_FACESIZE * sizeof(TCHAR));
  36. size_t fnLen = fontName.length();
  37. fontName.copy(
  38. bigBoldLogFont.lfFaceName,
  39. // don't copy over the last null
  40. min(LF_FACESIZE - 1, fnLen));
  41. unsigned fontSize = 0;
  42. String::load(IDS_BIG_BOLD_FONT_SIZE).convert(fontSize);
  43. ASSERT(fontSize);
  44. HDC hdc = 0;
  45. hr = Win::GetDC(hDialog, hdc);
  46. BREAK_ON_FAILED_HRESULT(hr);
  47. bigBoldLogFont.lfHeight =
  48. - ::MulDiv(
  49. static_cast<int>(fontSize),
  50. Win::GetDeviceCaps(hdc, LOGPIXELSY),
  51. 72);
  52. hr = Win::CreateFontIndirect(bigBoldLogFont, bigBoldFont);
  53. BREAK_ON_FAILED_HRESULT(hr);
  54. Win::ReleaseDC(hDialog, hdc);
  55. }
  56. while (0);
  57. }
  58. void
  59. SetControlFont(HWND parentDialog, int controlID, HFONT font)
  60. {
  61. ASSERT(Win::IsWindow(parentDialog));
  62. ASSERT(controlID);
  63. ASSERT(font);
  64. HWND control = Win::GetDlgItem(parentDialog, controlID);
  65. if (control)
  66. {
  67. Win::SetWindowFont(control, font, true);
  68. }
  69. }
  70. void
  71. SetLargeFont(HWND dialog, int bigBoldResID)
  72. {
  73. ASSERT(Win::IsWindow(dialog));
  74. ASSERT(bigBoldResID);
  75. static HFONT bigBoldFont = 0;
  76. if (!bigBoldFont)
  77. {
  78. InitFonts(dialog, bigBoldFont);
  79. }
  80. SetControlFont(dialog, bigBoldResID, bigBoldFont);
  81. }
  82. bool
  83. IsServiceInstalledHelper(const wchar_t* serviceName)
  84. {
  85. LOG_FUNCTION2(IsServiceInstalledHelper, serviceName);
  86. ASSERT(serviceName);
  87. // if we can open the service, then it is installed
  88. bool result = false;
  89. SC_HANDLE hsc =
  90. ::OpenSCManager(0, SERVICES_ACTIVE_DATABASE, GENERIC_READ);
  91. if (hsc)
  92. {
  93. SC_HANDLE hs = ::OpenServiceW(hsc, serviceName, GENERIC_READ);
  94. if (hs)
  95. {
  96. ::CloseServiceHandle(hs);
  97. result = true;
  98. }
  99. ::CloseServiceHandle(hsc);
  100. }
  101. return result;
  102. }
  103. // Wait for a handle to become signalled, or a timeout to expire, or WM_QUIT
  104. // to appear in the message queue. Pump the message queue while we wait.
  105. //
  106. // WARNING: UI should diable itself before calling any function that invokes
  107. // this function, or functions calling this one should guard against
  108. // re-entrance. Otherwise there will be a re-entrancy problem.
  109. //
  110. // e.g. command handler gets button clicked message, calls a func that calls
  111. // this wait function, then user clicks the button again, command handler call
  112. // a func that calls this one, and so on.
  113. DWORD
  114. MyWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
  115. {
  116. LOG_FUNCTION(MyWaitForSendMessageThread);
  117. ASSERT(hThread);
  118. MSG msg;
  119. DWORD dwRet;
  120. DWORD dwEnd = GetTickCount() + dwTimeout;
  121. bool quit = false;
  122. // We will attempt to wait up to dwTimeout for the thread to
  123. // terminate
  124. do
  125. {
  126. dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
  127. dwTimeout, QS_ALLEVENTS | QS_SENDMESSAGE );
  128. if (dwRet == (WAIT_OBJECT_0 + 1))
  129. {
  130. // empty out the message queue. We call DispatchMessage to
  131. // ensure that we still process the WM_PAINT messages.
  132. // DANGER: Make sure that the CYS UI is completely disabled
  133. // or there will be re-entrancy problems here
  134. while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  135. {
  136. if (msg.message == WM_QUIT)
  137. {
  138. // Need to re-post this so that we know to close CYS
  139. ::PostMessage(msg.hwnd, WM_QUIT, 0, 0);
  140. quit = true;
  141. break;
  142. }
  143. ::TranslateMessage(&msg);
  144. ::DispatchMessage(&msg);
  145. }
  146. // Calculate if we have any more time left in the timeout to
  147. // wait on.
  148. if (dwTimeout != INFINITE)
  149. {
  150. dwTimeout = dwEnd - GetTickCount();
  151. if ((long)dwTimeout <= 0)
  152. {
  153. // No more time left, fail with WAIT_TIMEOUT
  154. dwRet = WAIT_TIMEOUT;
  155. }
  156. }
  157. }
  158. // dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED
  159. // The thread must have exited, so we are happy
  160. //
  161. // dwRet == WAIT_TIMEOUT
  162. // The thread is taking too long to finish, so just
  163. // return and let the caller kill it
  164. } while (dwRet == (WAIT_OBJECT_0 + 1) && !quit);
  165. return(dwRet);
  166. }
  167. HRESULT
  168. CreateAndWaitForProcess(const String& commandLine, DWORD& exitCode)
  169. {
  170. LOG_FUNCTION2(CreateAndWaitForProcess, commandLine);
  171. ASSERT(!commandLine.empty());
  172. exitCode = 0;
  173. HRESULT hr = S_OK;
  174. do
  175. {
  176. PROCESS_INFORMATION procInfo;
  177. memset(&procInfo, 0, sizeof(procInfo));
  178. STARTUPINFO startup;
  179. memset(&startup, 0, sizeof(startup));
  180. String commandLine2(commandLine);
  181. LOG(L"Calling CreateProcess");
  182. LOG(commandLine2);
  183. hr =
  184. Win::CreateProcess(
  185. commandLine2,
  186. 0,
  187. 0,
  188. false,
  189. 0,
  190. 0,
  191. String(),
  192. startup,
  193. procInfo);
  194. BREAK_ON_FAILED_HRESULT(hr);
  195. ASSERT(procInfo.hProcess);
  196. DWORD dwRet = MyWaitForSendMessageThread(procInfo.hProcess, INFINITE);
  197. ASSERT(dwRet == WAIT_OBJECT_0);
  198. hr = Win::GetExitCodeProcess(procInfo.hProcess, exitCode);
  199. BREAK_ON_FAILED_HRESULT(hr);
  200. Win::CloseHandle(procInfo.hThread);
  201. Win::CloseHandle(procInfo.hProcess);
  202. }
  203. while (0);
  204. LOG(String::format(L"exit code = %1!x!", exitCode));
  205. LOG_HRESULT(hr);
  206. return hr;
  207. }
  208. HRESULT
  209. MyCreateProcess(const String& commandLine)
  210. {
  211. LOG_FUNCTION2(MyCreateProcess, commandLine);
  212. ASSERT(!commandLine.empty());
  213. HRESULT hr = S_OK;
  214. do
  215. {
  216. PROCESS_INFORMATION procInfo;
  217. memset(&procInfo, 0, sizeof(procInfo));
  218. STARTUPINFO startup;
  219. memset(&startup, 0, sizeof(startup));
  220. String commandLine2(commandLine);
  221. LOG(L"Calling CreateProcess");
  222. LOG(commandLine2);
  223. hr =
  224. Win::CreateProcess(
  225. commandLine2,
  226. 0,
  227. 0,
  228. false,
  229. 0,
  230. 0,
  231. String(),
  232. startup,
  233. procInfo);
  234. BREAK_ON_FAILED_HRESULT(hr);
  235. ASSERT(procInfo.hProcess);
  236. Win::CloseHandle(procInfo.hThread);
  237. Win::CloseHandle(procInfo.hProcess);
  238. }
  239. while (0);
  240. LOG_HRESULT(hr);
  241. return hr;
  242. }
  243. bool
  244. IsKeyValuePresent(RegistryKey& key, const String& valueKey)
  245. {
  246. LOG_FUNCTION(IsKeyValuePresent);
  247. bool result = false;
  248. do
  249. {
  250. String value;
  251. HRESULT hr = key.GetValue(valueKey, value);
  252. if (FAILED(hr))
  253. {
  254. LOG(String::format(
  255. L"Failed to read regkey %1 because: hr = %2!x!",
  256. valueKey,
  257. hr));
  258. break;
  259. }
  260. if (!value.empty())
  261. {
  262. result = true;
  263. break;
  264. }
  265. } while (false);
  266. LOG_BOOL(result);
  267. return result;
  268. }
  269. bool
  270. GetRegKeyValue(
  271. const String& keyName,
  272. const String& value,
  273. String& resultString,
  274. HKEY parentKey)
  275. {
  276. LOG_FUNCTION(GetRegKeyValue);
  277. bool result = true;
  278. do
  279. {
  280. HRESULT hr = S_OK;
  281. RegistryKey key;
  282. hr = key.Open(parentKey, keyName);
  283. if (FAILED(hr))
  284. {
  285. LOG(String::format(
  286. L"Failed to open regkey %1 because: hr = %2!x!",
  287. keyName.c_str(),
  288. hr));
  289. result = false;
  290. break;
  291. }
  292. hr = key.GetValue(value, resultString);
  293. if (FAILED(hr))
  294. {
  295. LOG(String::format(
  296. L"Failed to read regkey %1 because: hr = %2!x!",
  297. value.c_str(),
  298. hr));
  299. result = false;
  300. break;
  301. }
  302. LOG(String::format(
  303. L"Value of key: %1",
  304. resultString.c_str()));
  305. } while (false);
  306. LOG_BOOL(result);
  307. return result;
  308. }
  309. bool
  310. GetRegKeyValue(
  311. const String& keyName,
  312. const String& value,
  313. DWORD& resultValue,
  314. HKEY parentKey)
  315. {
  316. LOG_FUNCTION(GetRegKeyValue);
  317. bool result = true;
  318. do
  319. {
  320. HRESULT hr = S_OK;
  321. RegistryKey key;
  322. hr = key.Open(parentKey, keyName);
  323. if (FAILED(hr))
  324. {
  325. LOG(String::format(
  326. L"Failed to open regkey %1 because: hr = %2!x!",
  327. keyName.c_str(),
  328. hr));
  329. result = false;
  330. break;
  331. }
  332. hr = key.GetValue(value, resultValue);
  333. if (FAILED(hr))
  334. {
  335. LOG(String::format(
  336. L"Failed to read regkey %1 because: hr = %2!x!",
  337. value.c_str(),
  338. hr));
  339. result = false;
  340. break;
  341. }
  342. LOG(String::format(
  343. L"Key value: %1!d!",
  344. resultValue));
  345. } while (false);
  346. LOG_BOOL(result);
  347. return result;
  348. }
  349. bool
  350. SetRegKeyValue(
  351. const String& keyName,
  352. const String& value,
  353. const String& newString,
  354. HKEY parentKey,
  355. bool create
  356. )
  357. {
  358. LOG_FUNCTION(SetRegKeyValue);
  359. bool result = true;
  360. do
  361. {
  362. HRESULT hr = S_OK;
  363. RegistryKey key;
  364. if (create)
  365. {
  366. hr = key.Create(parentKey, keyName);
  367. }
  368. else
  369. {
  370. hr = key.Open(parentKey, keyName, KEY_ALL_ACCESS);
  371. }
  372. if (FAILED(hr))
  373. {
  374. LOG(String::format(
  375. L"Failed to open regkey %1 because: hr = %2!x!",
  376. keyName.c_str(),
  377. hr));
  378. result = false;
  379. break;
  380. }
  381. hr = key.SetValue(value, newString);
  382. if (FAILED(hr))
  383. {
  384. LOG(String::format(
  385. L"Failed to write regkey %1 because: hr = %2!x!",
  386. value.c_str(),
  387. hr));
  388. result = false;
  389. break;
  390. }
  391. } while (false);
  392. LOG_BOOL(result);
  393. return result;
  394. }
  395. bool
  396. SetRegKeyValue(
  397. const String& keyName,
  398. const String& value,
  399. DWORD newValue,
  400. HKEY parentKey,
  401. bool create)
  402. {
  403. LOG_FUNCTION(SetRegKeyValue);
  404. bool result = true;
  405. do
  406. {
  407. HRESULT hr = S_OK;
  408. RegistryKey key;
  409. if (create)
  410. {
  411. hr = key.Create(parentKey, keyName);
  412. }
  413. else
  414. {
  415. hr = key.Open(parentKey, keyName, KEY_WRITE);
  416. }
  417. if (FAILED(hr))
  418. {
  419. LOG(String::format(
  420. L"Failed to open regkey %1 because: hr = %2!x!",
  421. keyName.c_str(),
  422. hr));
  423. result = false;
  424. break;
  425. }
  426. hr = key.SetValue(value, newValue);
  427. if (FAILED(hr))
  428. {
  429. LOG(String::format(
  430. L"Failed to write regkey %1 because: hr = %2!x!",
  431. value.c_str(),
  432. hr));
  433. result = false;
  434. break;
  435. }
  436. } while (false);
  437. LOG_BOOL(result);
  438. return result;
  439. }
  440. bool
  441. IsIndexingServiceOn()
  442. {
  443. LOG_FUNCTION(IsIndexingServiceOn);
  444. bool result = false;
  445. do
  446. {
  447. CLSID clsid;
  448. HRESULT hr = CLSIDFromProgID( L"Microsoft.ISAdm", &clsid );
  449. if (FAILED(hr))
  450. {
  451. LOG(String::format(
  452. L"Failed to get the CLSID from ProgID: hr = 0x%x",
  453. hr));
  454. break;
  455. }
  456. SmartInterface<IAdminIndexServer> adminIndexServer;
  457. hr = adminIndexServer.AcquireViaCreateInstance(
  458. clsid,
  459. 0,
  460. CLSCTX_INPROC_SERVER);
  461. if (FAILED(hr))
  462. {
  463. LOG(String::format(
  464. L"Failed to CoCreateInstance of IAdminIndexServer: hr = 0x%x",
  465. hr));
  466. break;
  467. }
  468. VARIANT_BOOL var;
  469. hr = adminIndexServer->IsRunning(&var);
  470. if (FAILED(hr))
  471. {
  472. LOG(String::format(
  473. L"Failed to get running state: hr = 0x%x",
  474. hr));
  475. break;
  476. }
  477. result = var ? true : false;
  478. } while (false);
  479. LOG_BOOL(result);
  480. return result;
  481. }
  482. HRESULT
  483. ModifyIndexingService(bool turnOn)
  484. {
  485. LOG_FUNCTION2(
  486. ModifyIndexingService,
  487. turnOn ? L"true" : L"false");
  488. HRESULT hr = S_OK;
  489. do
  490. {
  491. CLSID clsid;
  492. hr = CLSIDFromProgID( L"Microsoft.ISAdm", &clsid );
  493. if (FAILED(hr))
  494. {
  495. LOG(String::format(
  496. L"Failed to get the CLSID from ProgID: hr = 0x%x",
  497. hr));
  498. break;
  499. }
  500. SmartInterface<IAdminIndexServer> adminIndexServer;
  501. hr = adminIndexServer.AcquireViaCreateInstance(
  502. clsid,
  503. 0,
  504. CLSCTX_INPROC_SERVER);
  505. if (FAILED(hr))
  506. {
  507. LOG(String::format(
  508. L"Failed to CoCreateInstance of IAdminIndexServer: hr = 0x%x",
  509. hr));
  510. break;
  511. }
  512. if (turnOn)
  513. {
  514. hr = adminIndexServer->Start();
  515. }
  516. else
  517. {
  518. hr = adminIndexServer->Stop();
  519. }
  520. if (FAILED(hr))
  521. {
  522. LOG(String::format(
  523. L"Failed to start or stop indexing service: hr = 0x%x",
  524. hr));
  525. break;
  526. }
  527. } while (false);
  528. LOG(String::format(L"hr = %1!x!", hr));
  529. return hr;
  530. }
  531. HRESULT
  532. StartIndexingService()
  533. {
  534. return ModifyIndexingService(true);
  535. }
  536. HRESULT
  537. StopIndexingService()
  538. {
  539. return ModifyIndexingService(false);
  540. }
  541. // return true if the name is a reserved name, false otherwise. If true, also
  542. // set message to an error message describing the problem.
  543. bool
  544. IsReservedDnsName(const String& dnsName, String& message)
  545. {
  546. LOG_FUNCTION2(IsReservedDnsName, dnsName);
  547. ASSERT(!dnsName.empty());
  548. message.erase();
  549. bool result = false;
  550. // We're still trying to decide if we should restrict these names
  551. //
  552. // // names with these as the last labels are illegal.
  553. //
  554. // static const String RESERVED[] =
  555. // {
  556. // L"in-addr.arpa",
  557. // L"ipv6.int",
  558. //
  559. // // RFC 2606 documents these:
  560. //
  561. // L"test",
  562. // L"example",
  563. // L"invalid",
  564. // L"localhost",
  565. // L"example.com",
  566. // L"example.org",
  567. // L"example.net"
  568. // };
  569. //
  570. // String name(dnsName);
  571. // name.to_upper();
  572. // if (name[name.length() - 1] == L'.')
  573. // {
  574. // // remove the trailing dot
  575. //
  576. // name.resize(name.length() - 1);
  577. // }
  578. //
  579. // for (int i = 0; i < sizeof(RESERVED) / sizeof(String); ++i)
  580. // {
  581. // String res = RESERVED[i];
  582. // res.to_upper();
  583. //
  584. // size_t pos = name.rfind(res);
  585. //
  586. // if (pos == String::npos)
  587. // {
  588. // continue;
  589. // }
  590. //
  591. // if (pos == 0 && name.length() == res.length())
  592. // {
  593. // ASSERT(name == res);
  594. //
  595. // result = true;
  596. // message =
  597. // String::format(
  598. // IDS_RESERVED_NAME,
  599. // dnsName.c_str());
  600. // break;
  601. // }
  602. //
  603. // if ((pos == name.length() - res.length()) && (name[pos - 1] == L'.'))
  604. // {
  605. // // the name has reserved as a suffix.
  606. //
  607. // result = true;
  608. // message =
  609. // String::format(
  610. // IDS_RESERVED_NAME_SUFFIX,
  611. // dnsName.c_str(),
  612. // RESERVED[i].c_str());
  613. // break;
  614. // }
  615. // }
  616. return result;
  617. }
  618. bool
  619. ValidateDomainDnsNameSyntax(
  620. HWND dialog,
  621. int editResID,
  622. bool warnOnNonRFC,
  623. bool* isNonRFC)
  624. {
  625. return
  626. ValidateDomainDnsNameSyntax(
  627. dialog,
  628. String(),
  629. editResID,
  630. warnOnNonRFC,
  631. isNonRFC);
  632. }
  633. bool
  634. ValidateDomainDnsNameSyntax(
  635. HWND dialog,
  636. const String& domainName,
  637. int editResID,
  638. bool warnOnNonRFC,
  639. bool* isNonRFC)
  640. {
  641. LOG_FUNCTION(ValidateDomainDnsNameSyntax);
  642. ASSERT(Win::IsWindow(dialog));
  643. ASSERT(editResID > 0);
  644. bool valid = false;
  645. String message;
  646. String dnsName =
  647. domainName.empty()
  648. ? Win::GetTrimmedDlgItemText(dialog, editResID)
  649. : domainName;
  650. if (isNonRFC)
  651. {
  652. *isNonRFC = false;
  653. }
  654. LOG(L"validating " + dnsName);
  655. switch (
  656. Dns::ValidateDnsNameSyntax(
  657. dnsName,
  658. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY_UTF8) )
  659. {
  660. case Dns::NON_RFC:
  661. {
  662. if (isNonRFC)
  663. {
  664. *isNonRFC = true;
  665. }
  666. if (warnOnNonRFC)
  667. {
  668. // warn about non-rfc names
  669. String msg = String::format(IDS_NON_RFC_NAME, dnsName.c_str());
  670. popup.Info(
  671. dialog,
  672. msg);
  673. LOG(msg);
  674. }
  675. // fall through
  676. //lint -e616 allow fall thru
  677. }
  678. case Dns::VALID:
  679. {
  680. valid = !IsReservedDnsName(dnsName, message);
  681. break;
  682. }
  683. case Dns::TOO_LONG:
  684. {
  685. message =
  686. String::format(
  687. IDS_DNS_NAME_TOO_LONG,
  688. dnsName.c_str(),
  689. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY,
  690. DNS_DOMAIN_NAME_MAX_LIMIT_DUE_TO_POLICY_UTF8);
  691. break;
  692. }
  693. case Dns::NUMERIC:
  694. case Dns::BAD_CHARS:
  695. case Dns::INVALID:
  696. default:
  697. {
  698. message =
  699. String::format(
  700. IDS_BAD_DNS_SYNTAX,
  701. dnsName.c_str(),
  702. Dns::MAX_LABEL_LENGTH);
  703. break;
  704. }
  705. }
  706. if (!valid)
  707. {
  708. popup.Gripe(dialog, editResID, message);
  709. }
  710. return valid;
  711. }
  712. bool
  713. ConfirmNetbiosLookingNameIsReallyDnsName(HWND parentDialog, int editResID)
  714. {
  715. ASSERT(Win::IsWindow(parentDialog));
  716. ASSERT(editResID > 0);
  717. // check if the name is a single DNS label (a single label with a trailing
  718. // dot does not count. If the user is DNS-saavy enough to use an absolute
  719. // DNS name, then we will pester him no further.)
  720. String domain = Win::GetTrimmedDlgItemText(parentDialog, editResID);
  721. if (domain.find(L'.') == String::npos)
  722. {
  723. // no dot found: must be a single label
  724. if (
  725. popup.MessageBox(
  726. parentDialog,
  727. String::format(
  728. IDS_CONFIRM_NETBIOS_LOOKING_NAME,
  729. domain.c_str(),
  730. domain.c_str()),
  731. MB_YESNO) == IDNO)
  732. {
  733. // user goofed. or we frightened them.
  734. HWND edit = Win::GetDlgItem(parentDialog, editResID);
  735. Win::SendMessage(edit, EM_SETSEL, 0, -1);
  736. Win::SetFocus(edit);
  737. return false;
  738. }
  739. }
  740. return true;
  741. }
  742. HRESULT
  743. VariantArrayToStringList(VARIANT* variant, StringList& stringList)
  744. {
  745. LOG_FUNCTION(VariantArrayToStringList);
  746. ASSERT(variant);
  747. ASSERT(V_VT(variant) == (VT_ARRAY | VT_BSTR));
  748. HRESULT hr = S_OK;
  749. stringList.clear();
  750. SAFEARRAY* psa = V_ARRAY(variant);
  751. do
  752. {
  753. ASSERT(psa);
  754. ASSERT(psa != (SAFEARRAY*)-1);
  755. if (!psa or psa == (SAFEARRAY*)-1)
  756. {
  757. LOG(L"variant not safe array");
  758. break;
  759. }
  760. if (::SafeArrayGetDim(psa) != 1)
  761. {
  762. LOG(L"safe array: wrong number of dimensions");
  763. break;
  764. }
  765. VARTYPE vt = VT_EMPTY;
  766. hr = ::SafeArrayGetVartype(psa, &vt);
  767. if (FAILED(hr) || vt != VT_BSTR)
  768. {
  769. LOG(L"safe array: wrong element type");
  770. break;
  771. }
  772. long lower = 0;
  773. long upper = 0;
  774. hr = ::SafeArrayGetLBound(psa, 1, &lower);
  775. if (FAILED(hr))
  776. {
  777. LOG(L"can't get lower bound");
  778. break;
  779. }
  780. hr = ::SafeArrayGetUBound(psa, 1, &upper);
  781. if (FAILED(hr))
  782. {
  783. LOG(L"can't get upper bound");
  784. break;
  785. }
  786. for (long i = lower; i <= upper; ++i)
  787. {
  788. BSTR item;
  789. hr = ::SafeArrayGetElement(psa, &i, &item);
  790. if (FAILED(hr))
  791. {
  792. LOG(String::format(L"index %1!d! failed", i));
  793. continue;
  794. }
  795. if (item)
  796. {
  797. stringList.push_back(String(item));
  798. }
  799. ::SysFreeString(item);
  800. }
  801. }
  802. while (0);
  803. LOG_HRESULT(hr);
  804. return hr;
  805. }