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.

2109 lines
53 KiB

  1. /*++
  2. Copyright (c) 1989-2001 Microsoft Corporation
  3. Module Name:
  4. utils.cpp
  5. Abstract:
  6. Various utility functions
  7. Author:
  8. kinshu created July 2, 2001
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "uxtheme.h" // needed for tab control theme support
  13. extern "C" {
  14. BOOL ShimdbcExecute(LPCWSTR lpszCmdLine);
  15. }
  16. //////////////////////// Extern variables /////////////////////////////////////
  17. extern TAG g_Attributes[];
  18. extern HANDLE g_arrhEventNotify[];
  19. extern HANDLE g_hThreadWait;
  20. extern HKEY g_hKeyNotify[];
  21. extern HWND g_hDlg;
  22. extern HINSTANCE g_hInstance;
  23. extern TCHAR g_szAppPath[MAX_PATH];
  24. extern BOOL g_bDeletingEntryTree;
  25. ///////////////////////////////////////////////////////////////////////////////
  26. ///////////////////////// Defines And Typedefs ////////////////////////////////
  27. typedef void (CALLBACK *PFN_SHIMFLUSHCACHE)(HWND, HINSTANCE, LPSTR, int);
  28. ///////////////////////////////////////////////////////////////////////////////
  29. ///////////////////////// Function Declarations //////////////////////////////
  30. BOOL
  31. WriteXML(
  32. CSTRING& szFilename,
  33. CSTRINGLIST* pString
  34. );
  35. ///////////////////////////////////////////////////////////////////////////////
  36. ///////////////////////// Global variables ////////////////////////////////////
  37. // Process handle of the running exe. Set in InvokeExe
  38. HANDLE g_hTestRunExec;
  39. // The name of the program file that is to be executed
  40. CSTRING g_strTestFile;
  41. // Commandline for the program file that has to be executed
  42. CSTRING g_strCommandLine;
  43. ///////////////////////////////////////////////////////////////////////////////
  44. INT_PTR CALLBACK
  45. TestRunWait(
  46. IN HWND hWnd,
  47. IN UINT uMsg,
  48. IN WPARAM wParam,
  49. IN LPARAM lParam
  50. )
  51. /*++
  52. TestRunWait
  53. Desc: Handler for the Test Run Wait Dialog. This is the dialog that
  54. says "Waiting for Application to Finish"
  55. Params: Standard dialog handler parameters
  56. IN HWND hDlg
  57. IN UINT uMsg
  58. IN WPARAM wParam
  59. IN LPARAM lParam: 0 we do not want to show the wait dialog
  60. Return: Standard dialog handler return
  61. --*/
  62. {
  63. switch (uMsg) {
  64. case WM_INITDIALOG:
  65. if (lParam == 0) {
  66. //
  67. // We do not wish to show the wait dialog
  68. //
  69. SendMessage(hWnd, WM_USER_TESTRUN_NODIALOG, 0, 0);
  70. ShowWindow(hWnd, SW_HIDE);
  71. } else {
  72. ShowWindow(hWnd, SW_SHOWNORMAL);
  73. SetTimer(hWnd, 0, 50, NULL);
  74. }
  75. return TRUE;
  76. case WM_TIMER:
  77. {
  78. //
  79. // Check if the app being test-run has terminated, if yes close the dialog
  80. //
  81. DWORD dwResult = WaitForSingleObject(g_hTestRunExec, 10);
  82. if (dwResult == WAIT_OBJECT_0) {
  83. KillTimer(hWnd, 0);
  84. EndDialog(hWnd, 0);
  85. }
  86. break;
  87. }
  88. case WM_USER_TESTRUN_NODIALOG:
  89. {
  90. //
  91. // Wait till the app being test run is running and then close the dialog
  92. //
  93. WaitForSingleObject(g_hTestRunExec, INFINITE);
  94. EndDialog(hWnd, 0);
  95. }
  96. break;
  97. }
  98. return FALSE;
  99. }
  100. BOOL
  101. InvokeEXE(
  102. IN PCTSTR szEXE,
  103. IN PCTSTR szCommandLine,
  104. IN BOOL bWait,
  105. IN BOOL bDialog,
  106. IN BOOL bConsole,
  107. IN HWND hwndParent = NULL
  108. )
  109. /*++
  110. InvokeEXE
  111. Desc: Creates the process and shows the wait dialog box
  112. Params:
  113. IN PCTSTR szEXE: Name of the program that is being executed
  114. IN PCTSTR szCommandLine: Exe name and the command-line for the exe
  115. IN BOOL bWait: Should we wait till the app finishes?
  116. IN BOOL bDialog: Should we show the wait dialog?
  117. IN BOOL bConsole: If true, then we do not show any window
  118. IN HWND hwndParent (NULL): The parent of the wait window, if it is created
  119. If this is NULL, then we set the main app window as the parent
  120. Return:
  121. TRUE: ShellExecuteEx was successful
  122. FALSE: Otherewise
  123. Notes: If bWait is FALSE, then this function will return immediately, otherwise it will
  124. return when the new process has terminated
  125. --*/
  126. {
  127. BOOL bCreate;
  128. SHELLEXECUTEINFO shEx;
  129. ZeroMemory(&shEx, sizeof(SHELLEXECUTEINFO));
  130. if (hwndParent == NULL) {
  131. hwndParent = g_hDlg;
  132. }
  133. //
  134. // We need to disable the main window. After CreateProcess() the wizard that was
  135. // modal till now starts behaving like a modeless wizard. We do not want the user to change
  136. // selections on the main dialog or start up some other wizard.
  137. //
  138. ENABLEWINDOW(g_hDlg, FALSE);
  139. shEx.cbSize = sizeof(SHELLEXECUTEINFO);
  140. shEx.fMask = SEE_MASK_NOCLOSEPROCESS;
  141. shEx.hwnd = hwndParent;
  142. shEx.lpVerb = TEXT("open");
  143. shEx.lpFile = szEXE;
  144. shEx.lpParameters = szCommandLine;
  145. shEx.nShow = SW_SHOWNORMAL;
  146. bCreate = ShellExecuteEx(&shEx);
  147. if (bCreate && bWait) {
  148. //
  149. // We need to wait till the process has terminated
  150. //
  151. g_hTestRunExec = shEx.hProcess;
  152. //
  153. // If we have to show the wait dialog, bDialog should be TRUE
  154. //
  155. DialogBoxParam(g_hInstance,
  156. MAKEINTRESOURCE(IDD_WAIT),
  157. hwndParent,
  158. TestRunWait,
  159. (LPARAM)bDialog);
  160. //
  161. // Now the app has terminated
  162. //
  163. if (shEx.hProcess) {
  164. CloseHandle(shEx.hProcess);
  165. }
  166. }
  167. //
  168. // Since the process has terminated let us now Enable the main window again
  169. //
  170. ENABLEWINDOW(g_hDlg, TRUE);
  171. return bCreate ? TRUE : FALSE;
  172. }
  173. BOOL
  174. InvokeCompiler(
  175. IN CSTRING& szInCommandLine
  176. )
  177. /*++
  178. InvokeCompiler
  179. Desc: Runs the database compiler: shimdbc. Shimdbc.dll is statically linked
  180. into CompatAdmin.exe
  181. Params:
  182. IN CSTRING& szInCommandLine: Commandline to the compiler
  183. Return:
  184. TRUE: The compiler was executed successfully
  185. FALSE: Otherwise
  186. --*/
  187. {
  188. CSTRING szCommandLine = szInCommandLine;
  189. szCommandLine.Sprintf(TEXT("shimdbc.exe %s"), (LPCTSTR)szInCommandLine);
  190. if (!ShimdbcExecute(szCommandLine)) {
  191. MessageBox(NULL,
  192. CSTRING(IDS_COMPILER_ERROR),
  193. g_szAppName,
  194. MB_ICONERROR);
  195. return FALSE;
  196. }
  197. return TRUE;
  198. }
  199. INT_PTR CALLBACK
  200. TestRunDlg(
  201. IN HWND hWnd,
  202. IN UINT uMsg,
  203. IN WPARAM wParam,
  204. IN LPARAM lParam
  205. )
  206. /*++
  207. TestRunWait
  208. Desc: Dialog proc for the test run dialog box. This dialog box, takes the
  209. name of the program to execute and the commandlines
  210. Params: Standard dialog handler parameters
  211. IN HWND hDlg
  212. IN UINT uMsg
  213. IN WPARAM wParam
  214. IN LPARAM lParam: The PDBENTRY for then entry being test run
  215. Return:
  216. TRUE: Pressed OK
  217. FALSE: Pressed Cancel
  218. --*/
  219. {
  220. static CSTRING s_strExeName;
  221. switch (uMsg) {
  222. case WM_INITDIALOG:
  223. s_strExeName.Release();
  224. if ((PDBENTRY)lParam) {
  225. s_strExeName = ((PDBENTRY)lParam)->strExeName;
  226. }
  227. //
  228. // Set the file name of the program. g_strTestFile is set in TestRun()
  229. //
  230. SetWindowText(GetDlgItem(hWnd, IDC_EXE), g_strTestFile);
  231. //
  232. // Change the OK button status properly
  233. //
  234. SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_EXE, EN_CHANGE), 0);
  235. SHAutoComplete(GetDlgItem(hWnd, IDC_EXE), AUTOCOMPLETE);
  236. //
  237. // Limit the length of the exe path
  238. //
  239. SendMessage(GetDlgItem(hWnd, IDC_EXE),
  240. EM_LIMITTEXT,
  241. (WPARAM)MAX_PATH - 1,
  242. (LPARAM)0);
  243. SendMessage(GetDlgItem(hWnd, IDC_COMMANDLINE),
  244. EM_LIMITTEXT,
  245. (WPARAM)MAX_PATH - 1,
  246. (LPARAM)0);
  247. break;
  248. case WM_COMMAND:
  249. switch (LOWORD(wParam)) {
  250. case IDC_EXE:
  251. {
  252. if (EN_CHANGE == HIWORD(wParam)) {
  253. HWND hwndOkButton = GetDlgItem(hWnd, IDOK);
  254. INT iLength = 0;
  255. TCHAR szExeName[MAX_PATH];
  256. *szExeName = 0;
  257. //
  258. // Disable the OK button if we do not have the complete path
  259. //
  260. GetDlgItemText(hWnd, IDC_EXE, szExeName, ARRAYSIZE(szExeName));
  261. iLength = CSTRING::Trim(szExeName);
  262. if (iLength < 3) {
  263. //
  264. // Cannot be a proper path
  265. //
  266. ENABLEWINDOW(hwndOkButton , FALSE);
  267. } else {
  268. //
  269. // Ok button should be enabled if we have a
  270. // Local path or network path
  271. //
  272. if (NotCompletePath(szExeName)) {
  273. ENABLEWINDOW(hwndOkButton, FALSE);
  274. } else {
  275. ENABLEWINDOW(hwndOkButton, TRUE);
  276. }
  277. }
  278. }
  279. }
  280. break;
  281. case IDC_BROWSE:
  282. {
  283. HWND hwndFocus = GetFocus();
  284. CSTRING szFilename;
  285. TCHAR szBuffer[MAX_PATH] = TEXT("");
  286. GetString(IDS_EXEFILTER, szBuffer, ARRAYSIZE(szBuffer));
  287. if (GetFileName(hWnd,
  288. CSTRING(IDS_FINDEXECUTABLE),
  289. szBuffer,
  290. g_strTestFile,
  291. TEXT(""),
  292. OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST,
  293. TRUE,
  294. szFilename)) {
  295. g_strTestFile = szFilename;
  296. SetWindowText(GetDlgItem(hWnd, IDC_EXE), g_strTestFile);
  297. }
  298. SetFocus(hwndFocus);
  299. break;
  300. }
  301. case IDOK:
  302. {
  303. TCHAR szBuffer[MAX_PATH];
  304. *szBuffer = 0;
  305. GetWindowText(GetDlgItem(hWnd, IDC_EXE), szBuffer, ARRAYSIZE(szBuffer));
  306. //
  307. // Check if we are test running the correct file
  308. //
  309. if (s_strExeName != PathFindFileName(szBuffer)) {
  310. //
  311. // User did not give the complete path of the program being fixed.
  312. //
  313. MessageBox(hWnd, GetString(IDS_DOESNOTMATCH), g_szAppName, MB_ICONWARNING);
  314. break;
  315. }
  316. g_strTestFile = szBuffer;
  317. *szBuffer = 0;
  318. GetWindowText(GetDlgItem(hWnd, IDC_COMMANDLINE), szBuffer, ARRAYSIZE(szBuffer));
  319. g_strCommandLine = szBuffer;
  320. EndDialog(hWnd, 1);
  321. break;
  322. }
  323. case IDCANCEL:
  324. EndDialog(hWnd, 0);
  325. break;
  326. }
  327. }
  328. return FALSE;
  329. }
  330. void
  331. FlushCache(
  332. void
  333. )
  334. /*++
  335. FlushCache
  336. Desc: Calls FlushCache from apphelp.dll to flush the shim cache. We should flush the
  337. cache before doing a test run
  338. --*/
  339. {
  340. PFN_SHIMFLUSHCACHE pShimFlushCache;
  341. TCHAR szLibPath[MAX_PATH * 2];
  342. HMODULE hAppHelp = NULL;
  343. UINT uResult = 0;
  344. K_SIZE k_size = MAX_PATH;
  345. *szLibPath = 0;
  346. uResult = GetSystemDirectory(szLibPath, k_size);
  347. if (uResult == 0 || uResult >= k_size) {
  348. Dbg(dlError, "%s - Failed to Execute GetSystemDirectory. Result was %d", __FUNCTION__, uResult);
  349. return;
  350. }
  351. ADD_PATH_SEPARATOR(szLibPath, ARRAYSIZE(szLibPath));
  352. StringCchCat(szLibPath, ARRAYSIZE(szLibPath), TEXT("apphelp.dll"));
  353. hAppHelp = LoadLibrary(szLibPath);
  354. if (hAppHelp) {
  355. pShimFlushCache = (PFN_SHIMFLUSHCACHE)GetProcAddress(hAppHelp,
  356. "ShimFlushCache");
  357. if (pShimFlushCache) {
  358. pShimFlushCache(NULL, NULL, NULL, 0);
  359. }
  360. FreeLibrary(hAppHelp);
  361. }
  362. }
  363. BOOL
  364. TestRun(
  365. IN PDBENTRY pEntry,
  366. IN OUT CSTRING* pszFile,
  367. IN CSTRING* pszCommandLine,
  368. IN HWND hwndParent,
  369. IN CSTRINGLIST* pstrlXML //(NULL)
  370. )
  371. /*++
  372. TestRun
  373. Desc: Pops up the test run dialog that lets users to test run programs. This is
  374. interface for test running programs
  375. Params:
  376. IN PDBENTRY pEntry: The entry that has to be test-run. We need this variable
  377. because we need to call GetXML which takes this as a param. If pszFile is NULL, then we get
  378. the name of the program file from pEntry->strExeName
  379. IN OUT CSTRING* pszFile: The file name of the program that has to be test-run
  380. IN CSTRING* pszCommandLine: The command line for the program that has to be test-run
  381. IN HWND hwndParent: The intended parent for the actual test-run dialog
  382. IN CSTRINGLIST* pstrlXML (NULL): LUA wizard likes to give the XML generated using
  383. LuapGenerateTrackXML
  384. Return:
  385. TRUE: Success
  386. FALSE: There was some error or the user pressed CANCEL in the test run dialog
  387. Notes: pEntry will be NULL if we have to run an app from the disk search window,
  388. but we no longer allow that.
  389. --*/
  390. {
  391. CSTRING strCommandLine, strFile, strSdbPath;
  392. CSTRINGLIST strlXMLTemp;
  393. TCHAR szSystemDir[MAX_PATH * 2];
  394. TCHAR szLogPath[MAX_PATH * 2];
  395. HWND hwndFocus = GetFocus();
  396. BOOL bResult = FALSE;
  397. UINT uResult = 0;
  398. g_strTestFile.Release();
  399. g_strCommandLine.Release();
  400. *szSystemDir = 0;
  401. if (pszFile && pszFile->Length()) {
  402. //
  403. // Set the name of the program that has to be executed. This will be used by the test-run dialog
  404. //
  405. g_strTestFile = *pszFile;
  406. } else {
  407. //
  408. // We have not been given the complete path, so we should get the name of the exe
  409. // from pEntry
  410. //
  411. if (pEntry == NULL) {
  412. goto End;
  413. }
  414. //
  415. // Set the name of the program that has to be executed. This will be used by the test-run dialog
  416. //
  417. g_strTestFile = pEntry->strExeName;
  418. }
  419. if (pszCommandLine && pszCommandLine->Length()) {
  420. //
  421. // Set the name of the command line for the program that has to be executed.
  422. // This will be used by the test-run dialog
  423. //
  424. g_strCommandLine = *pszCommandLine;
  425. }
  426. //
  427. // Show the test run dialog
  428. //
  429. if (0 == DialogBoxParam(g_hInstance,
  430. MAKEINTRESOURCE(IDD_TESTRUN),
  431. hwndParent,
  432. TestRunDlg,
  433. (LPARAM)pEntry)) {
  434. //
  435. // Cancel pressed
  436. //
  437. goto End;
  438. }
  439. *szLogPath = 0;
  440. uResult = GetSystemWindowsDirectory(szLogPath, MAX_PATH);
  441. if (uResult == 0 || uResult >= MAX_PATH) {
  442. assert(FALSE);
  443. Dbg(dlError, "TestRun", "GetSystemWindowsDirectory failed");
  444. bResult = FALSE;
  445. goto End;
  446. }
  447. //
  448. // Set the SHIM_FILE_LOG env. variable so that we can show the shim log
  449. //
  450. ADD_PATH_SEPARATOR(szLogPath, ARRAYSIZE(szLogPath));
  451. StringCchCat(szLogPath, ARRAYSIZE(szLogPath), TEXT("AppPatch\\") SHIM_FILE_LOG_NAME);
  452. //
  453. // Delete previous log file if any
  454. //
  455. DeleteFile(szLogPath);
  456. SetEnvironmentVariable(TEXT("SHIM_FILE_LOG"), SHIM_FILE_LOG_NAME);
  457. //
  458. // If this is a system database, we do not need to create/install an sdb
  459. // OR If we are calling this TestRun from the disk search window also then we do not
  460. // need to get any xml If we are calling TestRun from the disk search window,
  461. // then we will already have the complete path (but not the pointer to the entry)
  462. // and in that case pEtnry can be NULL
  463. //
  464. if ((g_pPresentDataBase && g_pPresentDataBase->type == DATABASE_TYPE_GLOBAL)
  465. || pEntry == NULL) {
  466. //
  467. // Flush the shim cache, so that we do not get the previous fixes. We are flushing it here
  468. // because we are not installing the test database as the entry resides in the system database
  469. //
  470. FlushCache();
  471. if (!InvokeEXE((LPCTSTR)g_strTestFile, (LPCTSTR)g_strCommandLine, TRUE, TRUE, TRUE, hwndParent)) {
  472. MessageBox(g_hDlg,
  473. CSTRING(IDS_ERROREXECUTE),
  474. g_szAppName,
  475. MB_ICONERROR);
  476. bResult = FALSE;
  477. goto EXIT;
  478. }
  479. //
  480. // We are done, now eject...
  481. //
  482. return TRUE;
  483. }
  484. if (pstrlXML == NULL) {
  485. //
  486. // LUA wizard will provides its own XML, for other cases we must get that
  487. //
  488. BOOL bReturn = GetXML(pEntry, FALSE, &strlXMLTemp, g_pPresentDataBase);
  489. if (!bReturn) {
  490. assert(FALSE);
  491. return FALSE;
  492. }
  493. pstrlXML = &strlXMLTemp;
  494. }
  495. uResult = GetSystemWindowsDirectory(szSystemDir, MAX_PATH);
  496. if (uResult == 0 || uResult >= MAX_PATH) {
  497. assert(FALSE);
  498. goto End;
  499. }
  500. //
  501. // Write the XML into AppPatch\\Test.XML
  502. //
  503. ADD_PATH_SEPARATOR(szSystemDir, ARRAYSIZE(szSystemDir));
  504. strFile.Sprintf(TEXT("%sAppPatch\\Test.XML"), szSystemDir);
  505. if (!WriteXML(strFile, pstrlXML)) {
  506. MessageBox(g_hDlg,
  507. CSTRING(IDS_UNABLETOSAVETEMP),
  508. g_szAppName,
  509. MB_OK);
  510. goto End;
  511. }
  512. strCommandLine.Sprintf(TEXT("custom \"%sAppPatch\\Test.XML\" \"%sAppPatch\\Test.SDB\""),
  513. szSystemDir,
  514. szSystemDir);
  515. if (!InvokeCompiler(strCommandLine)) {
  516. MessageBox(g_hDlg,
  517. CSTRING(IDS_ERRORCOMPILE),
  518. g_szAppName,
  519. MB_ICONERROR);
  520. goto End;
  521. }
  522. //
  523. // No need to flush the shim cache, it is done when we install a database,
  524. // sdbinst.exe does it for us.
  525. //
  526. // Note the space after AppPatch\\Test.SDB
  527. strSdbPath.Sprintf(TEXT("%sAppPatch\\Test.SDB "),(LPCTSTR)szSystemDir);
  528. //
  529. // Install the test database
  530. //
  531. if (!InstallDatabase(strSdbPath, TEXT("-q"), FALSE)) {
  532. MessageBox(g_hDlg,
  533. CSTRING(IDS_ERRORINSTALL),
  534. g_szAppName,
  535. MB_ICONERROR);
  536. goto EXIT;
  537. }
  538. //
  539. // Now execute the app to be test run
  540. //
  541. if (!InvokeEXE((LPCTSTR)g_strTestFile, (LPCTSTR)g_strCommandLine, TRUE, TRUE, TRUE, hwndParent)) {
  542. MessageBox(g_hDlg,
  543. CSTRING(IDS_ERROREXECUTE),
  544. g_szAppName,
  545. MB_ICONERROR);
  546. goto EXIT;
  547. }
  548. //
  549. // Uninstall the test database
  550. //
  551. if (!InstallDatabase(strSdbPath, TEXT("-q -u"), FALSE)) {
  552. MessageBox(g_hDlg,
  553. CSTRING(IDS_ERRORUNINSTALL),
  554. g_szAppName,
  555. MB_ICONERROR);
  556. goto EXIT;
  557. }
  558. bResult = TRUE;
  559. EXIT:
  560. strCommandLine.Sprintf(TEXT("%sAppPatch\\Test.XML"), szSystemDir);
  561. DeleteFile(strCommandLine);
  562. strCommandLine.Sprintf(TEXT("%sAppPatch\\Test.SDB"), szSystemDir);
  563. DeleteFile(strCommandLine);
  564. //
  565. // If caller wants it, then return the app path that we executed. Caller might need it
  566. // because he was not having the compete path
  567. //
  568. if (bResult && pszFile) {
  569. *pszFile = g_strTestFile;
  570. }
  571. End:
  572. return bResult;
  573. }
  574. VOID
  575. FormatVersion(
  576. IN ULONGLONG ullBinVer,
  577. OUT PTSTR pszText,
  578. IN INT chBuffSize
  579. )
  580. /*++
  581. FormatVersion
  582. Desc: Formats a LARGE_INTEGER into a.b.c.d format
  583. Params:
  584. IN LARGE_INTEGER liBinVer: The LARGE_INTEGER to format
  585. OUT LPTSTR pszText: The buffer that will store the complete formatted string
  586. IN INT chBuffSize: The size of the buffer in characters
  587. Return:
  588. void
  589. --*/
  590. {
  591. WORD dwWord = 0;
  592. TCHAR szTemp[10];
  593. if (chBuffSize < 16) {
  594. assert(FALSE);
  595. return;
  596. }
  597. *szTemp = 0;
  598. dwWord = WORD(ullBinVer >> 48);
  599. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d"), dwWord);
  600. StringCchCat(pszText, chBuffSize, szTemp);
  601. dwWord = (WORD)(ullBinVer >> 32);
  602. if (dwWord == 0xFFFF) {
  603. return;
  604. }
  605. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
  606. StringCchCat(pszText, chBuffSize, szTemp);
  607. dwWord = (WORD)(ullBinVer >> 16);
  608. if (dwWord == 0xFFFF) {
  609. return;
  610. }
  611. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
  612. StringCchCat(pszText, chBuffSize, szTemp);
  613. dwWord = (WORD)(ullBinVer);
  614. if (dwWord == 0xFFFF) {
  615. return;
  616. }
  617. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
  618. StringCchCat(pszText, chBuffSize, szTemp);
  619. }
  620. BOOL
  621. InstallDatabase(
  622. IN CSTRING& strPath,
  623. IN PCTSTR szOptions,
  624. IN BOOL bShowDialog
  625. )
  626. /*++
  627. InstallDatabase
  628. Desc: Installs or Uninstalls a database using sdbinst.exe. This guy lives in the system32 dir.
  629. Params:
  630. IN CSTRING& strPath: The path of the database (.sdb) file
  631. IN PCTSTR szOptions: The options to be passed to sdbinst.exe
  632. IN BOOL bShowDialog: Should we show the dialog after the install/uninstall is over?
  633. We do not want to show that when we are doing a test run and we have to install the database
  634. Return:
  635. TRUE: Success
  636. FALSE: There was some error
  637. --*/
  638. {
  639. TCHAR szSystemDir[MAX_PATH];
  640. CSTRING strSdbInstCommandLine;
  641. CSTRING strSdbInstExe;
  642. UINT uResult = 0;
  643. *szSystemDir = 0;
  644. uResult = GetSystemDirectory(szSystemDir, MAX_PATH);
  645. if (uResult == 0 || uResult >= MAX_PATH) {
  646. assert(FALSE);
  647. return FALSE;
  648. }
  649. ADD_PATH_SEPARATOR(szSystemDir, ARRAYSIZE(szSystemDir));
  650. strSdbInstExe.Sprintf(TEXT("%ssdbinst.exe"), szSystemDir);
  651. strSdbInstCommandLine.Sprintf(TEXT("%s \"%s\" "),
  652. szOptions,
  653. strPath.pszString);
  654. BOOL bOk = TRUE;
  655. HWND hwndFocus = GetFocus();
  656. //
  657. // We do not want the installed database list and tree items to get refreshed
  658. // when we are (un)installing
  659. // a database because of Test Run. If user is actually (un)installing a database
  660. // then we manually refresh the list in the handler for the corresonding WM_COMMAND
  661. //
  662. g_bUpdateInstalledRequired = FALSE;
  663. //
  664. // Stall the thread that refreshes the installed databases list and tree items
  665. //
  666. while (SuspendThread(g_hThreadWait) == -1) {
  667. ;
  668. }
  669. //
  670. // Call sdbinst.exe
  671. //
  672. if (!InvokeEXE((LPCTSTR)strSdbInstExe, (LPCTSTR)strSdbInstCommandLine, TRUE, FALSE, FALSE, g_hDlg)) {
  673. MessageBox(g_hDlg,
  674. CSTRING(IDS_ERRORINSTALL),
  675. g_szAppName,
  676. MB_ICONERROR);
  677. bOk = FALSE;
  678. } else {
  679. //
  680. // Show the Dialog only if quiet mode is off
  681. //
  682. if (bShowDialog) {
  683. CSTRING strMessage;
  684. if (_tcschr(szOptions, TEXT('u')) || _tcschr(szOptions, TEXT('g'))) {
  685. //
  686. // Uninstalling database
  687. //
  688. strMessage.Sprintf(GetString(IDS_UINSTALL),
  689. g_pPresentDataBase->strName);
  690. } else {
  691. //
  692. // Installing database
  693. //
  694. strMessage.Sprintf(GetString(IDS_INSTALL),
  695. g_pPresentDataBase->strName);
  696. }
  697. MessageBox(g_hDlg, strMessage.pszString, g_szAppName, MB_ICONINFORMATION);
  698. }
  699. }
  700. //
  701. // Listen for app compat regsitry changes
  702. //
  703. RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
  704. TRUE,
  705. REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
  706. REG_NOTIFY_CHANGE_LAST_SET,
  707. g_arrhEventNotify[IND_ALLUSERS],
  708. TRUE);
  709. RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
  710. TRUE,
  711. REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
  712. REG_NOTIFY_CHANGE_LAST_SET,
  713. g_arrhEventNotify[IND_PERUSER],
  714. TRUE);
  715. SetFocus(hwndFocus);
  716. //
  717. // Resume the thread that refreshes the installed databases list and tree items
  718. //
  719. ResumeThread(g_hThreadWait);
  720. return bOk;
  721. }
  722. void
  723. CenterWindow(
  724. IN HWND hParent,
  725. IN HWND hWnd
  726. )
  727. /*++
  728. CenterWindow
  729. Desc: Centers a dialog wrt to its parent
  730. Params:
  731. IN HWND hParent: The parent of the dialog to center
  732. IN HWND hWnd: The dialog to center
  733. Return:
  734. void
  735. --*/
  736. {
  737. RECT rRect;
  738. RECT rParentRect;
  739. GetWindowRect(hWnd, &rRect);
  740. GetWindowRect(hParent, &rParentRect);
  741. //
  742. // Compute actual width and height
  743. //
  744. rRect.right -= rRect.left;
  745. rRect.bottom -= rRect.top;
  746. rParentRect.right -= rParentRect.left;
  747. rParentRect.bottom -= rParentRect.top;
  748. int nX;
  749. int nY;
  750. //
  751. // Resolve X, Y location required to center whole window.
  752. //
  753. nX = (rParentRect.right - rRect.right) / 2;
  754. nY = (rParentRect.bottom - rRect.bottom) / 2;
  755. //
  756. // Move the window to the center location.
  757. //
  758. MoveWindow(hWnd,
  759. rRect.left + nX,
  760. rRect.top + nY,
  761. rRect.right,
  762. rRect.bottom,
  763. TRUE);
  764. }
  765. int
  766. CDECL
  767. MSGF(
  768. IN HWND hwndParent,
  769. IN PCTSTR pszCaption,
  770. IN UINT uType,
  771. IN PCTSTR pszFormat,
  772. ...
  773. )
  774. /*++
  775. MSGF
  776. Desc: Variable argument MessageBox
  777. Params:
  778. IN HWND hwndParent: The parent for the message box
  779. IN PCTSTR pszCaption: The caption for the message box
  780. IN UINT uType: The messagebox type param
  781. IN PCTSTR pszFormat: The format string
  782. Return: Whatever MessageBox() returns
  783. --*/
  784. {
  785. TCHAR szBuffer[1024];
  786. *szBuffer = 0;
  787. va_list pArgList;
  788. va_start(pArgList, pszFormat);
  789. StringCchVPrintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, pArgList);
  790. va_end(pArgList);
  791. return MessageBox(hwndParent,
  792. szBuffer,
  793. pszCaption,
  794. uType);
  795. }
  796. void
  797. EnableTabBackground(
  798. IN HWND hDlg
  799. )
  800. /*++
  801. EnableTabBackground
  802. Desc: Makes the back ground of a dialog blend with the tab background. Enables the texture
  803. Params:
  804. IN HWND hDlg: The dialog box to whose back ground we want to change
  805. Return:
  806. void
  807. --*/
  808. {
  809. PFNEnableThemeDialogTexture pFnEnableThemeDialogTexture;
  810. HMODULE hUxTheme;
  811. TCHAR szThemeManager[MAX_PATH * 2];
  812. UINT uResult = 0;
  813. *szThemeManager = 0;
  814. uResult = GetSystemDirectory(szThemeManager, MAX_PATH);
  815. if (uResult == 0 || uResult >= MAX_PATH) {
  816. assert(FALSE);
  817. return;
  818. }
  819. ADD_PATH_SEPARATOR(szThemeManager, ARRAYSIZE(szThemeManager));
  820. StringCchCat(szThemeManager, ARRAYSIZE(szThemeManager), _T("uxtheme.dll"));
  821. hUxTheme = (HMODULE)LoadLibrary(szThemeManager);
  822. if (hUxTheme) {
  823. pFnEnableThemeDialogTexture = (PFNEnableThemeDialogTexture)
  824. GetProcAddress(hUxTheme, "EnableThemeDialogTexture");
  825. if (pFnEnableThemeDialogTexture) {
  826. pFnEnableThemeDialogTexture(hDlg, 4 /*ETDT_USETABTEXTURE*/);
  827. }
  828. FreeLibrary(hUxTheme);
  829. }
  830. }
  831. int
  832. TagToIndex(
  833. IN TAG tag
  834. )
  835. /*++
  836. TagToIndex
  837. Desc: Gets the The index in the attribute info array (g_rgAttributeTags)
  838. Return: The index in the attribute info array (g_rgAttributeTags), if found.
  839. -1: Otherwise
  840. Note:
  841. --*/
  842. {
  843. int i;
  844. int iAttrCount = (int)ATTRIBUTE_COUNT;
  845. for (i = 0; i < iAttrCount; i++) {
  846. if (tag == g_Attributes[i]) {
  847. return i;
  848. }
  849. }
  850. return -1;
  851. }
  852. PTSTR
  853. GetString(
  854. IN UINT iResource,
  855. OUT PTSTR pszStr, //(NULL)
  856. IN INT nLength //(0) bytes
  857. )
  858. /*++
  859. GetString
  860. Desc: Wrapper for LoadString. If pszStr == NULL, then loads the resouce string in a static
  861. TCHAR[1024] and returns the pointer to that.
  862. Params:
  863. IN UINT iResource: The string resource ID
  864. OUT PTSTR pszStr (NULL): The buffer in which we might need to read
  865. in the string resource
  866. IN INT nLength (0) bytes: If pszStr is not NULL then this will contain
  867. the size of the buffer in bytes.
  868. Return:
  869. The pointer to the string read.
  870. --*/
  871. {
  872. static TCHAR s_szString[1024];
  873. if (NULL == pszStr) {
  874. *s_szString = 0;
  875. LoadString(g_hInstance, iResource, s_szString, ARRAYSIZE(s_szString));
  876. return s_szString;
  877. }
  878. *pszStr = 0;
  879. LoadString(g_hInstance, iResource, pszStr, nLength);
  880. return pszStr;
  881. }
  882. DWORD
  883. WIN_MSG(
  884. void
  885. )
  886. /*++
  887. WIN_MSG
  888. Desc: Shows up the message for the last Windows error
  889. --*/
  890. {
  891. LPVOID lpMsgBuf = NULL;
  892. DWORD returnVal;
  893. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
  894. | FORMAT_MESSAGE_IGNORE_INSERTS,
  895. NULL,
  896. returnVal = GetLastError(),
  897. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  898. (PTSTR) &lpMsgBuf,
  899. 0,
  900. NULL);
  901. //
  902. // Prefix :-(
  903. //
  904. if (lpMsgBuf) {
  905. MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK | MB_ICONINFORMATION);
  906. }
  907. if (lpMsgBuf) {
  908. LocalFree(lpMsgBuf);
  909. lpMsgBuf = NULL;
  910. }
  911. return returnVal;
  912. }
  913. INT
  914. Atoi(
  915. IN PCTSTR pszStr,
  916. OUT BOOL* pbValid
  917. )
  918. /*++
  919. Atoi
  920. Desc: Converts a string into a integer.
  921. Params:
  922. IN PCTSTR pszStr: The string to convert into a integer
  923. OUT BOOL* pbValid: Will be FALSE, if the string was not an integer e.g. "foo",
  924. otherwise this will be TRUE
  925. Return:
  926. The integer representation of the string
  927. --*/
  928. {
  929. BOOL bNegative = FALSE;
  930. INT result = 0, iIndex = 0;
  931. if (pszStr == NULL) {
  932. if (pbValid) {
  933. *pbValid = FALSE;
  934. }
  935. return 0;
  936. }
  937. if (pbValid) {
  938. *pbValid = TRUE;
  939. }
  940. while (isspace(*pszStr)) {
  941. pszStr++;
  942. }
  943. if (*pszStr == TEXT('-')) {
  944. bNegative = TRUE;
  945. ++pszStr;
  946. }
  947. while (isspace(*pszStr)) {
  948. pszStr++;
  949. }
  950. while (*pszStr) {
  951. if (*pszStr >= TEXT('0') && *pszStr <= TEXT('9')) {
  952. result = 10 * result + (*pszStr) - TEXT('0');
  953. } else {
  954. if (pbValid) {
  955. *pbValid = FALSE;
  956. }
  957. return 0;
  958. }
  959. ++pszStr;
  960. }
  961. if (bNegative) {
  962. return 0 - result;
  963. }
  964. return result;
  965. }
  966. BOOL
  967. NotCompletePath(
  968. IN PCTSTR pszFileName
  969. )
  970. /*++
  971. NotCompletePath
  972. Desc: Checks if we have the complete path or just the file name
  973. Params:
  974. IN PCTSTR pszFileName: The file-name to check
  975. Return:
  976. TRUE: pszFileName is not a complete path
  977. FALSE: Otherwise
  978. --*/
  979. {
  980. if (!pszFileName) {
  981. assert(FALSE);
  982. return TRUE;
  983. }
  984. if (lstrlen(pszFileName) < 3) {
  985. assert(FALSE);
  986. return FALSE;
  987. }
  988. if ((isalpha(pszFileName[0]) && pszFileName[1] == TEXT(':'))
  989. || (pszFileName[0] == TEXT('\\') && pszFileName[1] == TEXT('\\'))) {
  990. return FALSE;
  991. } else {
  992. return TRUE;
  993. }
  994. }
  995. void
  996. TreeDeleteAll(
  997. IN HWND hwndTree
  998. )
  999. /*++
  1000. TreeDeleteAll
  1001. Desc: Deletes all the items from this tree
  1002. Params:
  1003. IN HWND hwndTree: The handle to the tree view
  1004. Return:
  1005. void
  1006. --*/
  1007. {
  1008. SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0);
  1009. if (hwndTree == g_hwndEntryTree) {
  1010. g_bDeletingEntryTree = TRUE;
  1011. }
  1012. TreeView_DeleteAllItems(hwndTree);
  1013. if (hwndTree == g_hwndEntryTree) {
  1014. g_bDeletingEntryTree = FALSE;
  1015. }
  1016. SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0);
  1017. SendMessage(hwndTree, WM_NCPAINT, 1, 0);
  1018. }
  1019. BOOL
  1020. FormatDate(
  1021. IN PSYSTEMTIME pSysTime,
  1022. OUT PTSTR pszDate,
  1023. IN UINT cchDate
  1024. )
  1025. /*++
  1026. FormatDate
  1027. Desc: Formats a PSYSTEMTIME to a format that can be displayed.
  1028. The format is the form of day , month date, year, hr:minutes:seconds AM/PM
  1029. Params:
  1030. IN PSYSTEMTIME pSysTime: The time that we want to format
  1031. OUT PTSTR pszDate: The buffer that will hold the formatted string
  1032. IN UINT cchDate: The size of the buffer in characters
  1033. --*/
  1034. {
  1035. TCHAR szDay[128], szMonth[128];
  1036. if (pSysTime == NULL || pszDate == NULL) {
  1037. assert(FALSE);
  1038. return FALSE;
  1039. }
  1040. *szDay = *pszDate = *szMonth = 0;
  1041. GetString(IDS_DAYS + pSysTime->wDayOfWeek, szDay, ARRAYSIZE(szDay));
  1042. GetString(IDS_MONTHS + pSysTime->wMonth - 1, szMonth, ARRAYSIZE(szMonth));
  1043. StringCchPrintf(pszDate,
  1044. cchDate - 3,
  1045. TEXT("%s, %s %02d, %02d, %02d:%02d:%02d "),
  1046. szDay,
  1047. szMonth,
  1048. pSysTime->wDay,
  1049. pSysTime->wYear,
  1050. (pSysTime->wHour % 12) == 0 ? 12 : pSysTime->wHour % 12,
  1051. pSysTime->wMinute, pSysTime->wSecond);
  1052. if (pSysTime->wHour >= 12) {
  1053. StringCchCat(pszDate, cchDate, GetString(IDS_PM));
  1054. } else {
  1055. StringCchCat(pszDate, cchDate, GetString(IDS_AM));
  1056. }
  1057. return TRUE;
  1058. }
  1059. BOOL
  1060. GetFileContents(
  1061. IN PCTSTR pszFileName,
  1062. OUT PWSTR* ppwszFileContents
  1063. )
  1064. /*++
  1065. GetFileContents
  1066. Desc: Given a file name, this function gets the contents in a unicode buffer.
  1067. Params:
  1068. IN PCTSTR pszFileName: The name of the file
  1069. OUT PWSTR* ppwszFileContents: The buffer in which to store the contents
  1070. Return:
  1071. TRUE: If we successfully copied the concents into a unicode buffer.
  1072. FALSE: Otherwise.
  1073. --*/
  1074. {
  1075. BOOL bIsSuccess = FALSE;
  1076. LPSTR pszFileContents = NULL;
  1077. LPWSTR pwszFileContents = NULL;
  1078. if (ppwszFileContents) {
  1079. *ppwszFileContents = 0;
  1080. }
  1081. HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
  1082. if (hFile == INVALID_HANDLE_VALUE) {
  1083. return FALSE;
  1084. }
  1085. DWORD cSize = 0;
  1086. // The file is small so we don't care about the high-order
  1087. // word of the file.
  1088. if ((cSize = GetFileSize(hFile, NULL)) == -1) {
  1089. Dbg(dlError, "[GetFileContents] Opening file %S failed: %d",
  1090. pszFileName, GetLastError());
  1091. goto EXIT;
  1092. } else {
  1093. DWORD cNumberOfBytesRead = 0;
  1094. pszFileContents = new CHAR [cSize];
  1095. if (pszFileContents) {
  1096. if (ReadFile(hFile, pszFileContents, cSize, &cNumberOfBytesRead, NULL)) {
  1097. // Convert to unicode.
  1098. DWORD cSizeUnicode = MultiByteToWideChar(CP_ACP,
  1099. 0,
  1100. pszFileContents,
  1101. cNumberOfBytesRead,
  1102. 0,
  1103. 0);
  1104. pwszFileContents = new WCHAR [cSizeUnicode + 1];
  1105. if (pwszFileContents) {
  1106. MultiByteToWideChar(CP_ACP,
  1107. 0,
  1108. pszFileContents,
  1109. cNumberOfBytesRead,
  1110. pwszFileContents,
  1111. cSizeUnicode);
  1112. pwszFileContents[cSizeUnicode] = L'\0';
  1113. if (ppwszFileContents) {
  1114. *ppwszFileContents = pwszFileContents;
  1115. } else {
  1116. Dbg(dlError, "[GetFileContents] ppwszFileContents is NULL");
  1117. }
  1118. bIsSuccess = TRUE;
  1119. } else {
  1120. Dbg(dlError, "[GetFileContents] Error allocating memory");
  1121. goto EXIT;
  1122. }
  1123. } else {
  1124. Dbg(dlError, "[GetFileContents] Error reading the file contents: %d",
  1125. GetLastError());
  1126. goto EXIT;
  1127. }
  1128. } else {
  1129. Dbg(dlError, "[GetFileContents] Error allocating memory");
  1130. goto EXIT;
  1131. }
  1132. }
  1133. EXIT:
  1134. CloseHandle(hFile);
  1135. if (pszFileContents) {
  1136. delete [] pszFileContents;
  1137. }
  1138. if (bIsSuccess == FALSE) {
  1139. if (pwszFileContents) {
  1140. delete[] pwszFileContents;
  1141. pwszFileContents = NULL;
  1142. if (ppwszFileContents) {
  1143. *ppwszFileContents = NULL;
  1144. }
  1145. }
  1146. }
  1147. return bIsSuccess;
  1148. }
  1149. void
  1150. TrimLeadingSpaces(
  1151. IN OUT LPCWSTR& pwsz
  1152. )
  1153. /*++
  1154. TrimLeadingSpaces
  1155. Desc: Removes the leading tabs and spaces
  1156. Params:
  1157. IN OUT LPCWSTR& pwsz: The string to be trimmed
  1158. Return:
  1159. void
  1160. --*/
  1161. {
  1162. if (pwsz) {
  1163. pwsz += wcsspn(pwsz, L" \t");
  1164. }
  1165. }
  1166. void
  1167. TrimTrailingSpaces(
  1168. IN OUT LPWSTR pwsz
  1169. )
  1170. /*++
  1171. TrimTrailingSpaces
  1172. Desc: Removes the trailing tabs and spaces
  1173. Params:
  1174. IN OUT LPWSTR pwsz: The string to be trimmed
  1175. Return:
  1176. void
  1177. --*/
  1178. {
  1179. if (pwsz) {
  1180. DWORD cLen = wcslen(pwsz);
  1181. LPWSTR pwszEnd = pwsz + cLen - 1;
  1182. while (pwszEnd >= pwsz && (*pwszEnd == L' ' || *pwszEnd == L'\t')) {
  1183. --pwszEnd;
  1184. }
  1185. *(++pwszEnd) = L'\0';
  1186. }
  1187. }
  1188. LPWSTR
  1189. GetNextLine(
  1190. IN LPWSTR pwszBuffer
  1191. )
  1192. /*++
  1193. GetNextLine
  1194. Desc: Given a buffer, get the contents up until "\r\n" or EOF.
  1195. Usage is the same as strtok.
  1196. Params:
  1197. IN LPWSTR pwszBuffer: Buffer to get next line from.
  1198. Return: Pointer to beginning of next line.
  1199. --*/
  1200. {
  1201. static LPWSTR pwsz;
  1202. LPWSTR pwszNextLineStart;
  1203. if (pwszBuffer) {
  1204. pwsz = pwszNextLineStart = pwszBuffer;
  1205. }
  1206. while (TRUE) {
  1207. // If we are at the end of the line, go to the next line
  1208. // if there's any.
  1209. if (*pwsz == L'\r') {
  1210. pwsz = pwsz + 2;
  1211. continue;
  1212. }
  1213. if (*pwsz == L'\0') {
  1214. return NULL;
  1215. }
  1216. pwszNextLineStart = pwsz;
  1217. while (*pwsz != L'\r' && *pwsz != L'\0') {
  1218. ++pwsz;
  1219. }
  1220. if (*pwsz) {
  1221. // Set the end of the line.
  1222. *pwsz = L'\0';
  1223. pwsz = pwsz + 2;
  1224. }
  1225. return pwszNextLineStart;
  1226. }
  1227. return NULL;
  1228. }
  1229. LPWSTR GetNextToken(
  1230. IN OUT LPWSTR pwsz
  1231. )
  1232. /*++
  1233. Desc: Parse the commandline argument for the LUA shims using ' ' as the delimiter.
  1234. If a token has spaces, use double quotes around it. Use this function the
  1235. same way you use strtok except you don't have to specify the delimiter.
  1236. Params:
  1237. IN OUT LPWSTR pwsz: The string to parse.
  1238. Return Value: Pointer to the next token.
  1239. --*/
  1240. {
  1241. static LPWSTR pwszToken;
  1242. static LPWSTR pwszEndOfLastToken;
  1243. if (!pwsz) {
  1244. pwsz = pwszEndOfLastToken;
  1245. }
  1246. // Skip the white space.
  1247. while (*pwsz && *pwsz == ' ') {
  1248. ++pwsz;
  1249. }
  1250. pwszToken = pwsz;
  1251. BOOL fInsideQuotes = 0;
  1252. while (*pwsz) {
  1253. switch(*pwsz) {
  1254. case L'"':
  1255. fInsideQuotes ^= 1;
  1256. if (fInsideQuotes) {
  1257. ++pwszToken;
  1258. }
  1259. case L' ':
  1260. if (!fInsideQuotes) {
  1261. goto EXIT;
  1262. }
  1263. default:
  1264. ++pwsz;
  1265. }
  1266. }
  1267. EXIT:
  1268. if (*pwsz) {
  1269. *pwsz = L'\0';
  1270. pwszEndOfLastToken = ++pwsz;
  1271. } else {
  1272. pwszEndOfLastToken = pwsz;
  1273. }
  1274. return pwszToken;
  1275. }
  1276. int CALLBACK
  1277. CompareItemsEx(
  1278. IN LPARAM lParam1,
  1279. IN LPARAM lParam2,
  1280. IN LPARAM lParam
  1281. )
  1282. /*++
  1283. CompareItemsEx
  1284. Desc: Used to sort items in the list view for a column
  1285. Params:
  1286. IN LPARAM lParam1: Index of the first item
  1287. IN LPARAM lParam2: Index of the second item
  1288. IN LPARAM lParam: ListView_SortItemEx's lParamSort parameter. This is a COLSORT*
  1289. Return: Return a negative value if the first item should precede the second,
  1290. a positive value if the first item should follow the second,
  1291. or zero if the two items are equivalent.
  1292. Notes: Our comparison is case-INsensitive. The COLSORT contains the handle of the list view,
  1293. the index of the column for which we are doing the sort and a bit array. The
  1294. bit arrays helps us to determine, which columns are sorted in which way and this
  1295. function should reverse the sort order. Initially we will assume that the cols
  1296. are sorted in ascending order (they might not be actually) and when we click on the
  1297. column for the first time, we will sort them in descending order.
  1298. --*/
  1299. {
  1300. COLSORT* pColSort = (COLSORT*)lParam;
  1301. TCHAR szBufferOne[512], szBufferTwo[512];
  1302. LVITEM lvi;
  1303. HWND hwndList = pColSort->hwndList;
  1304. CSTRING strExeNameOne, strExeNameTwo;
  1305. INT nVal = 0;
  1306. *szBufferOne = *szBufferTwo = 0;
  1307. ZeroMemory(&lvi, sizeof(lvi));
  1308. lvi.mask = LVIF_TEXT;
  1309. lvi.iItem = (INT)lParam1;
  1310. lvi.iSubItem = pColSort->iCol;
  1311. lvi.pszText = szBufferOne;
  1312. lvi.cchTextMax = ARRAYSIZE(szBufferOne);
  1313. if (!ListView_GetItem(hwndList, &lvi)) {
  1314. assert(FALSE);
  1315. return -1;
  1316. }
  1317. lvi.mask = LVIF_TEXT;
  1318. lvi.iItem = (INT)lParam2;
  1319. lvi.iSubItem = pColSort->iCol;
  1320. lvi.pszText = szBufferTwo;
  1321. lvi.cchTextMax = ARRAYSIZE(szBufferTwo);
  1322. if (!ListView_GetItem(hwndList, &lvi)) {
  1323. assert(FALSE);
  1324. return -1;
  1325. }
  1326. nVal = lstrcmpi(szBufferOne, szBufferTwo);
  1327. if (nVal == 0) {
  1328. return 0;
  1329. }
  1330. if ((pColSort->lSortColMask & (1L << pColSort->iCol)) == 0) {
  1331. // This is in ascending order right now, sort in descending order
  1332. nVal == -1 ? nVal = 1 : nVal = -1;
  1333. }
  1334. return nVal;
  1335. }
  1336. BOOL
  1337. SaveListViewToFile(
  1338. IN HWND hwndList,
  1339. IN INT iCols,
  1340. IN PCTSTR pszFile,
  1341. IN PCTSTR pszHeader
  1342. )
  1343. /*++
  1344. SaveListViewToFile
  1345. Desc: Saves the contents of list view to a file in tab separated form. Also prints the header
  1346. before writing the contents
  1347. Params:
  1348. IN HWND hwndList: The handle to the list view
  1349. IN INT iCols: Number of columns in the list view
  1350. IN PCTSTR pszFile: The path of the file in which we want to save the contents
  1351. IN PTSTR pszHeader: Any header to be written in the file before writing the contents
  1352. Return:
  1353. TRUE: Success
  1354. FALSE: Error
  1355. --*/
  1356. {
  1357. FILE* fp = _tfopen(pszFile, TEXT("w"));
  1358. TCHAR szBuffer[256];
  1359. LVCOLUMN lvCol;
  1360. LVITEM lvi;
  1361. *szBuffer = 0;
  1362. if (fp == NULL) {
  1363. return FALSE;
  1364. }
  1365. if (pszHeader) {
  1366. fwprintf(fp, TEXT("%s\n\r"), pszHeader);
  1367. }
  1368. //
  1369. // Print the column names first
  1370. //
  1371. ZeroMemory(&lvCol, sizeof(lvCol));
  1372. lvCol.mask = LVCF_TEXT;
  1373. lvCol.pszText = szBuffer;
  1374. lvCol.cchTextMax = sizeof(szBuffer)/sizeof(szBuffer[0]);
  1375. for (INT iIndex = 0; iIndex < iCols; ++iIndex) {
  1376. *szBuffer = 0;
  1377. ListView_GetColumn(hwndList, iIndex, &lvCol);
  1378. fwprintf(fp, TEXT("%s\t"), lvCol.pszText);
  1379. }
  1380. fwprintf(fp, TEXT("\n\n"));
  1381. INT iRowCount = ListView_GetItemCount(hwndList);
  1382. ZeroMemory(&lvi, sizeof(lvi));
  1383. for (INT iRowIndex = 0; iRowIndex < iRowCount; ++ iRowIndex) {
  1384. for (INT iColIndex = 0; iColIndex < iCols; ++iColIndex) {
  1385. *szBuffer = 0;
  1386. lvi.mask = LVIF_TEXT;
  1387. lvi.pszText = szBuffer;
  1388. lvi.cchTextMax = sizeof(szBuffer)/sizeof(szBuffer[0]);
  1389. lvi.iItem = iRowIndex;
  1390. lvi.iSubItem = iColIndex;
  1391. if (!ListView_GetItem(hwndList, &lvi)) {
  1392. assert(FALSE);
  1393. }
  1394. fwprintf(fp, TEXT("%s\t"), szBuffer);
  1395. }
  1396. fwprintf(fp, TEXT("\n"));
  1397. }
  1398. fclose(fp);
  1399. return TRUE;
  1400. }
  1401. BOOL
  1402. ReplaceChar(
  1403. IN OUT PTSTR pszString,
  1404. IN TCHAR chCharToFind,
  1405. IN TCHAR chReplaceBy
  1406. )
  1407. /*++
  1408. ReplaceChar
  1409. Desc: Replaces all occurences of chCharToFind in pszString by chReplaceBy
  1410. Params:
  1411. IN OUT PTSTR pszString: The string in which the replace has to be made
  1412. IN TCHAR chCharToFind: The char to look for
  1413. IN TCHAR chReplaceBy: All occurences of chCharToFind will be replaced by this
  1414. Return:
  1415. TRUE: At least one replacement was done.
  1416. FALSE: Otherwise
  1417. --*/
  1418. {
  1419. BOOL bChanged = FALSE; // Did the string change?
  1420. while (*pszString) {
  1421. if (*pszString == chCharToFind) {
  1422. *pszString = chReplaceBy;
  1423. bChanged = TRUE;
  1424. }
  1425. ++pszString;
  1426. }
  1427. return bChanged;
  1428. }
  1429. INT
  1430. Tokenize(
  1431. IN PCTSTR szString,
  1432. IN INT cchLength,
  1433. IN PCTSTR szDelims,
  1434. OUT CSTRINGLIST& strlTokens
  1435. )
  1436. /*++
  1437. Tokenize
  1438. Desc: Tokenizes the string szString based on delimiters szDelims and puts the individual
  1439. tokens in strlTokens
  1440. Params:
  1441. IN PCTSTR szString: The string to tokenize
  1442. IN INT cchLength: The length of szString. Note that this is the length obtained using lstrlen
  1443. IN PCTSTR szDelims: The delimiter string
  1444. OUT CSTRINGLIST& strlTokens: Will contain the tokens
  1445. Return:
  1446. The count of tokens produced
  1447. Note: Please note that the tokens are always trimmed, so we cannot have a token that begins
  1448. or ends with a tab or a space
  1449. --*/
  1450. {
  1451. TCHAR* pszCopyBeg = NULL; // Pointer to the pszCopy so that we can free it
  1452. TCHAR* pszCopy = NULL; // Will contain the copy of szString
  1453. TCHAR* pszEnd = NULL; // Pointer to end of token
  1454. INT iCount = 0; // Total tokens found
  1455. BOOL bNullFound = FALSE;
  1456. CSTRING strTemp;
  1457. K_SIZE k_pszCopy = (cchLength + 1);
  1458. strlTokens.DeleteAll();
  1459. pszCopy = new TCHAR[k_pszCopy];
  1460. if (pszCopy == NULL) {
  1461. MEM_ERR;
  1462. goto End;
  1463. }
  1464. SafeCpyN(pszCopy, szString, k_pszCopy);
  1465. pszCopyBeg = pszCopy;
  1466. //
  1467. // Search for tokens
  1468. //
  1469. while (TRUE) {
  1470. //
  1471. // Ignore leading delimiters
  1472. //
  1473. while (*pszCopy && _tcschr(szDelims, *pszCopy)) {
  1474. pszCopy++;
  1475. }
  1476. if (*pszCopy == NULL) {
  1477. break;
  1478. }
  1479. //
  1480. // Find the end of the token
  1481. //
  1482. pszEnd = pszCopy + _tcscspn(pszCopy, szDelims);
  1483. if (*pszEnd == 0) {
  1484. //
  1485. // No more tokens will be found, we have found the last token
  1486. //
  1487. bNullFound = TRUE;
  1488. }
  1489. *pszEnd = 0;
  1490. ++iCount;
  1491. strTemp = pszCopy;
  1492. strTemp.Trim();
  1493. strlTokens.AddString(strTemp);
  1494. if (bNullFound == FALSE) {
  1495. //
  1496. // There might be still some tokens that we will get
  1497. //
  1498. pszCopy = pszEnd + 1;
  1499. } else {
  1500. break;
  1501. }
  1502. }
  1503. End:
  1504. if (pszCopyBeg) {
  1505. delete[] pszCopyBeg;
  1506. pszCopyBeg = NULL;
  1507. }
  1508. return iCount;
  1509. }
  1510. void
  1511. ShowInlineHelp(
  1512. IN LPCTSTR pszInlineHelpHtmlFile
  1513. )
  1514. /*++
  1515. ShowInlineHelp
  1516. Desc:
  1517. Shows in line help by loading the specified html file
  1518. Params:
  1519. IN LPCTSTR pszInlineHelpHtmlFile: The html file that contained the
  1520. help
  1521. Return:
  1522. void
  1523. --*/
  1524. {
  1525. TCHAR szPath[MAX_PATH * 2], szDir[MAX_PATH], szDrive[MAX_PATH * 2];
  1526. INT iType = 0;
  1527. if (pszInlineHelpHtmlFile == NULL) {
  1528. return;
  1529. }
  1530. *szDir = *szDrive = 0;
  1531. _tsplitpath(g_szAppPath, szDrive, szDir, NULL, NULL);
  1532. StringCchPrintf(szPath,
  1533. ARRAYSIZE(szPath),
  1534. TEXT("%s%sCompatAdmin.chm::/%s"),
  1535. szDrive,
  1536. szDir,
  1537. pszInlineHelpHtmlFile);
  1538. HtmlHelp(GetDesktopWindow(), szPath, HH_DISPLAY_TOPIC, 0);
  1539. }
  1540. PTSTR
  1541. GetSpace(
  1542. IN OUT PTSTR pszSpace,
  1543. IN INT iSpaces,
  1544. IN INT iBuffSize
  1545. )
  1546. /*++
  1547. GetSpace
  1548. Desc:
  1549. Fills in pszSpace with iSpaces number of spaces
  1550. Params:
  1551. IN OUT PTSTR pszSpace: The buffer that will be filled with spaces
  1552. IN INT iSpaces: The number of spaces to fill
  1553. IN INT iBuffSize: Size of buffer in TCHARS
  1554. Return:
  1555. The modified buffer
  1556. --*/
  1557. {
  1558. if (pszSpace == NULL) {
  1559. //
  1560. // Error..
  1561. //
  1562. goto End;
  1563. }
  1564. //
  1565. // Fill the buffer with spaces
  1566. //
  1567. for (INT iLoop = 0; iLoop < min(iSpaces, iBuffSize - 1); ++iLoop) {
  1568. *(pszSpace + iLoop) = TEXT(' ');
  1569. }
  1570. //
  1571. // Put the terminating NULL
  1572. //
  1573. *(pszSpace + min(iSpaces, iBuffSize - 1)) = 0;
  1574. End:
  1575. return pszSpace;
  1576. }
  1577. BOOL
  1578. ValidInput(
  1579. IN PCTSTR pszStr
  1580. )
  1581. /*++
  1582. ValidInput
  1583. Desc: Checks if the input contains chars other than space, tab, new line and carriage return
  1584. Params:
  1585. IN PCTSTR pszStr: The input that we want to check for validity
  1586. Return:
  1587. TRUE: Valid input
  1588. FALSE: Otherwise
  1589. --*/
  1590. {
  1591. BOOL bOk = FALSE;
  1592. if (pszStr == NULL) {
  1593. bOk = FALSE;
  1594. goto End;
  1595. }
  1596. while (*pszStr) {
  1597. if (*pszStr != TEXT(' ') && *pszStr != TEXT('\t') && *pszStr != TEXT('\n') && *pszStr != TEXT('\r')) {
  1598. bOk = TRUE;
  1599. goto End;
  1600. }
  1601. ++pszStr;
  1602. }
  1603. End:
  1604. return bOk;
  1605. }