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.

944 lines
22 KiB

  1. //
  2. // Util.cpp
  3. //
  4. #include "stdafx.h"
  5. #include "Util.h"
  6. #include "theapp.h"
  7. #include <stdarg.h>
  8. #include <shlobj.h>
  9. LPTSTR lstrchr(LPCTSTR pszString, TCHAR ch)
  10. {
  11. while (*pszString != _T('\0'))
  12. {
  13. if (*pszString == ch)
  14. return (LPTSTR)pszString;
  15. pszString = CharNext(pszString);
  16. }
  17. return NULL;
  18. }
  19. LPTSTR lstrdup(LPCTSTR psz)
  20. {
  21. LPTSTR pszResult = (LPTSTR)malloc((lstrlen(psz)+1) * sizeof(TCHAR));
  22. if (pszResult != NULL)
  23. lstrcpy(pszResult, psz);
  24. return pszResult;
  25. }
  26. void ReplaceString(LPTSTR& pszTarget, LPCTSTR pszSource)
  27. {
  28. free(pszTarget);
  29. pszTarget = lstrdup(pszSource);
  30. }
  31. BOOL MyIsDigit(TCHAR ch)
  32. {
  33. return ((UINT)ch - (UINT)_T('0')) <= 9;
  34. }
  35. // A version of atoi that doesn't use the CRT
  36. int MyAtoi(LPCTSTR psz)
  37. {
  38. int result = 0;
  39. UINT digit;
  40. TCHAR chSign = *psz;
  41. if (*psz == _T('-') || *psz == _T('+'))
  42. psz += 1;
  43. while ((digit = (UINT)((int)*psz - (int)_T('0'))) <= 9)
  44. {
  45. result = (result * 10) + (int)digit;
  46. psz += 1;
  47. }
  48. if (chSign == _T('-'))
  49. result = -result;
  50. return result;
  51. }
  52. // CountChars
  53. //
  54. // Returns the number of times the given character appears in the
  55. // string.
  56. //
  57. // 2/03/1999 KenSh Created
  58. //
  59. int CountChars(LPCTSTR psz, TCHAR ch)
  60. {
  61. int count = 0;
  62. while (*psz != _T('\0'))
  63. {
  64. if (*psz == ch)
  65. count++;
  66. psz = CharNext(psz);
  67. }
  68. return count;
  69. }
  70. // GetFirstToken
  71. //
  72. // Copies the characters up to but not including the separator char, and
  73. // advances the source pointer to the character after the separator char.
  74. // Returns TRUE if a token was found, FALSE if not.
  75. //
  76. BOOL GetFirstToken(LPCTSTR& pszList, TCHAR chSeparator, LPTSTR pszBuf, int cchBuf)
  77. {
  78. if (pszList == NULL || *pszList == '\0')
  79. {
  80. *pszBuf = '\0';
  81. return FALSE;
  82. }
  83. LPTSTR pchComma = lstrchr(pszList, chSeparator);
  84. int cchCopy;
  85. int cchSkip;
  86. if (pchComma == NULL)
  87. {
  88. cchCopy = lstrlen(pszList);
  89. cchSkip = cchCopy;
  90. }
  91. else
  92. {
  93. cchCopy = (int)(pchComma - pszList);
  94. cchSkip = cchCopy + 1;
  95. }
  96. cchCopy += 1;
  97. if (cchCopy > cchBuf)
  98. cchCopy = cchBuf;
  99. lstrcpyn(pszBuf, pszList, cchCopy);
  100. pszList += cchSkip;
  101. return TRUE;
  102. }
  103. // Use this function for initializing multiple DLL procs
  104. // pszFunction names is a series of null-separated proc names, followed by an extra null
  105. BOOL LoadDllFunctions(LPCTSTR pszDll, LPCSTR pszFunctionNames, FARPROC* prgFunctions)
  106. {
  107. UINT uPrevMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  108. HINSTANCE hInst = LoadLibrary(pszDll);
  109. SetErrorMode(uPrevMode);
  110. if (hInst == NULL)
  111. {
  112. ASSERT(FALSE);
  113. return FALSE;
  114. }
  115. while (*pszFunctionNames != '\0')
  116. {
  117. *prgFunctions = GetProcAddress(hInst, pszFunctionNames);
  118. if (*prgFunctions == NULL)
  119. {
  120. ASSERT(FALSE);
  121. return FALSE;
  122. }
  123. pszFunctionNames += (lstrlenA(pszFunctionNames) + 1);
  124. prgFunctions += 1;
  125. }
  126. return TRUE;
  127. }
  128. int MakePath(LPTSTR pszBuf, LPCTSTR pszFolder, LPCTSTR pszFileTitle)
  129. {
  130. lstrcpy(pszBuf, pszFolder);
  131. int cch = lstrlen(pszBuf);
  132. if (pszBuf[cch-1] != _T('\\'))
  133. pszBuf[cch++] = _T('\\');
  134. lstrcpy(pszBuf + cch, pszFileTitle);
  135. return lstrlen(pszBuf);
  136. }
  137. // pszLinkTarget - where the link will point
  138. // pszDescription - link's description
  139. // pszFolderPath - path to folder to create file in or fully qualified file path to create
  140. // pszFileName - name of file to create in pszFolderPath or NULL to indicate pszFolderPath is already a file path
  141. //
  142. #ifndef NO_MAKELNKFILE
  143. HRESULT MakeLnkFile(CLSID clsid, LPCTSTR pszLinkTarget, LPCTSTR pszDescription, LPCTSTR pszFolderPath, LPCTSTR pszFileName)
  144. {
  145. HRESULT hresCoInit = CoInitialize(NULL); // we will create a COM object
  146. IUnknown *punk;
  147. HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk));
  148. if (SUCCEEDED(hr))
  149. {
  150. IShellLinkW * pslW;
  151. hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkW, &pslW));
  152. if (SUCCEEDED(hr))
  153. {
  154. //WCHAR szBuffer[MAX_PATH];
  155. //SHTCharToUnicode(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer));
  156. pslW->SetPath(pszLinkTarget);
  157. if (pszDescription)
  158. {
  159. //SHTCharToUnicode(pszDescription, szBuffer, ARRAYSIZE(szBuffer));
  160. pslW->SetDescription(pszDescription);
  161. }
  162. pslW->Release();
  163. }
  164. else
  165. {
  166. IShellLinkA * pslA;
  167. hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkA, &pslA));
  168. if (SUCCEEDED(hr))
  169. {
  170. char szBuffer[MAX_PATH];
  171. SHTCharToAnsi(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer));
  172. pslA->SetPath(szBuffer);
  173. if (pszDescription)
  174. {
  175. SHTCharToAnsi(pszDescription, szBuffer, ARRAYSIZE(szBuffer));
  176. pslA->SetDescription(szBuffer);
  177. }
  178. pslA->Release();
  179. }
  180. }
  181. if (SUCCEEDED(hr))
  182. {
  183. IPersistFile *ppf;
  184. hr = punk->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  185. if (SUCCEEDED(hr))
  186. {
  187. TCHAR szPath[MAX_PATH];
  188. if (!pszFileName)
  189. {
  190. MakePath(szPath, pszFolderPath, pszFileName);
  191. pszFolderPath = szPath;
  192. }
  193. //WCHAR szFolderPath[MAX_PATH];
  194. //SHTCharToUnicode(pszFolderPath, szFolderPath, ARRAYSIZE(szFolderPath));
  195. hr = ppf->Save(pszFolderPath, TRUE);
  196. ppf->Release();
  197. }
  198. }
  199. punk->Release();
  200. }
  201. if (SUCCEEDED(hresCoInit))
  202. CoUninitialize();
  203. return hr;
  204. }
  205. #endif
  206. // FindPartialPath
  207. //
  208. // Returns a pointer to the file title preceded by nDepth levels of
  209. // directory names (zero == file title only). If the path has less than
  210. // nDepth levels, a pointer to the beginning of the string is returned.
  211. // NULL is never returned.
  212. //
  213. // 10/18/1996 KenSh Created
  214. //
  215. LPTSTR FindPartialPath(LPCTSTR pszFullPath, int nDepth)
  216. {
  217. #define MAX_SLASHES (MAX_PATH / 2) // No more slashes than this in the path
  218. LPTSTR pch;
  219. LPTSTR rgpchSlashes[MAX_SLASHES];
  220. int cSlashes = 0;
  221. for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch))
  222. {
  223. if (*pch == _T('\\') || *pch == _T('/'))
  224. {
  225. rgpchSlashes[cSlashes++] = pch;
  226. }
  227. }
  228. if (cSlashes > nDepth)
  229. {
  230. return rgpchSlashes[cSlashes-nDepth-1] + 1;
  231. }
  232. else
  233. {
  234. // Not enough slashes - return the full path
  235. return (LPTSTR)pszFullPath;
  236. }
  237. }
  238. // FindFileTitle
  239. //
  240. // Given a full pathname or URL, returns a pointer to the file title. If
  241. // the given does not contain path information, a pointer to the beginning
  242. // of the string is returned. NULL is never returned.
  243. //
  244. // 4/19/1996 KenSh Created
  245. //
  246. LPTSTR FindFileTitle(LPCTSTR pszFullPath)
  247. {
  248. LPTSTR pch;
  249. LPTSTR pchSlash = NULL;
  250. for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch))
  251. {
  252. if (*pch == _T('\\') || *pch == _T('/'))
  253. pchSlash = pch;
  254. }
  255. if (pchSlash)
  256. return pchSlash+1;
  257. else
  258. return (LPTSTR)pszFullPath;
  259. }
  260. // FindExtension
  261. //
  262. // Given a path, returns a pointer to its file extension (the character
  263. // following the "."). If there is no extension, the return value is
  264. // a pointer to the end of the string ('\0' character).
  265. //
  266. // 3/04/1996 KenSh Created
  267. // 11/17/1997 KenSh Fixed case where path has "." but the filename doesn't
  268. //
  269. LPTSTR FindExtension(LPCTSTR pszFileName)
  270. {
  271. // Start with the file title
  272. LPTSTR pch = FindFileTitle(pszFileName);
  273. LPTSTR pchDot = NULL;
  274. // Find the last "." in the filename
  275. while (*pch)
  276. {
  277. if (*pch == _T('.'))
  278. pchDot = pch;
  279. pch = CharNext(pch);
  280. }
  281. if (pchDot)
  282. return pchDot+1;
  283. else
  284. return pch; // empty string
  285. }
  286. // IsFullPath
  287. //
  288. // Returns nonzero if the given path is a fully qualified path starting
  289. // with "X:\" or "\\"
  290. //
  291. // 5/19/1999 KenSh Created
  292. //
  293. BOOL IsFullPath(LPCTSTR pszPath)
  294. {
  295. if ((*pszPath == '\\' && *(pszPath+1) == '\\') ||
  296. (*pszPath != '\0' && *(pszPath+1) == ':' && *(pszPath+2) == '\\'))
  297. {
  298. return TRUE;
  299. }
  300. else
  301. {
  302. return FALSE;
  303. }
  304. }
  305. void ShowDlgItem(HWND hwndDlg, int nCtrlID, int nCmdShow)
  306. {
  307. ShowWindow(GetDlgItem(hwndDlg, nCtrlID), nCmdShow);
  308. }
  309. // GetDlgItemRect
  310. //
  311. // Retrieves the bounding rect of the dialog item relative to the top left
  312. // corner of the dialog's client area.
  313. //
  314. // 10/13/1997 KenSh Created
  315. //
  316. HWND GetDlgItemRect(HWND hwndDlg, int nCtrlID, RECT* pRect)
  317. {
  318. ASSERT(IsWindow(hwndDlg));
  319. ASSERT(pRect);
  320. HWND hwndCtrl = GetDlgItem(hwndDlg, nCtrlID);
  321. if (hwndCtrl != NULL)
  322. {
  323. POINT ptTopLeft;
  324. ptTopLeft.x = ptTopLeft.y = 0;
  325. ClientToScreen(hwndDlg, &ptTopLeft);
  326. GetWindowRect(hwndCtrl, pRect);
  327. OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y);
  328. }
  329. return hwndCtrl;
  330. }
  331. // GetRelativeRect
  332. //
  333. // Retrieves the bounding rect of the window relative to the top left
  334. // corner of its parent client area.
  335. //
  336. // 1/04/2000 KenSh Created
  337. //
  338. void GetRelativeRect(HWND hwndCtrl, RECT* pRect)
  339. {
  340. ASSERT(IsWindow(hwndCtrl));
  341. ASSERT(pRect != NULL);
  342. HWND hwndParent = GetParent(hwndCtrl);
  343. POINT ptTopLeft = { 0, 0 };
  344. ClientToScreen(hwndParent, &ptTopLeft);
  345. GetWindowRect(hwndCtrl, pRect);
  346. OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y);
  347. }
  348. // SetDlgItemRect
  349. //
  350. // Updates the position and size of a dialog item to the given rectangle,
  351. // in coordinates relative to the top left corner of the dialog's client area.
  352. //
  353. // 3/17/1999 KenSh Created
  354. //
  355. void SetDlgItemRect(HWND hwndDlg, int nCtrlID, CONST RECT* pRect)
  356. {
  357. ASSERT(IsWindow(hwndDlg));
  358. ASSERT(GetDlgItem(hwndDlg, nCtrlID));
  359. ASSERT(pRect);
  360. SetWindowPos(GetDlgItem(hwndDlg, nCtrlID), NULL, pRect->left, pRect->top,
  361. pRect->right - pRect->left, pRect->bottom - pRect->top,
  362. SWP_NOZORDER | SWP_NOACTIVATE);
  363. }
  364. // FormatDlgItemText
  365. //
  366. // Works like wsprintf to change the text of an existing dialog control.
  367. // If pszFormat is non-NULL, it contains the formatting string.
  368. // If pszFormat is NULL, then the existing control text is used as the
  369. // format string.
  370. //
  371. // 9/22/1999 KenSh Created
  372. //
  373. BOOL __cdecl FormatDlgItemText(HWND hwnd, int nCtrlID, LPCTSTR pszFormat, ...)
  374. {
  375. HWND hwndCtrl = GetDlgItem(hwnd, nCtrlID);
  376. if (NULL == hwndCtrl)
  377. return FALSE;
  378. va_list argList;
  379. va_start(argList, pszFormat);
  380. FormatWindowTextV(hwndCtrl, pszFormat, argList);
  381. return TRUE;
  382. }
  383. // FormatWindowTextV
  384. //
  385. // Combines the functionality of wvsprintf with SetWindowText, automatically
  386. // allocating a buffer large enough to hold the expanded string, and freeing
  387. // the buffer after setting the window text.
  388. //
  389. // 9/22/1999 KenSh Created
  390. //
  391. void FormatWindowTextV(HWND hwnd, LPCTSTR pszFormat, va_list argList)
  392. {
  393. LPTSTR pszWindowText = NULL;
  394. if (pszFormat == NULL)
  395. {
  396. int cchWindowText = GetWindowTextLength(hwnd) + 1;
  397. pszWindowText = (LPTSTR)malloc(cchWindowText * sizeof(TCHAR));
  398. if (pszWindowText)
  399. {
  400. GetWindowText(hwnd, pszWindowText, cchWindowText);
  401. pszFormat = pszWindowText;
  402. }
  403. }
  404. if (pszFormat)
  405. {
  406. int cchNeeded = EstimateFormatLength(pszFormat, argList);
  407. LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR));
  408. if (pszBuf)
  409. {
  410. #ifdef UNICODE
  411. wvnsprintf(pszBuf, cchNeeded, pszFormat, argList);
  412. #else
  413. wvsprintf(pszBuf, pszFormat, argList);
  414. #endif
  415. SetWindowText(hwnd, pszBuf);
  416. free(pszBuf);
  417. }
  418. }
  419. if (pszWindowText != NULL)
  420. {
  421. free(pszWindowText);
  422. }
  423. }
  424. LPTSTR __cdecl LoadStringFormat(HINSTANCE hInstance, UINT nStringID, ...)
  425. {
  426. LPTSTR pszBuf = NULL;
  427. LPTSTR pszFormat = LoadStringAlloc(hInstance, nStringID);
  428. if (pszFormat)
  429. {
  430. va_list argList;
  431. va_start(argList, nStringID);
  432. int cchNeeded = EstimateFormatLength(pszFormat, argList);
  433. LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR));
  434. if (pszBuf)
  435. {
  436. #ifdef UNICODE
  437. wvnsprintf(pszBuf, cchNeeded, pszFormat, argList);
  438. #else
  439. wvsprintf(pszBuf, pszFormat, argList);
  440. #endif
  441. }
  442. free(pszFormat);
  443. }
  444. return pszBuf;
  445. }
  446. // EstimateFormatLength
  447. //
  448. // Estimates the number of characters needed to format the string,
  449. // including the terminating NULL.
  450. //
  451. // 9/22/1999 KenSh Created
  452. //
  453. int EstimateFormatLength(LPCTSTR pszFormat, va_list argList)
  454. {
  455. ASSERT(pszFormat != NULL);
  456. int cch = lstrlen(pszFormat) + 1;
  457. for (LPCTSTR pch = pszFormat; *pch; pch = CharNext(pch))
  458. {
  459. if (*pch == _T('%'))
  460. {
  461. pch++;
  462. if (*pch == _T('-')) // we don't care about left vs. right justification
  463. pch++;
  464. if (*pch == _T('#')) // prefix hex numbers with 0x
  465. {
  466. pch++;
  467. cch += 2;
  468. }
  469. if (*pch == _T('0')) // pads with zeroes instead of spaces
  470. pch++;
  471. if (MyIsDigit(*pch))
  472. {
  473. cch += MyAtoi(pch); // this overshoots but that's ok
  474. do
  475. {
  476. pch++;
  477. } while (MyIsDigit(*pch));
  478. }
  479. switch (*pch)
  480. {
  481. case _T('s'):
  482. cch += lstrlen(va_arg(argList, LPCTSTR)) - 2;
  483. break;
  484. case _T('c'):
  485. case _T('C'):
  486. va_arg(argList, TCHAR);
  487. cch -= 1;
  488. break;
  489. case _T('d'):
  490. va_arg(argList, int);
  491. cch += INT_CCH_MAX - 2;
  492. break;
  493. case _T('h'):
  494. pch++;
  495. ASSERT(*pch == _T('d') || *pch == _T('u')); // other forms of 'h' not implemented!
  496. cch += SHORT_CCH_MAX - 2;
  497. break;
  498. case _T('l'):
  499. pch++;
  500. if (*pch == _T('d') || *pch == _T('i'))
  501. cch += LONG_CCH_MAX - 2;
  502. else if (*pch == _T('x') || *pch == _T('X'))
  503. cch += LONGX_CCH_MAX - 2;
  504. else
  505. ASSERT(FALSE); // other forms of 'l' not implemented!
  506. break;
  507. default:
  508. ASSERT(FALSE); // other
  509. break;
  510. }
  511. }
  512. }
  513. return cch;
  514. }
  515. // CenterWindow
  516. //
  517. // Centers the given window relative to its parent window. If the parent
  518. // is NULL, the window is centered over the desktop excluding the taskbar.
  519. //
  520. // 9/24/1999 KenSh Created
  521. //
  522. void CenterWindow(HWND hwnd)
  523. {
  524. RECT rcWindow;
  525. RECT rcDesktop;
  526. GetWindowRect(hwnd, &rcWindow);
  527. HWND hwndParent = GetParent(hwnd);
  528. if (hwndParent == NULL)
  529. {
  530. SystemParametersInfo(SPI_GETWORKAREA, sizeof(RECT), &rcDesktop, FALSE);
  531. }
  532. else
  533. {
  534. GetWindowRect(hwndParent, &rcDesktop);
  535. }
  536. int cxWindow = rcWindow.right - rcWindow.left;
  537. int cyWindow = rcWindow.bottom - rcWindow.top;
  538. int x = (rcDesktop.left + rcDesktop.right - cxWindow) / 2;
  539. int y = (rcDesktop.top + rcDesktop.bottom - cyWindow) / 2;
  540. SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  541. }
  542. // FindResourceString
  543. //
  544. // Returns a pointer to the given string resource in memory, or NULL
  545. // if the string does not exist. Note that the string is in Unicode,
  546. // and is not NULL-terminated.
  547. //
  548. // 3/17/1999 KenSh Created
  549. //
  550. LPCWSTR FindResourceString(HINSTANCE hInstance, UINT nStringID, int* pcchString, WORD wLangID)
  551. {
  552. ASSERT(pcchString != NULL);
  553. *pcchString = 0;
  554. HRSRC hRsrc = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE((nStringID/16)+1), wLangID);
  555. if (hRsrc == NULL)
  556. return NULL;
  557. DWORD cbStringTable = SizeofResource(hInstance, hRsrc);
  558. HGLOBAL hGlb = LoadResource(hInstance, hRsrc);
  559. LPBYTE pbData = (LPBYTE)LockResource(hGlb);
  560. LPBYTE pbEnd = pbData + cbStringTable;
  561. // Skip strings preceding desired one
  562. int iString = (int)nStringID % 16;
  563. for (int i = 0; i < iString; i++)
  564. {
  565. int cch = (int)*((LPWORD)pbData);
  566. pbData += sizeof(WORD) + (sizeof(WCHAR) * cch);
  567. if (pbData >= pbEnd)
  568. return NULL;
  569. }
  570. if (pbData + sizeof(WORD) >= pbEnd)
  571. return NULL;
  572. *pcchString = (int)*((LPWORD)pbData);
  573. pbData += sizeof(WORD);
  574. return (LPCWSTR)pbData;
  575. }
  576. // GetResourceStringLength
  577. //
  578. // Finds the given string in the string table, and returns its length
  579. // in characters, not including room for the terminating NULL.
  580. //
  581. // History:
  582. //
  583. // 3/17/1999 KenSh Created
  584. //
  585. int GetResourceStringLength(HINSTANCE hInstance, UINT nStringID, WORD wLangID)
  586. {
  587. int cch;
  588. FindResourceString(hInstance, nStringID, &cch, wLangID);
  589. return cch;
  590. }
  591. // LoadStringHelper
  592. //
  593. // Helper function for LoadStringAllocEx.
  594. //
  595. // 2/23/1998 KenSh Created
  596. // 9/27/1999 KenSh changed alloc method from new[] to malloc
  597. // 12/21/1999 KenSh fixed unicode and DBCS bugs
  598. //
  599. int LoadStringHelper(HINSTANCE hInstance, UINT nID, LPTSTR* ppszBuf, int cchBuf, WORD wLangID)
  600. {
  601. int cch, cchCopy;
  602. LPCWSTR pwszString = FindResourceString(hInstance, nID, &cch, wLangID);
  603. if (pwszString == NULL)
  604. return 0;
  605. if (!(*ppszBuf))
  606. {
  607. #ifdef UNICODE
  608. cchBuf = 1 + cch;
  609. #else
  610. cchBuf = 1 + WideCharToMultiByte(CP_ACP, 0, pwszString, cch, NULL, 0, NULL, NULL);
  611. #endif
  612. *ppszBuf = (LPTSTR)malloc(cchBuf * sizeof(TCHAR));
  613. cchCopy = cch;
  614. }
  615. else
  616. {
  617. cchCopy = min(cchBuf-1, cch);
  618. }
  619. if (*ppszBuf)
  620. {
  621. #ifdef UNICODE
  622. CopyMemory(*ppszBuf, pwszString, cchCopy * sizeof(WCHAR));
  623. (*ppszBuf)[cchCopy] = _T('\0');
  624. #else
  625. cchCopy = WideCharToMultiByte(CP_ACP, 0, pwszString, cchCopy, *ppszBuf, cchBuf, NULL, NULL);
  626. (*ppszBuf)[cchCopy] = _T('\0');
  627. #endif
  628. return cchCopy;
  629. }
  630. return 0;
  631. }
  632. // LoadStringAllocEx
  633. //
  634. // Finds the string resource with the given ID and language, allocates a
  635. // buffer using malloc, and copies the string to the buffer. If the
  636. // string is not found, NULL is returned.
  637. //
  638. // 2/24/1998 KenSh Created
  639. //
  640. LPTSTR LoadStringAllocEx(HINSTANCE hInstance, UINT nID, WORD wLangID)
  641. {
  642. LPTSTR psz = NULL;
  643. LoadStringHelper(hInstance, nID, &psz, 0, wLangID);
  644. return psz;
  645. }
  646. void TrimLeft(LPTSTR pszText)
  647. {
  648. LPTSTR pch2 = pszText; // will point to first non-space
  649. while (*pch2 == _T(' '))
  650. pch2++;
  651. // If there's leading space, slide the string down
  652. if (pch2 != pszText)
  653. {
  654. // Note: it's safe to skip CharNext here, since '\0' is immune to DBCS
  655. while (_T('\0') != (*pszText++ = *pch2++))
  656. NULL;
  657. }
  658. }
  659. void TrimRight(LPTSTR pszText)
  660. {
  661. LPTSTR pch2 = NULL; // will point to beginning of trailing space
  662. while (*pszText != _T('\0'))
  663. {
  664. if (*pszText == _T(' '))
  665. {
  666. if (pch2 == NULL)
  667. pch2 = pszText;
  668. }
  669. else
  670. {
  671. // found more non-space, reset the trailing-space pointer
  672. pch2 = NULL;
  673. }
  674. pszText = CharNext(pszText);
  675. }
  676. // Truncate the trailing spaces, if any
  677. if (pch2 != NULL)
  678. *pch2 = _T('\0');
  679. }
  680. // RegDeleteKeyAndSubKeys
  681. //
  682. // Does what RegDeleteKey should do. (Actually a single call to RegDeleteKey
  683. // will do this in Win95, but not in NT, according to the docs. Should see
  684. // if this gets fixed in NT5.)
  685. //
  686. // 2/24/1998 KenSh Created
  687. //
  688. DWORD RegDeleteKeyAndSubKeys(HKEY hkey, LPCTSTR pszSubKey)
  689. {
  690. #if 0 // This might be faster in Win95 than doing it manually, but bigger.
  691. OSVERSIONINFO osvi;
  692. osvi.dwOSVersionInfoSize = sizeof(osvi);
  693. GetVersionEx(&osvi);
  694. if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
  695. #endif
  696. {
  697. HKEY hSubKey;
  698. LONG err = RegOpenKeyEx(hkey, pszSubKey, 0, KEY_ALL_ACCESS, &hSubKey);
  699. if (ERROR_SUCCESS == err)
  700. {
  701. DWORD dwNumSubKeys;
  702. RegQueryInfoKey(hSubKey, NULL, NULL, NULL, &dwNumSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  703. for (DWORD iSubKey = dwNumSubKeys; iSubKey > 0; iSubKey--)
  704. {
  705. TCHAR szSubKey[260];
  706. DWORD cchSubKey = _countof(szSubKey);
  707. if (ERROR_SUCCESS == RegEnumKeyEx(hSubKey, iSubKey-1, szSubKey, &cchSubKey, NULL, NULL, NULL, NULL))
  708. {
  709. RegDeleteKeyAndSubKeys(hSubKey, szSubKey);
  710. }
  711. }
  712. RegCloseKey(hSubKey);
  713. }
  714. }
  715. return RegDeleteKey(hkey, pszSubKey);
  716. }
  717. // LoadFile
  718. //
  719. // Loads the file and null-terminates the copy in memory. The memory
  720. // is allocated via malloc().
  721. //
  722. // 4/05/1996 KenSh Created
  723. // 8/27/1996 KenSh Improved error checking
  724. // 4/21/1997 KenSh Tightened up a bit
  725. // 2/01/1998 KenSh Append a null-terminating byte
  726. // 9/29/1999 KenSh use malloc instead of new []
  727. //
  728. LPBYTE LoadFile(LPCTSTR pszFileName, DWORD* pdwFileSize /*=NULL*/)
  729. {
  730. HANDLE hFile;
  731. LPBYTE pData = NULL;
  732. DWORD dwFileSize = 0;
  733. hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
  734. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
  735. if (hFile == INVALID_HANDLE_VALUE)
  736. goto done;
  737. dwFileSize = GetFileSize(hFile, NULL);
  738. ASSERT(dwFileSize != 0xFFFFFFFF); // this shouldn't ever happen for valid hFile
  739. pData = (LPBYTE)malloc(dwFileSize + 1);
  740. if (!pData)
  741. goto done;
  742. DWORD cbRead;
  743. if (!ReadFile(hFile, pData, dwFileSize, &cbRead, NULL))
  744. {
  745. free(pData);
  746. pData = NULL;
  747. goto done;
  748. }
  749. pData[dwFileSize] = 0;
  750. done:
  751. if (pdwFileSize)
  752. *pdwFileSize = dwFileSize;
  753. if (hFile != INVALID_HANDLE_VALUE)
  754. CloseHandle(hFile);
  755. return pData;
  756. }
  757. // DrawHollowRect
  758. //
  759. // Draws a hollow rectangle in the current background color.
  760. //
  761. // 2/06/1998 KenSh Created
  762. //
  763. void DrawHollowRect(HDC hdc, const RECT* pRect, int cxLeft, int cyTop, int cxRight, int cyBottom)
  764. {
  765. RECT rcCopy;
  766. RECT rcNewCoords;
  767. int i;
  768. CopyRect(&rcCopy, pRect);
  769. SetRect(&rcNewCoords,
  770. pRect->right - cxRight,
  771. pRect->bottom - cyBottom,
  772. pRect->left + cxLeft,
  773. pRect->top + cyTop);
  774. // Do each side in turn : right, bottom, left, top
  775. for (i = 0; i < 4; i++)
  776. {
  777. LONG coordSave = ((LONG*)&rcCopy)[i];
  778. ((LONG*)&rcCopy)[i] = ((LONG*)&rcNewCoords)[i];
  779. DrawFastRect(hdc, &rcCopy);
  780. ((LONG*)&rcCopy)[i] = coordSave;
  781. }
  782. }
  783. void DrawFastRect(HDC hdc, const RECT* pRect)
  784. {
  785. COLORREF crTextSave = SetTextColor(hdc, GetBkColor(hdc));
  786. ExtTextOut(hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, pRect, TEXT(" "), 1, NULL);
  787. SetTextColor(hdc, crTextSave);
  788. }
  789. int GetFontHeight(HFONT hFont)
  790. {
  791. HDC hdcT = GetDC(NULL);
  792. HFONT hFontSave = (HFONT)SelectObject(hdcT, hFont);
  793. TEXTMETRIC tm;
  794. GetTextMetrics(hdcT, &tm);
  795. SelectObject(hdcT, hFontSave);
  796. ReleaseDC(NULL, hdcT);
  797. return tm.tmHeight;
  798. }
  799. HRESULT MyGetSpecialFolderPath(int nFolder, LPTSTR pszPath)
  800. {
  801. LPITEMIDLIST pidl;
  802. HRESULT hr;
  803. if (SUCCEEDED(hr = SHGetSpecialFolderLocation(NULL, nFolder, &pidl)))
  804. {
  805. hr = SHGetPathFromIDList(pidl, pszPath) ? S_OK : E_FAIL;
  806. LPMALLOC pMalloc;
  807. if (SUCCEEDED(SHGetMalloc(&pMalloc)))
  808. {
  809. pMalloc->Free(pidl);
  810. pMalloc->Release();
  811. }
  812. }
  813. return hr;
  814. }