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.

721 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. runcompat.cpp
  5. Abstract:
  6. This app sets an environment variable that tells the compat system in Whistler to run
  7. an app using a predefined set of fixes, called a "layer."
  8. Usage is:
  9. runcompat name_of_layer command_line
  10. Example:
  11. runcompat win95 notepad foo.txt
  12. Created:
  13. 06/06/2000 dmunsil
  14. Modified:
  15. --*/
  16. #define UNICODE
  17. #define _UNICODE
  18. #include <windows.h>
  19. #include <shellapi.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include "commdlg.h"
  23. #include "resource.h"
  24. extern "C" {
  25. #include "shimdb.h"
  26. }
  27. // defines
  28. const int MAX_COMMAND_LINE = 1000;
  29. const int MAX_LAYER = 100;
  30. // globals
  31. WCHAR gszCommandLine[MAX_COMMAND_LINE] = L"";
  32. WCHAR gszLayerName[MAX_LAYER] = L"";
  33. BOOL gbEnableLog = FALSE;
  34. BOOL gbDisableExisting = FALSE;
  35. BOOL gbCreateRegistryStub = FALSE;
  36. HINSTANCE ghInstance = NULL;
  37. // forward function declarations
  38. LRESULT CALLBACK DlgMain(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
  39. void ViewFileLog(void);
  40. void ClearFileLog(void);
  41. void RunThatApp(void);
  42. void GetNewCommandLine(HWND hDlg);
  43. void EnumerateLayers(HWND hDlg);
  44. void ParseFileLog(HWND hDlg);
  45. LRESULT CALLBACK DlgParseLog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
  46. void ShowHelp(void);
  47. extern "C" int APIENTRY wWinMain(HINSTANCE hInstance,
  48. HINSTANCE hPrevInstance,
  49. LPWSTR lpCmdLine,
  50. int nCmdShow)
  51. {
  52. LPWSTR *pszCommandItems = NULL;
  53. int nArgs = 0;
  54. int i;
  55. WCHAR *szExt;
  56. DWORD dwLen = 0;
  57. BOOL bShowUI = FALSE;
  58. OSVERSIONINFO osvi;
  59. ghInstance = hInstance;
  60. osvi.dwOSVersionInfoSize = sizeof(osvi);
  61. GetVersionEx(&osvi);
  62. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
  63. //
  64. // It runs on Win2k. Make sure to create the stub registry entry for
  65. // the first EXE
  66. //
  67. gbCreateRegistryStub = TRUE;
  68. }
  69. if (!lpCmdLine || !lpCmdLine[0]) {
  70. bShowUI = TRUE;
  71. }
  72. pszCommandItems = CommandLineToArgvW(lpCmdLine, &nArgs);
  73. if (!pszCommandItems) {
  74. bShowUI = TRUE;
  75. } else {
  76. if (pszCommandItems[0][0] == '-' || pszCommandItems[0][0] == '/') {
  77. if (pszCommandItems[0][1] == '?' || pszCommandItems[0][1] == 'h' || pszCommandItems[0][1] == 'H') {
  78. ShowHelp();
  79. goto out;
  80. }
  81. }
  82. if (nArgs <= 1) {
  83. bShowUI = TRUE;
  84. }
  85. if (nArgs >= 1) {
  86. wcscpy(gszLayerName, pszCommandItems[0]);
  87. }
  88. }
  89. if (bShowUI) {
  90. HWND hMainDlg = CreateDialog(hInstance, (LPCTSTR)IDD_MAIN, NULL, (DLGPROC)DlgMain);
  91. MSG msg;
  92. // Main message loop:
  93. while (GetMessage(&msg, NULL, 0, 0))
  94. {
  95. if (!IsDialogMessage(hMainDlg, &msg)) {
  96. TranslateMessage(&msg);
  97. DispatchMessage(&msg);
  98. }
  99. }
  100. } else {
  101. if (gszLayerName[0] == '!') {
  102. // strip the bang, even though we'll put it back later,
  103. // just so we disconnect the input syntax from the output syntax. That way we
  104. // can change one or the other separately.
  105. memmove(gszLayerName, gszLayerName + 1, wcslen(gszLayerName) * sizeof(WCHAR));
  106. gbDisableExisting = TRUE;
  107. } else {
  108. gbDisableExisting = FALSE;
  109. }
  110. wcscpy(gszCommandLine, pszCommandItems[1]);
  111. for (i = 2; i < nArgs; ++i) {
  112. wcscat(gszCommandLine, L" ");
  113. wcscat(gszCommandLine, pszCommandItems[i]);
  114. }
  115. RunThatApp();
  116. }
  117. out:
  118. if (pszCommandItems) {
  119. GlobalFree((HGLOBAL)pszCommandItems);
  120. }
  121. return 0;
  122. }
  123. // Message handler for main dialog.
  124. LRESULT CALLBACK DlgMain(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  125. {
  126. HDC hDC;
  127. switch (message)
  128. {
  129. case WM_INITDIALOG:
  130. {
  131. CheckDlgButton(hDlg, IDC_CHECK_LOG, BST_CHECKED);
  132. EnumerateLayers(hDlg);
  133. HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_COMMAND_LINE);
  134. if (hEdit) {
  135. SetFocus(hEdit);
  136. }
  137. // we set the focus manually, so we can return FALSE here.
  138. return FALSE;
  139. }
  140. break;
  141. case WM_COMMAND:
  142. switch LOWORD(wParam) {
  143. case IDC_BUTTON_VIEW_LOG:
  144. ViewFileLog();
  145. break;
  146. case IDC_BUTTON_CLEAR_LOG:
  147. ClearFileLog();
  148. break;
  149. case IDC_BUTTON_PARSE_LOG:
  150. DialogBox(ghInstance, MAKEINTRESOURCE(IDD_LOG_INFO), hDlg, (DLGPROC)DlgParseLog);
  151. break;
  152. case IDC_BUTTON_BROWSE:
  153. GetNewCommandLine(hDlg);
  154. break;
  155. case IDC_BUTTON_HELP:
  156. ShowHelp();
  157. break;
  158. case IDOK:
  159. {
  160. int nSel;
  161. GetDlgItemText(hDlg, IDC_EDIT_COMMAND_LINE, gszCommandLine, MAX_COMMAND_LINE);
  162. gbEnableLog = (IsDlgButtonChecked(hDlg, IDC_CHECK_LOG) == BST_CHECKED);
  163. gbDisableExisting = (IsDlgButtonChecked(hDlg, IDC_CHECK_DISABLE_EXISTING) == BST_CHECKED);
  164. nSel = (int)SendDlgItemMessage(hDlg, IDC_LIST_LAYER, LB_GETCURSEL, 0, 0);
  165. if (nSel != LB_ERR) {
  166. if (LB_ERR != SendDlgItemMessage(hDlg, IDC_LIST_LAYER, LB_GETTEXT, nSel, (LPARAM)gszLayerName)) {
  167. RunThatApp();
  168. }
  169. }
  170. }
  171. break;
  172. case IDCANCEL:
  173. EndDialog(hDlg, LOWORD(wParam));
  174. PostQuitMessage(0);
  175. return TRUE;
  176. break;
  177. }
  178. }
  179. return FALSE;
  180. }
  181. #define APPCOMPAT_KEY L"System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
  182. void
  183. AddRegistryStubForExe(
  184. void)
  185. {
  186. BOOL bBraket = FALSE;
  187. WCHAR* pwsz = gszCommandLine;
  188. WCHAR wszExeName[128];
  189. while (*pwsz == L' ' || *pwsz == L'\t') {
  190. pwsz++;
  191. }
  192. while (*pwsz != 0) {
  193. if (*pwsz == L'\"') {
  194. bBraket = !bBraket;
  195. } else if (*pwsz == L' ' && !bBraket) {
  196. break;
  197. }
  198. pwsz++;
  199. }
  200. //
  201. // Now walk back to get the caracters
  202. //
  203. pwsz--;
  204. if (*pwsz == L'\"') {
  205. pwsz--;
  206. }
  207. WCHAR* pwszEnd;
  208. WCHAR* pwszStart = gszCommandLine;
  209. pwszEnd = pwsz + 1;
  210. while (pwsz >= gszCommandLine) {
  211. if (*pwsz == L'\\') {
  212. pwszStart = pwsz + 1;
  213. break;
  214. }
  215. pwsz--;
  216. }
  217. memcpy(wszExeName, pwszStart, (pwszEnd - pwszStart) * sizeof(WCHAR));
  218. wszExeName[pwszEnd - pwszStart] = 0;
  219. WCHAR wszKey[256];
  220. HKEY hkey;
  221. DWORD type;
  222. DWORD cbData = 0;
  223. swprintf(wszKey, L"%s\\%s", APPCOMPAT_KEY, wszExeName);
  224. if (RegCreateKeyW(HKEY_LOCAL_MACHINE, wszKey, &hkey) != ERROR_SUCCESS) {
  225. // DPF_Log((eDbgLevelError, "Failed to open/create the appcompat key \"%s\"", szKey));
  226. } else {
  227. if (RegQueryValueExW(hkey, L"DllPatch-x", NULL, &type, NULL, &cbData) != ERROR_SUCCESS) {
  228. BYTE data[16] = {0x0c, 0, 0, 0, 0, 0, 0, 0,
  229. 0x06, 0, 0, 0, 0, 0, 0, 0};
  230. //
  231. // The value doesn't exist. Create it.
  232. //
  233. RegSetValueExW(hkey,
  234. L"y",
  235. NULL,
  236. REG_BINARY,
  237. data,
  238. sizeof(data));
  239. data[0] = 0;
  240. RegSetValueExW(hkey,
  241. L"DllPatch-y",
  242. NULL,
  243. REG_SZ,
  244. data,
  245. 2);
  246. }
  247. }
  248. RegCloseKey(hkey);
  249. }
  250. void RunThatApp(void)
  251. {
  252. STARTUPINFO StartupInfo;
  253. PROCESS_INFORMATION ProcessInfo;
  254. SHELLEXECUTEINFO ShellExecuteInfo;
  255. WCHAR szLayer[MAX_LAYER];
  256. BOOL bSuccess = TRUE;
  257. int nExt = 0;
  258. WCHAR *szExt = NULL;
  259. // put a bang on the front if we're supposed to disable the existing
  260. // shims in the database
  261. if (gbDisableExisting) {
  262. wcscpy(szLayer, L"!");
  263. } else {
  264. szLayer[0] = 0;
  265. }
  266. wcscat(szLayer, gszLayerName);
  267. SetEnvironmentVariable(L"__COMPAT_LAYER", szLayer);
  268. if (gbEnableLog) {
  269. SetEnvironmentVariable(L"SHIM_FILE_LOG", L"shim.log");
  270. } else {
  271. SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
  272. }
  273. if (gbCreateRegistryStub) {
  274. AddRegistryStubForExe();
  275. }
  276. // if the whole command line is a shell link, use
  277. // ShellExecuteEx, but otherwise use CreateProcess
  278. // for its simpler command-line handling
  279. nExt = wcslen(gszCommandLine) - 4;
  280. szExt = gszCommandLine + nExt;
  281. if (nExt > 0 && _wcsicmp(szExt, L".lnk") == 0) {
  282. ZeroMemory(&ShellExecuteInfo, sizeof(ShellExecuteInfo));
  283. ShellExecuteInfo.cbSize = sizeof(ShellExecuteInfo);
  284. ShellExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI;
  285. ShellExecuteInfo.lpVerb = L"open";
  286. ShellExecuteInfo.lpFile = gszCommandLine;
  287. ShellExecuteInfo.nShow = SW_SHOW;
  288. bSuccess = ShellExecuteEx(&ShellExecuteInfo);
  289. } else {
  290. //
  291. // try to get the starting directory
  292. //
  293. LPWSTR *argv;
  294. int argc;
  295. WCHAR szWorkingBuffer[MAX_PATH];
  296. WCHAR *pszWorkingDir = NULL;
  297. //
  298. // try to get the starting directory
  299. //
  300. argv = CommandLineToArgvW(gszCommandLine, &argc);
  301. if (argv && argv[0] && argv[0][0] && argc) {
  302. //
  303. // we only set the working directory if they give us a full path.
  304. //
  305. if (argv[0][1] == L':' || argv[0][1] == L'\\') {
  306. //
  307. // get the working directory, if possible
  308. //
  309. WCHAR *szTemp = wcsrchr(argv[0], L'\\');
  310. if (szTemp) {
  311. wcsncpy(szWorkingBuffer, argv[0], szTemp - argv[0]);
  312. szWorkingBuffer[szTemp - argv[0]] = 0;
  313. pszWorkingDir = szWorkingBuffer;
  314. }
  315. }
  316. GlobalFree(argv);
  317. argv = NULL;
  318. }
  319. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  320. StartupInfo.cb = sizeof(StartupInfo);
  321. ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
  322. bSuccess = TRUE;
  323. if (!CreateProcess(NULL,
  324. gszCommandLine,
  325. NULL,
  326. NULL,
  327. FALSE,
  328. 0,
  329. NULL,
  330. pszWorkingDir,
  331. &StartupInfo,
  332. &ProcessInfo)) {
  333. bSuccess = FALSE;
  334. }
  335. }
  336. if (!bSuccess) {
  337. DWORD dwErr;
  338. WCHAR szMsg[1000];
  339. WCHAR szErr[1000];
  340. dwErr = GetLastError();
  341. if (!FormatMessage(
  342. FORMAT_MESSAGE_FROM_SYSTEM |
  343. FORMAT_MESSAGE_IGNORE_INSERTS,
  344. NULL,
  345. dwErr,
  346. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  347. szErr,
  348. 999,
  349. NULL )) {
  350. wcscpy(szErr, L"(unknown)");
  351. }
  352. wsprintf(szMsg, L"Error 0x%08X:\n\n%s\nwhile executing command line \"%s\".", dwErr, szErr, gszCommandLine);
  353. MessageBox(NULL, szMsg, L"Error", MB_OK | MB_ICONEXCLAMATION);
  354. }
  355. SetEnvironmentVariable(L"__COMPAT_LAYER", NULL);
  356. SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
  357. }
  358. void GetNewCommandLine(HWND hDlg)
  359. {
  360. OPENFILENAME ofn;
  361. WCHAR szFullPath[1000];
  362. szFullPath[0] = L'\0';
  363. ZeroMemory(&ofn, sizeof(OPENFILENAME));
  364. ofn.lStructSize = sizeof(OPENFILENAME);
  365. ofn.hwndOwner = hDlg;
  366. ofn.lpstrFile = szFullPath;
  367. ofn.nMaxFile = sizeof(szFullPath)/sizeof(szFullPath[0]);
  368. ofn.lpstrFilter = L"Exe\0*.EXE\0All\0*.*\0";
  369. ofn.nFilterIndex = 1;
  370. ofn.lpstrTitle = L"Select EXE to run";
  371. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NODEREFERENCELINKS;
  372. // get the matching file name
  373. if (GetOpenFileName(&ofn) == FALSE) {
  374. return;
  375. }
  376. if (wcschr(szFullPath, L' ')) {
  377. //
  378. // if there are spaces in the path, quote the whole thing.
  379. //
  380. WCHAR szFullPathTemp[1002];
  381. swprintf(szFullPathTemp, L"\"%s\"", szFullPath);
  382. wcscpy(szFullPath, szFullPathTemp);
  383. }
  384. SetDlgItemText(hDlg, IDC_EDIT_COMMAND_LINE, szFullPath);
  385. }
  386. void ViewFileLog(void)
  387. {
  388. STARTUPINFO StartupInfo;
  389. PROCESS_INFORMATION ProcessInfo;
  390. WCHAR szCommand[1000];
  391. // make sure we aren't shimming Notepad.
  392. SetEnvironmentVariable(L"__COMPAT_LAYER", NULL);
  393. SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
  394. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  395. StartupInfo.cb = sizeof(StartupInfo);
  396. ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
  397. ExpandEnvironmentStringsW(L"notepad %windir%\\AppPatch\\shim.log", szCommand, 1000);
  398. CreateProcess(NULL,
  399. szCommand,
  400. NULL,
  401. NULL,
  402. FALSE,
  403. 0,
  404. NULL,
  405. NULL,
  406. &StartupInfo,
  407. &ProcessInfo);
  408. }
  409. void ClearFileLog(void)
  410. {
  411. WCHAR szPath[1000];
  412. WCHAR szMsg[1000];
  413. ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\shim.log", szPath, 1000);
  414. DeleteFile(szPath);
  415. wsprintf(szMsg, L"Deleted file \"%s\".", szPath);
  416. MessageBox(NULL, szMsg, L"Cleared Log", MB_OK);
  417. }
  418. void EnumerateLayers(HWND hDlg)
  419. {
  420. PDB pdb = NULL;
  421. WCHAR wszPath[MAX_PATH];
  422. HWND hList;
  423. TAGID tiDatabase;
  424. TAGID tiLayer;
  425. hList = GetDlgItem(hDlg, IDC_LIST_LAYER);
  426. if (!hList) {
  427. goto out;
  428. }
  429. ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\sysmain.sdb", wszPath, MAX_PATH);
  430. pdb = SdbOpenDatabase(wszPath, DOS_PATH);
  431. if (!pdb) {
  432. goto out;
  433. }
  434. tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
  435. if (!tiDatabase) {
  436. goto out;
  437. }
  438. tiLayer = SdbFindFirstTag(pdb, tiDatabase, TAG_LAYER);
  439. while (tiLayer) {
  440. TAGID tiName;
  441. WCHAR wszName[MAX_PATH];
  442. wszName[0] = 0;
  443. tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);
  444. if (tiName) {
  445. SdbReadStringTag(pdb, tiName, wszName, MAX_PATH * sizeof(WCHAR));
  446. }
  447. if (wszName[0]) {
  448. SendMessageW(hList, LB_ADDSTRING, 0, (LPARAM)wszName);
  449. }
  450. tiLayer = SdbFindNextTag(pdb, tiDatabase, tiLayer);
  451. }
  452. out:
  453. if (pdb) {
  454. SdbCloseDatabase(pdb);
  455. }
  456. // select the first item that begins with "Win9" or the first one in the list, if
  457. // none match.
  458. if (LB_ERR == SendMessageW(hList, LB_SELECTSTRING, -1, (LPARAM)L"Win9")) {
  459. SendMessageW(hList, LB_SETCURSEL, 0, 0);
  460. }
  461. }
  462. void ParseFileLog(HWND hDlg)
  463. {
  464. WCHAR szPath[1000];
  465. WCHAR szLine[1000];
  466. WCHAR szDate[50];
  467. WCHAR szTime[50];
  468. WCHAR szDll[200];
  469. DWORD dwLevel = 0;
  470. int nLast = 0;
  471. static WCHAR szHeader[10000];
  472. static WCHAR szWarnings[10000];
  473. static WCHAR szErrors[10000];
  474. static WCHAR szOther[10000];
  475. FILE *file = NULL;
  476. BOOL bInHeader = FALSE;
  477. ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\shim.log", szPath, 1000);
  478. file = _wfopen(szPath, L"rt");
  479. if (!file) {
  480. goto out;
  481. }
  482. while (fgetws(szLine, 1000, file)) {
  483. if (wcsncmp(szLine, L"----", 4) == 0) {
  484. bInHeader = !bInHeader;
  485. if (bInHeader) {
  486. // we just started a new header, so clear everything
  487. szHeader[0] = 0;
  488. szErrors[0] = 0;
  489. szWarnings[0] = 0;
  490. szOther[0] = 0;
  491. }
  492. continue;
  493. }
  494. // remove the terminator(s)
  495. nLast = wcslen(szLine) - 1;
  496. while (nLast >= 0 && (szLine[nLast] == L'\n'|| szLine[nLast] == L'\r')) {
  497. szLine[nLast] = L'\0';
  498. nLast--;
  499. }
  500. if (bInHeader) {
  501. wcscat(szHeader, szLine);
  502. wcscat(szHeader, L"\r\n");
  503. continue;
  504. }
  505. dwLevel = 0;
  506. swscanf(szLine, L"%s %s %s %d", szDate, szTime, szDll, &dwLevel);
  507. _wcslwr(szDll);
  508. if (!wcsstr(szDll, L".dll") || dwLevel == 0) {
  509. WCHAR szError[1000];
  510. swprintf(szError, L"Unrecognized data in line: %s", szLine);
  511. OutputDebugStringW(szError);
  512. continue;
  513. }
  514. if (dwLevel == 1) {
  515. if (!wcsstr(szErrors, szDll)) {
  516. wcscat(szErrors, szDll);
  517. wcscat(szErrors, L"\r\n");
  518. }
  519. } else if (dwLevel == 2) {
  520. if (!wcsstr(szWarnings, szDll)) {
  521. wcscat(szWarnings, szDll);
  522. wcscat(szWarnings, L"\r\n");
  523. }
  524. } else {
  525. if (!wcsstr(szOther, szDll)) {
  526. wcscat(szOther, szDll);
  527. wcscat(szOther, L"\r\n");
  528. }
  529. }
  530. }
  531. SetDlgItemText(hDlg, IDC_EDIT_HEADER, szHeader);
  532. SetDlgItemText(hDlg, IDC_EDIT_ERRORS, szErrors);
  533. SetDlgItemText(hDlg, IDC_EDIT_WARNINGS, szWarnings);
  534. SetDlgItemText(hDlg, IDC_EDIT_OTHER, szOther);
  535. out:
  536. if (file) {
  537. fclose(file);
  538. }
  539. return;
  540. }
  541. // Message handler for parse dialog.
  542. LRESULT CALLBACK DlgParseLog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  543. {
  544. HDC hDC;
  545. switch (message)
  546. {
  547. case WM_INITDIALOG:
  548. ParseFileLog(hDlg);
  549. break;
  550. case WM_COMMAND:
  551. switch LOWORD(wParam) {
  552. case IDOK:
  553. case IDCANCEL:
  554. EndDialog(hDlg, LOWORD(wParam));
  555. return TRUE;
  556. break;
  557. }
  558. }
  559. return FALSE;
  560. }
  561. void ShowHelp(void)
  562. {
  563. MessageBox(NULL, L"Apply a compatibility layer in addition to any fixes in the database:\n\n"
  564. L" runcompat (layer name) (command line)\n\n"
  565. L"Apply a compatibility layer and disable any existing fixes:\n\n"
  566. L" runcompat !(layer name) (command line)\n\n"
  567. L"Run GUI version:\n\n"
  568. L" runcompat", L"Runcompat Command-Line Usage", MB_OK);
  569. }