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.

1554 lines
38 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 1995.
  5. //
  6. // File: util.cxx
  7. //
  8. // Contents: Misc helper functions
  9. //
  10. // History: 5-Apr-95 BruceFo Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include "headers.hxx"
  14. #pragma hdrstop
  15. #include "resource.h"
  16. #include "shares.h"
  17. #include "util.hxx"
  18. #include "shrpage.hxx"
  19. #include "dlgnew.hxx"
  20. //////////////////////////////////////////////////////////////////////////////
  21. DECLARE_INFOLEVEL(Sharing)
  22. #define NETMSG_DLL TEXT("netmsg.dll")
  23. #ifdef WIZARDS
  24. TCHAR g_szShareWizardCmd[] = TEXT("ShrPubW.exe"); // The Share Publishing Wizard
  25. TCHAR g_szShareWizardArg[] = TEXT("/folder "); // The Share Publishing Wizard
  26. TCHAR g_szSfm[] = TEXT("/sfm "); // Services For Macintosh
  27. TCHAR g_szFpnw[] = TEXT("/fpnw "); // File and Print Services for NetWare
  28. TCHAR g_szAll[] = TEXT("/all "); // all services
  29. #endif // WIZARDS
  30. //--------------------------------------------------------------------------
  31. // Globals used elsewhere
  32. UINT g_NonOLEDLLRefs = 0;
  33. HINSTANCE g_hInstance = NULL;
  34. UINT g_uiMaxUsers = 0; // max number of users based on product type
  35. WCHAR g_szAdminShare[] = L"ADMIN$";
  36. WCHAR g_szIpcShare[] = L"IPC$";
  37. UINT g_cfHIDA = 0;
  38. //////////////////////////////////////////////////////////////////////////////
  39. DWORD
  40. ConfirmStopShare(
  41. IN HWND hwnd,
  42. IN LPWSTR pszShare
  43. );
  44. NET_API_STATUS
  45. ShareConnectionInfo(
  46. IN LPWSTR pszShare,
  47. OUT LPDWORD pcConns,
  48. OUT LPDWORD pcOpens
  49. );
  50. //////////////////////////////////////////////////////////////////////////////
  51. //+-------------------------------------------------------------------------
  52. //
  53. // Function: MyFormatMessageText
  54. //
  55. // Synopsis: Given a resource IDs, load strings from given instance
  56. // and format the string into a buffer
  57. //
  58. // History: 11-Aug-93 WilliamW Created.
  59. //
  60. //--------------------------------------------------------------------------
  61. VOID
  62. MyFormatMessageText(
  63. IN HRESULT dwMsgId,
  64. IN PWSTR pszBuffer,
  65. IN DWORD dwBufferSize,
  66. IN va_list * parglist
  67. )
  68. {
  69. //
  70. // get message from system or app msg file.
  71. //
  72. DWORD dwReturn = FormatMessage(
  73. FORMAT_MESSAGE_FROM_HMODULE,
  74. g_hInstance,
  75. dwMsgId,
  76. LANG_USER_DEFAULT,
  77. pszBuffer,
  78. dwBufferSize,
  79. parglist);
  80. if (0 == dwReturn) // couldn't find message
  81. {
  82. appDebugOut((DEB_IERROR,
  83. "FormatMessage failed, 0x%08lx\n",
  84. GetLastError()));
  85. WCHAR szText[200];
  86. LoadString(g_hInstance, IDS_APP_MSG_NOT_FOUND, szText, ARRAYLEN(szText));
  87. wsprintf(pszBuffer,szText,dwMsgId);
  88. }
  89. }
  90. //+-------------------------------------------------------------------------
  91. //
  92. // Function: MyFormatMessage
  93. //
  94. // Synopsis:
  95. //
  96. // Note:
  97. //
  98. //--------------------------------------------------------------------------
  99. VOID
  100. MyFormatMessage(
  101. IN HRESULT dwMsgId,
  102. IN PWSTR pszBuffer,
  103. IN DWORD dwBufferSize,
  104. ...
  105. )
  106. {
  107. va_list arglist;
  108. va_start(arglist, dwBufferSize);
  109. MyFormatMessageText(dwMsgId, pszBuffer, dwBufferSize, &arglist);
  110. va_end(arglist);
  111. }
  112. //+-------------------------------------------------------------------------
  113. //
  114. // Function: MyCommonDialog
  115. //
  116. // Synopsis: Common popup dialog routine - stole from diskadm directory
  117. //
  118. //--------------------------------------------------------------------------
  119. DWORD
  120. MyCommonDialog(
  121. IN HWND hwnd,
  122. IN HRESULT dwMsgCode,
  123. IN PWSTR pszCaption,
  124. IN DWORD dwFlags,
  125. IN va_list arglist
  126. )
  127. {
  128. WCHAR szMsgBuf[500];
  129. MyFormatMessageText(dwMsgCode, szMsgBuf, ARRAYLEN(szMsgBuf), &arglist);
  130. return MessageBox(hwnd, szMsgBuf, pszCaption, dwFlags);
  131. }
  132. //+-------------------------------------------------------------------------
  133. //
  134. // Function: MyConfirmationDialog
  135. //
  136. // Synopsis: This routine retreives a message from the app or system
  137. // message file and displays it in a message box.
  138. //
  139. // Note: Stole from diskadm directory
  140. //
  141. //--------------------------------------------------------------------------
  142. DWORD
  143. MyConfirmationDialog(
  144. IN HWND hwnd,
  145. IN HRESULT dwMsgCode,
  146. IN DWORD dwFlags,
  147. ...
  148. )
  149. {
  150. WCHAR szCaption[100];
  151. DWORD dwReturn;
  152. va_list arglist;
  153. va_start(arglist, dwFlags);
  154. LoadString(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
  155. dwReturn = MyCommonDialog(hwnd, dwMsgCode, szCaption, dwFlags, arglist);
  156. va_end(arglist);
  157. return dwReturn;
  158. }
  159. //+-------------------------------------------------------------------------
  160. //
  161. // Function: MyErrorDialog
  162. //
  163. // Synopsis: This routine retreives a message from the app or system
  164. // message file and displays it in a message box.
  165. //
  166. // Note: Stole from diskadm directory
  167. //
  168. //--------------------------------------------------------------------------
  169. VOID
  170. MyErrorDialog(
  171. IN HWND hwnd,
  172. IN HRESULT dwErrorCode,
  173. ...
  174. )
  175. {
  176. WCHAR szCaption[100];
  177. va_list arglist;
  178. va_start(arglist, dwErrorCode);
  179. LoadString(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
  180. MyCommonDialog(hwnd, dwErrorCode, szCaption, MB_ICONSTOP | MB_OK, arglist);
  181. va_end(arglist);
  182. }
  183. //+---------------------------------------------------------------------------
  184. //
  185. // Function: NewDup
  186. //
  187. // Synopsis: Duplicate a string using '::new'
  188. //
  189. // History: 28-Dec-94 BruceFo Created
  190. //
  191. //----------------------------------------------------------------------------
  192. PWSTR
  193. NewDup(
  194. IN const WCHAR* psz
  195. )
  196. {
  197. if (NULL == psz)
  198. {
  199. appDebugOut((DEB_IERROR,"Illegal string to duplicate: NULL\n"));
  200. return NULL;
  201. }
  202. PWSTR pszRet = new WCHAR[wcslen(psz) + 1];
  203. if (NULL == pszRet)
  204. {
  205. appDebugOut((DEB_ERROR,"OUT OF MEMORY\n"));
  206. return NULL;
  207. }
  208. wcscpy(pszRet, psz);
  209. return pszRet;
  210. }
  211. //+---------------------------------------------------------------------------
  212. //
  213. // Function: GetResourceString
  214. //
  215. // Synopsis: Load a resource string, are return a "new"ed copy
  216. //
  217. // Arguments: [dwId] -- a resource string ID
  218. //
  219. // Returns: new memory copy of a string
  220. //
  221. // History: 5-Apr-95 BruceFo Created
  222. //
  223. //----------------------------------------------------------------------------
  224. PWSTR
  225. GetResourceString(
  226. IN DWORD dwId
  227. )
  228. {
  229. WCHAR sz[50];
  230. if (0 == LoadString(g_hInstance, dwId, sz, ARRAYLEN(sz)))
  231. {
  232. return NULL;
  233. }
  234. else
  235. {
  236. return NewDup(sz);
  237. }
  238. }
  239. //+-------------------------------------------------------------------------
  240. //
  241. // Member: CopySecurityDescriptor, public
  242. //
  243. // Synopsis: Copy an NT security descriptor. The security descriptor must
  244. // be in self-relative (not absolute) form. Delete the result
  245. // using LocalFree().
  246. //
  247. // History: 19-Apr-95 BruceFo Created
  248. //
  249. //--------------------------------------------------------------------------
  250. PSECURITY_DESCRIPTOR
  251. CopySecurityDescriptor(
  252. IN PSECURITY_DESCRIPTOR pSecDesc
  253. )
  254. {
  255. appDebugOut((DEB_ITRACE, "CopySecurityDescriptor, pSecDesc = 0x%08lx\n", pSecDesc));
  256. if (NULL == pSecDesc)
  257. {
  258. return NULL;
  259. }
  260. appAssert(IsValidSecurityDescriptor(pSecDesc));
  261. LONG err;
  262. DWORD dwLen = GetSecurityDescriptorLength(pSecDesc);
  263. PSECURITY_DESCRIPTOR pSelfSecDesc = reinterpret_cast<PSECURITY_DESCRIPTOR>(
  264. ::LocalAlloc(LMEM_ZEROINIT, dwLen) );
  265. if (NULL == pSelfSecDesc)
  266. {
  267. appDebugOut((DEB_ERROR, "new SECURITY_DESCRIPTOR (2) failed\n"));
  268. return NULL; // actually, should probably return an error
  269. }
  270. DWORD cbSelfSecDesc = dwLen;
  271. if (!MakeSelfRelativeSD(pSecDesc, pSelfSecDesc, &cbSelfSecDesc))
  272. {
  273. appDebugOut((DEB_TRACE, "MakeSelfRelativeSD failed, 0x%08lx\n", GetLastError()));
  274. // assume it failed because it was already self-relative
  275. CopyMemory(pSelfSecDesc, pSecDesc, dwLen);
  276. }
  277. appAssert(IsValidSecurityDescriptor(pSelfSecDesc));
  278. return pSelfSecDesc;
  279. }
  280. //+-------------------------------------------------------------------------
  281. //
  282. // Function: DisplayError
  283. //
  284. // Synopsis: Display an error message
  285. //
  286. // History: 24-Apr-95 BruceFo Stolen
  287. //
  288. //--------------------------------------------------------------------------
  289. VOID
  290. DisplayError(
  291. IN HWND hwnd,
  292. IN HRESULT dwErrorCode, // message file number. not really an HRESULT
  293. IN NET_API_STATUS err,
  294. IN PWSTR pszShare
  295. )
  296. {
  297. if ( err < MIN_LANMAN_MESSAGE_ID
  298. || err > MAX_LANMAN_MESSAGE_ID
  299. )
  300. {
  301. // a Win32 error?
  302. WCHAR szMsg[500];
  303. DWORD dwReturn = FormatMessage(
  304. FORMAT_MESSAGE_FROM_SYSTEM,
  305. NULL,
  306. err,
  307. LANG_USER_DEFAULT,
  308. szMsg,
  309. ARRAYLEN(szMsg),
  310. NULL);
  311. if (0 == dwReturn) // couldn't find message
  312. {
  313. appDebugOut((DEB_IERROR,
  314. "FormatMessage (from system) failed, 0x%08lx\n",
  315. GetLastError()));
  316. MyErrorDialog(hwnd, IERR_UNKNOWN, err);
  317. }
  318. else
  319. {
  320. MyErrorDialog(hwnd, dwErrorCode, pszShare, szMsg);
  321. }
  322. }
  323. else
  324. {
  325. DisplayLanmanError(hwnd, dwErrorCode, err, pszShare);
  326. }
  327. }
  328. //+-------------------------------------------------------------------------
  329. //
  330. // Function: DisplayLanmanError
  331. //
  332. // Synopsis: Display an error message from a LanMan error.
  333. //
  334. // History: 24-Apr-95 BruceFo Stolen
  335. //
  336. //--------------------------------------------------------------------------
  337. VOID
  338. DisplayLanmanError(
  339. IN HWND hwnd,
  340. IN HRESULT dwErrorCode, // message file number. not really an HRESULT
  341. IN NET_API_STATUS err,
  342. IN PWSTR pszShare
  343. )
  344. {
  345. if ( err < MIN_LANMAN_MESSAGE_ID
  346. || err > MAX_LANMAN_MESSAGE_ID
  347. )
  348. {
  349. MyErrorDialog(hwnd, IERR_UNKNOWN, err);
  350. return;
  351. }
  352. WCHAR szCaption[100];
  353. LoadString(g_hInstance, IDS_MSGTITLE, szCaption, ARRAYLEN(szCaption));
  354. //
  355. // get LanMan message from system message file.
  356. //
  357. WCHAR szNetMsg[500];
  358. WCHAR szBuf[500];
  359. HINSTANCE hInstanceNetMsg = LoadLibrary(NETMSG_DLL);
  360. if (NULL == hInstanceNetMsg)
  361. {
  362. appDebugOut((DEB_IERROR,
  363. "LoadLibrary(netmsg.dll) failed, 0x%08lx\n",
  364. GetLastError()));
  365. LoadString(g_hInstance, IDS_NO_NET_MSG, szBuf, ARRAYLEN(szBuf));
  366. MessageBox(hwnd, szBuf, szCaption, MB_ICONSTOP | MB_OK);
  367. return;
  368. }
  369. DWORD dwReturn = FormatMessage(
  370. FORMAT_MESSAGE_FROM_HMODULE,
  371. hInstanceNetMsg,
  372. err,
  373. LANG_USER_DEFAULT,
  374. szNetMsg,
  375. ARRAYLEN(szNetMsg),
  376. NULL);
  377. if (0 == dwReturn) // couldn't find message
  378. {
  379. appDebugOut((DEB_IERROR,
  380. "FormatMessage failed, 0x%08lx\n",
  381. GetLastError()));
  382. LoadString(g_hInstance, IDS_NET_MSG_NOT_FOUND, szBuf, ARRAYLEN(szBuf));
  383. wsprintf(szNetMsg, szBuf, GetLastError());
  384. MessageBox(hwnd, szNetMsg, szCaption, MB_ICONSTOP | MB_OK);
  385. }
  386. else
  387. {
  388. MyErrorDialog(hwnd, dwErrorCode, pszShare, szNetMsg);
  389. }
  390. FreeLibrary(hInstanceNetMsg);
  391. }
  392. //+-------------------------------------------------------------------------
  393. //
  394. // Function: IsValidShareName
  395. //
  396. // Synopsis: Checks if the proposed share name is valid or not. If not,
  397. // it will return a message id for the reason why.
  398. //
  399. // Arguments: [pszShareName] - Proposed share name
  400. // [puId] - If name is invalid, this will contain the reason why.
  401. //
  402. // Returns: TRUE if name is valid, else FALSE.
  403. //
  404. // History: 3-May-95 BruceFo Stolen
  405. //
  406. //--------------------------------------------------------------------------
  407. BOOL
  408. IsValidShareName(
  409. IN PCWSTR pszShareName,
  410. OUT HRESULT* uId
  411. )
  412. {
  413. if (NetpNameValidate(NULL, (PWSTR)pszShareName, NAMETYPE_SHARE, 0L) != NERR_Success)
  414. {
  415. *uId = IERR_InvalidShareName;
  416. return FALSE;
  417. }
  418. return TRUE;
  419. }
  420. //+-------------------------------------------------------------------------
  421. //
  422. // Function: SetErrorFocus
  423. //
  424. // Synopsis: Set focus to an edit control and select its text.
  425. //
  426. // Arguments: [hwnd] - dialog window
  427. // [idCtrl] - edit control to set focus to (and select)
  428. //
  429. // Returns: nothing
  430. //
  431. // History: 3-May-95 BruceFo Stolen
  432. //
  433. //--------------------------------------------------------------------------
  434. VOID
  435. SetErrorFocus(
  436. IN HWND hwnd,
  437. IN UINT idCtrl
  438. )
  439. {
  440. HWND hCtrl = ::GetDlgItem(hwnd, idCtrl);
  441. ::SetFocus(hCtrl);
  442. ::SendMessage(hCtrl, EM_SETSEL, 0, -1);
  443. }
  444. //+-------------------------------------------------------------------------
  445. //
  446. // Function: ConfirmReplaceShare
  447. //
  448. // Synopsis: Display confirmations for replacing an existing share
  449. //
  450. // Arguments: [hwnd] - dialog window
  451. // [pszShareName] - name of share being replaced
  452. // [pszOldPath] - current path for the share
  453. // [pszNewPath] - directory the user's trying to share
  454. //
  455. // Returns: Returns IDYES, IDNO, or IDCANCEL
  456. //
  457. // History: 4-May-95 BruceFo Stolen
  458. //
  459. //--------------------------------------------------------------------------
  460. DWORD
  461. ConfirmReplaceShare(
  462. IN HWND hwnd,
  463. IN PCWSTR pszShareName,
  464. IN PCWSTR pszOldPath,
  465. IN PCWSTR pszNewPath
  466. )
  467. {
  468. DWORD id = MyConfirmationDialog(
  469. hwnd,
  470. MSG_RESHARENAMECONFIRM,
  471. MB_YESNO | MB_ICONEXCLAMATION,
  472. pszOldPath,
  473. pszShareName,
  474. pszNewPath);
  475. if (id != IDYES)
  476. {
  477. return id;
  478. }
  479. return ConfirmStopShare(hwnd, (PWSTR)pszShareName);
  480. }
  481. //+-------------------------------------------------------------------------
  482. //
  483. // Member: ConfirmStopShare, public
  484. //
  485. // Synopsis: Display the appropriate confirmations when stopping a share.
  486. //
  487. // Arguments: [hwnd] - parent window handle for messages
  488. // [pszShare] - ptr to affected share name
  489. //
  490. // Returns: IDYES if share should be deleted, IDNO if we don't want to
  491. // delete, but keep going, IDCANCEL to stop going.
  492. //
  493. // History: 19-Apr-95 BruceFo Created
  494. //
  495. //--------------------------------------------------------------------------
  496. DWORD
  497. ConfirmStopShare(
  498. IN HWND hwnd,
  499. IN LPWSTR pszShare
  500. )
  501. {
  502. DWORD cConns, cOpens;
  503. NET_API_STATUS err = ShareConnectionInfo(pszShare, &cConns, &cOpens);
  504. if (err != NERR_Success)
  505. {
  506. DisplayError(hwnd, IERR_CANT_DEL_SHARE, err, pszShare);
  507. return IDYES; // allow the stop anyway
  508. }
  509. if (cConns != 0)
  510. {
  511. // If there are any open files, just give the more detailed
  512. // message about there being open files. Otherwise, just say how
  513. // many connections there are.
  514. if (cOpens != 0)
  515. {
  516. return MyConfirmationDialog(
  517. hwnd,
  518. MSG_STOPSHAREOPENS,
  519. MB_YESNOCANCEL | MB_ICONEXCLAMATION,
  520. cOpens,
  521. cConns,
  522. pszShare);
  523. }
  524. else
  525. {
  526. return MyConfirmationDialog(
  527. hwnd,
  528. MSG_STOPSHARECONNS,
  529. MB_YESNOCANCEL | MB_ICONEXCLAMATION,
  530. cConns,
  531. pszShare);
  532. }
  533. }
  534. return IDYES; /* OK to delete */
  535. }
  536. //+-------------------------------------------------------------------------
  537. //
  538. // Member: ShareConnectionInfo, public
  539. //
  540. // Synopsis: Determine how many connections and file opens exist for a
  541. // share, for use by confirmation dialogs.
  542. //
  543. // Arguments: [pszShare] - ptr to affected share name
  544. // [pcConns] - *pcConns get the number of connections
  545. // [pcOpens] - *pcOpens get the number of file opens
  546. //
  547. // Returns: standard net api code, NERR_Success if everything ok.
  548. //
  549. // History: 19-Apr-95 BruceFo Stolen
  550. //
  551. //--------------------------------------------------------------------------
  552. NET_API_STATUS
  553. ShareConnectionInfo(
  554. IN LPWSTR pszShare,
  555. OUT LPDWORD pcConns,
  556. OUT LPDWORD pcOpens
  557. )
  558. {
  559. CONNECTION_INFO_1* pBuf;
  560. DWORD iEntry, iTotal;
  561. NET_API_STATUS err = NetConnectionEnum(
  562. NULL,
  563. pszShare,
  564. 1,
  565. (LPBYTE*)&pBuf,
  566. 0xffffffff, // no buffer limit; get them all!
  567. &iEntry,
  568. &iTotal,
  569. NULL);
  570. if ((err == NERR_Success) || (err == ERROR_MORE_DATA))
  571. {
  572. int iConnections = 0;
  573. for (DWORD i = 0; i < iEntry; i++)
  574. {
  575. iConnections += pBuf[i].coni1_num_opens;
  576. }
  577. *pcConns = iTotal;
  578. *pcOpens = iConnections;
  579. err = NERR_Success;
  580. }
  581. else
  582. {
  583. *pcConns = 0;
  584. *pcOpens = 0;
  585. }
  586. NetApiBufferFree(pBuf);
  587. appDebugOut((DEB_ITRACE,"Share '%ws' has %d connections and %d opens\n", pszShare, *pcConns, *pcOpens));
  588. return err;
  589. }
  590. //+---------------------------------------------------------------------------
  591. //
  592. // Function: IsWorkstationProduct
  593. //
  594. // Synopsis: Determines the NT product type (server or workstation),
  595. // and returns TRUE if it is workstation.
  596. //
  597. // Arguments: (none)
  598. //
  599. // Returns: TRUE if running on workstation products
  600. //
  601. // History: 11-Sep-95 BruceFo Created
  602. //
  603. //----------------------------------------------------------------------------
  604. BOOL
  605. IsWorkstationProduct(
  606. VOID
  607. )
  608. {
  609. //
  610. // Determine whether this is the workstation or server product by looking
  611. // at HKEY_LOCAL_MACHINE, System\CurrentControlSet\Control\ProductOptions.
  612. // The ProductType value therein is interpreted as follows:
  613. //
  614. // LanmanNt -- server product, running as domain controller
  615. // ServerNt -- server product, not a domain controller
  616. // WinNT -- workstation product
  617. //
  618. LONG ec;
  619. HKEY hkey;
  620. DWORD type;
  621. DWORD size;
  622. UCHAR buf[100];
  623. BOOL fIsWorkstation = TRUE;
  624. ec = RegOpenKeyEx(
  625. HKEY_LOCAL_MACHINE,
  626. TEXT("System\\CurrentControlSet\\Control\\ProductOptions"),
  627. 0,
  628. KEY_QUERY_VALUE,
  629. &hkey
  630. );
  631. if (ec == NO_ERROR)
  632. {
  633. size = sizeof(buf);
  634. ec = RegQueryValueEx(hkey,
  635. TEXT("ProductType"),
  636. NULL,
  637. &type,
  638. buf,
  639. &size);
  640. if ((ec == NO_ERROR) && (type == REG_SZ))
  641. {
  642. if (0 == lstrcmpi((LPTSTR)buf, TEXT("lanmannt")))
  643. {
  644. fIsWorkstation = FALSE;
  645. }
  646. if (0 == lstrcmpi((LPTSTR)buf, TEXT("servernt")))
  647. {
  648. fIsWorkstation = FALSE;
  649. }
  650. }
  651. RegCloseKey(hkey);
  652. }
  653. return fIsWorkstation;
  654. }
  655. BOOL
  656. DriveLetterShare(
  657. PWSTR pszShareName
  658. )
  659. {
  660. if (NULL == pszShareName || lstrlen(pszShareName) != 2)
  661. {
  662. return FALSE;
  663. }
  664. // BUGBUG: what about non-English char sets?
  665. return ( ((pszShareName[0] >= TEXT('a')) && pszShareName[0] <= TEXT('z'))
  666. || ((pszShareName[0] >= TEXT('A')) && pszShareName[0] <= TEXT('Z'))
  667. )
  668. && (pszShareName[1] == TEXT('$'))
  669. ;
  670. }
  671. #if DBG == 1
  672. //+-------------------------------------------------------------------------
  673. //
  674. // Function: DumpNetEnum
  675. //
  676. // Synopsis: Dumps an array of SHARE_INFO_1 structures.
  677. //
  678. // History: 4-Apr-95 BruceFo Created
  679. //
  680. //--------------------------------------------------------------------------
  681. VOID
  682. DumpNetEnum(
  683. IN LPVOID pBufShares,
  684. IN ULONG entriesRead
  685. )
  686. {
  687. SHARE_INFO_1* pBase = (SHARE_INFO_1*) pBufShares;
  688. appDebugOut((DEB_TRACE,
  689. "DumpNetEnum: %d entries\n",
  690. entriesRead));
  691. for (ULONG i = 0; i < entriesRead; i++)
  692. {
  693. SHARE_INFO_1* p = &(pBase[i]);
  694. appDebugOut((DEB_TRACE | DEB_NOCOMPNAME,
  695. "\t Share name: %ws\n"
  696. "\t Type: %d (0x%08lx)\n"
  697. "\t Comment: %ws\n"
  698. "\n"
  699. ,
  700. p->shi1_netname,
  701. p->shi1_type, p->shi1_type,
  702. p->shi1_remark
  703. ));
  704. }
  705. }
  706. #endif // DBG == 1
  707. struct SHARE_PROPERTIES_DATA
  708. {
  709. IUnknown* punk;
  710. LPTSTR pszMachine;
  711. LPTSTR pszShareName;
  712. };
  713. DWORD CALLBACK
  714. SharePropertiesThreadProc(
  715. LPVOID lpThreadParameter
  716. )
  717. {
  718. SHARE_PROPERTIES_DATA* pData = (SHARE_PROPERTIES_DATA*)lpThreadParameter;
  719. if (NULL == pData)
  720. {
  721. appAssert(!"Unexpected properties thread data");
  722. return 0;
  723. }
  724. WCHAR szCaption[MAX_PATH];
  725. LoadString(g_hInstance, IDS_SHARE_PROPTITLE, szCaption, ARRAYLEN(szCaption));
  726. SHARE_PROPSHEETPAGE sprop;
  727. sprop.psp.dwSize = sizeof(sprop); // no extra data.
  728. sprop.psp.dwFlags = PSP_USEREFPARENT;
  729. sprop.psp.hInstance = g_hInstance;
  730. sprop.psp.pszTemplate = MAKEINTRESOURCE(IDD_SHARE_PROPERTIES);
  731. sprop.psp.hIcon = NULL;
  732. sprop.psp.pszTitle = NULL;
  733. sprop.psp.pfnDlgProc = CSharingPropertyPage::DlgProcPage;
  734. sprop.psp.lParam = 0;
  735. sprop.psp.pfnCallback = NULL;
  736. sprop.psp.pcRefParent = &g_NonOLEDLLRefs;
  737. sprop.pszMachine = pData->pszMachine;
  738. sprop.pszShareName = pData->pszShareName;
  739. PROPSHEETHEADER psh;
  740. psh.dwSize = sizeof(PROPSHEETHEADER);
  741. psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
  742. psh.hwndParent = NULL;
  743. psh.hInstance = g_hInstance;
  744. psh.pszIcon = MAKEINTRESOURCE(IDI_SHARESFLD);
  745. psh.pszCaption = szCaption;
  746. psh.nPages = 1;
  747. psh.nStartPage = 0;
  748. psh.ppsp = (LPCPROPSHEETPAGE)&sprop;
  749. psh.pfnCallback= NULL;
  750. PropertySheet(&psh);
  751. pData->punk->Release();
  752. LocalFree(pData); // The strings are packed in the same allocation!
  753. return 0;
  754. }
  755. HRESULT
  756. ShareDoProperties(
  757. IN IUnknown* punk,
  758. IN LPTSTR pszMachine,
  759. IN LPTSTR pszShareName
  760. )
  761. {
  762. if (NULL == pszShareName)
  763. {
  764. return E_INVALIDARG;
  765. }
  766. DWORD cbMachine = 0;
  767. DWORD cbStrings = 0;
  768. if (NULL != pszMachine)
  769. {
  770. cbMachine = (lstrlen(pszMachine) + 1) * sizeof(TCHAR);
  771. cbStrings += cbMachine;
  772. }
  773. cbStrings += (lstrlen(pszShareName) + 1) * sizeof(TCHAR);
  774. HRESULT hr = S_OK;
  775. HANDLE hThread;
  776. DWORD idThread;
  777. SHARE_PROPERTIES_DATA* pData = (SHARE_PROPERTIES_DATA*)LocalAlloc(LPTR, sizeof(SHARE_PROPERTIES_DATA) + cbStrings);
  778. if (pData)
  779. {
  780. if (NULL != pszMachine)
  781. {
  782. pData->pszMachine = (LPWSTR)(((LPBYTE)pData) + sizeof(SHARE_PROPERTIES_DATA));
  783. lstrcpy(pData->pszMachine, pszMachine);
  784. }
  785. else
  786. {
  787. pData->pszMachine = NULL;
  788. }
  789. pData->pszShareName = (LPWSTR)(((LPBYTE)pData) + sizeof(SHARE_PROPERTIES_DATA) + cbMachine);
  790. lstrcpy(pData->pszShareName, pszShareName);
  791. pData->punk = punk;
  792. pData->punk->AddRef();
  793. hThread = CreateThread(NULL, 0, SharePropertiesThreadProc, pData, 0, &idThread);
  794. if (hThread)
  795. {
  796. CloseHandle(hThread);
  797. return S_OK;
  798. }
  799. else
  800. {
  801. pData->punk->Release();
  802. LocalFree(pData);
  803. return HRESULT_FROM_WIN32(GetLastError());
  804. }
  805. }
  806. else
  807. {
  808. hr = E_OUTOFMEMORY;
  809. }
  810. return hr;
  811. }
  812. HRESULT
  813. ShareDoDelete(
  814. IN HWND hwndOwner,
  815. IN PWSTR pszMachine,
  816. IN PWSTR pszShareName
  817. )
  818. {
  819. // Remove the share. We need to know the path that was
  820. // shared to be able to update the explorer. So, get
  821. // that.
  822. SHARE_INFO_1* pInfo1 = NULL;
  823. SHARE_INFO_2* pInfo2 = NULL;
  824. DWORD ret;
  825. HRESULT hr = S_OK;
  826. ret = NetShareGetInfo(pszMachine, pszShareName, 2, (LPBYTE*)&pInfo2);
  827. if (NERR_Success != ret)
  828. {
  829. // make sure it's null
  830. pInfo2 = NULL;
  831. }
  832. // Warn and confirm if it's a special share, either ADMIN$, IPC$,
  833. // or <drive>$
  834. if (NULL == pInfo2)
  835. {
  836. // Permissions problem? Try getting SHARE_INFO_1.
  837. ret = NetShareGetInfo(pszMachine, pszShareName, 1, (LPBYTE*)&pInfo1);
  838. if (NERR_Success != ret)
  839. {
  840. // make sure it's null
  841. pInfo1 = NULL;
  842. }
  843. }
  844. else
  845. {
  846. pInfo1 = (SHARE_INFO_1*)pInfo2; // I just need the type
  847. }
  848. if (NULL != pInfo1)
  849. {
  850. DWORD id = IDYES;
  851. if (pInfo1->shi1_type & STYPE_SPECIAL)
  852. {
  853. id = MyConfirmationDialog(
  854. hwndOwner,
  855. MSG_DELETESPECIAL,
  856. MB_YESNO | MB_ICONEXCLAMATION,
  857. pszShareName);
  858. }
  859. if (pInfo1 != (SHARE_INFO_1*)pInfo2)
  860. {
  861. NetApiBufferFree(pInfo1);
  862. }
  863. if (id != IDYES)
  864. {
  865. hr = S_OK;
  866. goto nodelete;
  867. }
  868. }
  869. // Actually delete the share
  870. ret = NetShareDel(pszMachine, pszShareName, 0);
  871. if (NERR_Success == ret)
  872. {
  873. if (NULL != pInfo2)
  874. {
  875. SHChangeNotify(SHCNE_NETUNSHARE, SHCNF_PATH, pInfo2->shi2_path, 0);
  876. }
  877. }
  878. else
  879. {
  880. // BUGBUG: error message to user
  881. hr = HRESULT_FROM_WIN32(GetLastError());
  882. }
  883. nodelete:
  884. if (NULL != pInfo2)
  885. {
  886. NetApiBufferFree(pInfo2);
  887. }
  888. return hr;
  889. }
  890. struct SHARE_NEW_DATA
  891. {
  892. IUnknown* punk;
  893. LPTSTR pszMachine;
  894. };
  895. DWORD CALLBACK
  896. ShareNewThreadProc(
  897. LPVOID lpThreadParameter
  898. )
  899. {
  900. SHARE_NEW_DATA* pData = (SHARE_NEW_DATA*)lpThreadParameter;
  901. if (NULL == pData)
  902. {
  903. appAssert(!"Unexpected properties thread data");
  904. return 0;
  905. }
  906. CDlgNewShare dlg(NULL, pData->pszMachine);
  907. if (dlg.DoModal())
  908. {
  909. }
  910. pData->punk->Release();
  911. LocalFree(pData); // The strings are packed in the same allocation!
  912. return 0;
  913. }
  914. HRESULT
  915. ShareDoNew(
  916. IN IUnknown* punk,
  917. IN PWSTR pszMachine
  918. )
  919. {
  920. DWORD cbStrings = 0;
  921. if (NULL != pszMachine)
  922. {
  923. cbStrings += (lstrlen(pszMachine) + 1) * sizeof(TCHAR);
  924. }
  925. HRESULT hr = S_OK;
  926. HANDLE hThread;
  927. DWORD idThread;
  928. SHARE_NEW_DATA* pData = (SHARE_NEW_DATA*)LocalAlloc(LPTR, sizeof(SHARE_NEW_DATA) + cbStrings);
  929. if (pData)
  930. {
  931. if (NULL != pszMachine)
  932. {
  933. pData->pszMachine = (LPWSTR)(((LPBYTE)pData) + sizeof(SHARE_NEW_DATA));
  934. lstrcpy(pData->pszMachine, pszMachine);
  935. }
  936. else
  937. {
  938. pData->pszMachine = NULL;
  939. }
  940. pData->punk = punk;
  941. pData->punk->AddRef();
  942. hThread = CreateThread(NULL, 0, ShareNewThreadProc, pData, 0, &idThread);
  943. if (hThread)
  944. {
  945. CloseHandle(hThread);
  946. return S_OK;
  947. }
  948. else
  949. {
  950. pData->punk->Release();
  951. LocalFree(pData);
  952. return HRESULT_FROM_WIN32(GetLastError());
  953. }
  954. }
  955. else
  956. {
  957. hr = E_OUTOFMEMORY;
  958. }
  959. return hr;
  960. }
  961. #ifdef WIZARDS
  962. HRESULT
  963. ShareDoSpecial(
  964. IN HWND hwndOwner,
  965. IN PWSTR pszMachine,
  966. IN BYTE bType
  967. )
  968. {
  969. // Construct the command line to pass to the Share Wizard
  970. TCHAR szCommandLine[MAX_PATH];
  971. lstrcpy(szCommandLine, g_szShareWizardArg);
  972. switch (bType)
  973. {
  974. case SHID_SHARE_NW:
  975. wcscat(szCommandLine, g_szFpnw);
  976. break;
  977. case SHID_SHARE_MAC:
  978. wcscat(szCommandLine, g_szSfm);
  979. break;
  980. case SHID_SHARE_ALL:
  981. wcscat(szCommandLine, g_szAll);
  982. break;
  983. case SHID_SHARE_NEW:
  984. // nothing special
  985. break;
  986. default: appAssert(!"Unknown object type");
  987. }
  988. if (NULL != pszMachine)
  989. {
  990. wcscat(szCommandLine, pszMachine);
  991. }
  992. appDebugOut((DEB_TRACE, "Invoking wizard with this command line: %ws\n", szCommandLine));
  993. // Looks like CreateProcess writes to this buffer!
  994. STARTUPINFO si = { 0 };
  995. si.cb = sizeof(si);
  996. PROCESS_INFORMATION pi = { 0 };
  997. BOOL b = CreateProcess(
  998. g_szShareWizardCmd,
  999. szCommandLine,
  1000. NULL, // pointer to process security attributes
  1001. NULL, // pointer to thread security attributes
  1002. FALSE, // handle inheritance flag
  1003. 0, // creation flags
  1004. NULL, // pointer to new environment block
  1005. NULL, // pointer to current directory name
  1006. &si, // pointer to STARTUPINFO
  1007. &pi); // pointer to PROCESS_INFORMATION
  1008. if (b)
  1009. {
  1010. CloseHandle(pi.hProcess);
  1011. CloseHandle(pi.hThread);
  1012. }
  1013. else
  1014. {
  1015. appDebugOut((DEB_ERROR,
  1016. "CreateProcess failed, 0x%08lx\n",
  1017. GetLastError()));
  1018. MyErrorDialog(hwndOwner, MSG_NOWIZARD);
  1019. }
  1020. return S_OK;
  1021. }
  1022. #endif // WIZARDS
  1023. VOID FSSetStatusText(HWND hwndOwner, LPTSTR* ppszText, int iStart, int iEnd)
  1024. {
  1025. HWND hwndStatus = NULL;
  1026. IShellBrowser* psb = FileCabinet_GetIShellBrowser(hwndOwner);
  1027. if (psb)
  1028. {
  1029. psb->GetControlWindow(FCW_STATUS, &hwndStatus);
  1030. if (hwndStatus)
  1031. {
  1032. for (; iStart <= iEnd; iStart++)
  1033. {
  1034. LPTSTR lpsz;
  1035. if (ppszText)
  1036. {
  1037. lpsz = *ppszText;
  1038. ppszText++;
  1039. }
  1040. else
  1041. {
  1042. lpsz = (LPTSTR)TEXT("");
  1043. }
  1044. #ifdef WINDOWS_ME
  1045. SendMessage(hwndStatus, SB_SETTEXT, SB_RTLREADING | (WPARAM)iStart, (LPARAM)lpsz);
  1046. #else
  1047. SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)iStart, (LPARAM)lpsz);
  1048. #endif
  1049. }
  1050. }
  1051. }
  1052. }
  1053. BOOL
  1054. IsLevelOk(
  1055. IN PWSTR pszMachine,
  1056. IN DWORD level
  1057. )
  1058. {
  1059. LPBYTE pBuf = NULL;
  1060. DWORD entriesread, totalentries;
  1061. NET_API_STATUS ret;
  1062. // we want to get the minimum amount of data, because all we care about
  1063. // is whether it succeeds the access check
  1064. DWORD prefmaxlen = 300;
  1065. for (;; prefmaxlen *= 2)
  1066. {
  1067. ret = NetShareEnum(
  1068. pszMachine,
  1069. level,
  1070. &pBuf,
  1071. prefmaxlen,
  1072. &entriesread,
  1073. &totalentries,
  1074. NULL);
  1075. if (NERR_BufTooSmall != ret)
  1076. {
  1077. NetApiBufferFree(pBuf);
  1078. break;
  1079. }
  1080. }
  1081. if (ERROR_ACCESS_DENIED == ret)
  1082. {
  1083. return FALSE;
  1084. }
  1085. else if (NERR_Success == ret || ERROR_MORE_DATA == ret)
  1086. {
  1087. return TRUE;
  1088. }
  1089. else
  1090. {
  1091. // some other error
  1092. return FALSE;
  1093. }
  1094. }
  1095. VOID
  1096. SetDialogIconBig(
  1097. IN HWND hwnd,
  1098. WORD idIcon
  1099. )
  1100. {
  1101. HICON hiconLarge = (HICON)LoadImage(
  1102. g_hInstance,
  1103. MAKEINTRESOURCE(idIcon),
  1104. IMAGE_ICON,
  1105. GetSystemMetrics(SM_CXICON),
  1106. GetSystemMetrics(SM_CYICON),
  1107. LR_DEFAULTCOLOR);
  1108. if (NULL == hiconLarge)
  1109. {
  1110. appDebugOut((DEB_ERROR,
  1111. "LoadImage for large image failed, 0x%08lx\n",
  1112. GetLastError()));
  1113. }
  1114. else
  1115. {
  1116. SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hiconLarge);
  1117. }
  1118. }
  1119. VOID
  1120. SetDialogIconSmall(
  1121. IN HWND hwnd,
  1122. WORD idIcon
  1123. )
  1124. {
  1125. HICON hiconSmall = (HICON)LoadImage(
  1126. g_hInstance,
  1127. MAKEINTRESOURCE(idIcon),
  1128. IMAGE_ICON,
  1129. GetSystemMetrics(SM_CXSMICON),
  1130. GetSystemMetrics(SM_CYSMICON),
  1131. LR_DEFAULTCOLOR);
  1132. if (NULL == hiconSmall)
  1133. {
  1134. appDebugOut((DEB_ERROR,
  1135. "LoadImage for small image failed, 0x%08lx\n",
  1136. GetLastError()));
  1137. }
  1138. else
  1139. {
  1140. SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hiconSmall);
  1141. }
  1142. }
  1143. //////////////////////////////////////////////////////////////////////////////
  1144. HRESULT
  1145. STRRETLoadString(
  1146. UINT ids,
  1147. STRRET* pstr
  1148. )
  1149. {
  1150. #ifdef UNICODE
  1151. TCHAR szTemp[MAX_PATH];
  1152. szTemp[0] = TEXT('\0');
  1153. LoadString(g_hInstance, ids, szTemp, ARRAYLEN(szTemp));
  1154. pstr->pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1) * sizeof(TCHAR));
  1155. if (NULL != pstr->pOleStr)
  1156. {
  1157. pstr->uType = STRRET_OLESTR;
  1158. lstrcpy(pstr->pOleStr, szTemp);
  1159. }
  1160. else
  1161. {
  1162. pstr->uType = STRRET_CSTR;
  1163. pstr->cStr[0] = '\0';
  1164. return E_OUTOFMEMORY;
  1165. }
  1166. #else
  1167. pstr->uType = STRRET_CSTR;
  1168. LoadString(g_hInstance, ids, pstr->cStr, ARRAYLEN(pstr->cStr));
  1169. #endif
  1170. return S_OK;
  1171. }
  1172. HRESULT
  1173. STRRETCopy(
  1174. LPTSTR pszString,
  1175. STRRET* pstr
  1176. )
  1177. {
  1178. #ifdef UNICODE
  1179. pstr->pOleStr = (LPOLESTR)SHAlloc((lstrlen(pszString)+1) * sizeof(TCHAR));
  1180. if (NULL != pstr->pOleStr)
  1181. {
  1182. pstr->uType = STRRET_OLESTR;
  1183. lstrcpy(pstr->pOleStr, pszString);
  1184. }
  1185. else
  1186. {
  1187. pstr->uType = STRRET_CSTR;
  1188. pstr->cStr[0] = '\0';
  1189. return E_OUTOFMEMORY;
  1190. }
  1191. #else
  1192. pstr->uType = STRRET_CSTR;
  1193. int cch = lstrlen(pszString);
  1194. cch = min(cch, ARRAYLEN(pstr->cStr) - 1);
  1195. strncpy(pstr->cStr, pszString, cch);
  1196. pszString[cch] = '\0';
  1197. #endif
  1198. return S_OK;
  1199. }
  1200. VOID
  1201. FillSpecialID(
  1202. LPIDSHARE pids,
  1203. BYTE bFlags, // SHID_SHARE_*
  1204. UINT idsName
  1205. )
  1206. {
  1207. WCHAR szBuf[MAX_PATH];
  1208. szBuf[0] = L'\0';
  1209. LoadString(g_hInstance, idsName, szBuf, ARRAYLEN(szBuf));
  1210. LPWSTR pszName = szBuf;
  1211. USHORT nameLength = (USHORT)lstrlen(pszName);
  1212. USHORT nameOffset = 0;
  1213. pids->bFlags = bFlags;
  1214. pids->bReserved = 0;
  1215. pids->maxUses = 0xffffffff; // bogus
  1216. // we don't store nameOffset
  1217. pids->oComment = 0xffff; // bogus
  1218. pids->oPath = 0xffff; // bogus
  1219. lstrcpy(&pids->cBuf[nameOffset], pszName);
  1220. pids->cb = offsetof(IDSHARE, cBuf)
  1221. + (nameLength + 1) * sizeof(WCHAR);
  1222. //
  1223. // null terminate pidl
  1224. //
  1225. *(USHORT *)((LPBYTE)pids + pids->cb) = 0;
  1226. }
  1227. VOID
  1228. FillID1(
  1229. LPIDSHARE pids,
  1230. LPSHARE_INFO_1 pInfo
  1231. )
  1232. {
  1233. LPWSTR pszName = pInfo->shi1_netname;
  1234. LPWSTR pszComment = pInfo->shi1_remark;
  1235. USHORT nameLength, commentLength;
  1236. USHORT nameOffset, commentOffset;
  1237. nameLength = (USHORT)lstrlen(pszName);
  1238. commentLength = (USHORT)lstrlen(pszComment);
  1239. nameOffset = 0;
  1240. commentOffset = nameOffset + nameLength + 1;
  1241. pids->bFlags = SHID_SHARE_1;
  1242. pids->bReserved = 0;
  1243. pids->type = pInfo->shi1_type;
  1244. pids->maxUses = 0xffffffff; // bogus
  1245. // we don't store nameOffset
  1246. pids->oComment = commentOffset;
  1247. pids->oPath = 0xffff; // bogus
  1248. lstrcpy(&pids->cBuf[nameOffset], pszName);
  1249. lstrcpy(&pids->cBuf[commentOffset], pszComment);
  1250. pids->cb = offsetof(IDSHARE, cBuf)
  1251. + (nameLength + 1 + commentLength + 1) * sizeof(WCHAR);
  1252. //
  1253. // null terminate pidl
  1254. //
  1255. *(USHORT *)((LPBYTE)pids + pids->cb) = 0;
  1256. }
  1257. VOID
  1258. FillID2(
  1259. LPIDSHARE pids,
  1260. LPSHARE_INFO_2 pInfo
  1261. )
  1262. {
  1263. LPWSTR pszName = pInfo->shi2_netname;
  1264. LPWSTR pszComment = pInfo->shi2_remark;
  1265. LPWSTR pszPath = pInfo->shi2_path;
  1266. USHORT nameLength, commentLength, pathLength;
  1267. USHORT nameOffset, commentOffset, pathOffset;
  1268. nameLength = (USHORT)lstrlen(pszName);
  1269. commentLength = (USHORT)lstrlen(pszComment);
  1270. pathLength = (USHORT)lstrlen(pszPath);
  1271. nameOffset = 0;
  1272. commentOffset = nameOffset + nameLength + 1;
  1273. pathOffset = commentOffset + commentLength + 1;
  1274. pids->bFlags = SHID_SHARE_2;
  1275. pids->bReserved = 0;
  1276. pids->type = pInfo->shi2_type;
  1277. pids->maxUses = pInfo->shi2_max_uses;
  1278. // we don't store nameOffset
  1279. pids->oComment = commentOffset;
  1280. pids->oPath = pathOffset;
  1281. lstrcpy(&pids->cBuf[nameOffset], pszName);
  1282. lstrcpy(&pids->cBuf[commentOffset], pszComment);
  1283. lstrcpy(&pids->cBuf[pathOffset], pszPath);
  1284. pids->cb = offsetof(IDSHARE, cBuf)
  1285. + (nameLength + 1 + commentLength + 1 + pathLength + 1) * sizeof(WCHAR);
  1286. //
  1287. // null terminate pidl
  1288. //
  1289. *(USHORT *)((LPBYTE)pids + pids->cb) = 0;
  1290. }
  1291. VOID
  1292. StrNCopy(
  1293. OUT LPWSTR pszTarget,
  1294. IN LPCWSTR pszSource,
  1295. IN DWORD cchTarget
  1296. )
  1297. {
  1298. DWORD cch = lstrlen(pszSource) + 1;
  1299. cch = min(cch, cchTarget);
  1300. wcsncpy(pszTarget, pszSource, cch - 1);
  1301. pszTarget[cch - 1] = TEXT('\0');
  1302. }
  1303. //+---------------------------------------------------------------------------
  1304. //
  1305. // Function: TrimLeadingAndTrailingSpaces
  1306. //
  1307. // Synopsis: Trims the leading and trailing spaces from a null-terminated string.
  1308. // Used primarily for share names.
  1309. //
  1310. // History: 18-Jul-97 JonN Created
  1311. //
  1312. //----------------------------------------------------------------------------
  1313. VOID
  1314. TrimLeadingAndTrailingSpaces(
  1315. IN OUT PWSTR psz
  1316. )
  1317. {
  1318. int cchStrlen = ::wcslen(psz);
  1319. int cchLeadingSpaces = 0;
  1320. int cchTrailingSpaces = 0;
  1321. while (L' ' == psz[cchLeadingSpaces])
  1322. cchLeadingSpaces++;
  1323. if (cchLeadingSpaces < cchStrlen)
  1324. {
  1325. while (L' ' == psz[cchStrlen-(cchTrailingSpaces+1)])
  1326. cchTrailingSpaces++;
  1327. }
  1328. if ((cchLeadingSpaces+cchTrailingSpaces) > 0)
  1329. {
  1330. cchStrlen -= (cchLeadingSpaces+cchTrailingSpaces);
  1331. (void)memmove( psz,
  1332. psz+cchLeadingSpaces,
  1333. cchStrlen*sizeof(WCHAR) );
  1334. psz[cchStrlen] = L'\0';
  1335. }
  1336. }