722 lines
18 KiB
C++
722 lines
18 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
runcompat.cpp
|
|
|
|
Abstract:
|
|
|
|
This app sets an environment variable that tells the compat system in Whistler to run
|
|
an app using a predefined set of fixes, called a "layer."
|
|
|
|
Usage is:
|
|
|
|
runcompat name_of_layer command_line
|
|
|
|
Example:
|
|
|
|
runcompat win95 notepad foo.txt
|
|
|
|
Created:
|
|
|
|
06/06/2000 dmunsil
|
|
|
|
Modified:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "commdlg.h"
|
|
#include "resource.h"
|
|
|
|
extern "C" {
|
|
#include "shimdb.h"
|
|
}
|
|
|
|
// defines
|
|
const int MAX_COMMAND_LINE = 1000;
|
|
const int MAX_LAYER = 100;
|
|
|
|
// globals
|
|
WCHAR gszCommandLine[MAX_COMMAND_LINE] = L"";
|
|
WCHAR gszLayerName[MAX_LAYER] = L"";
|
|
BOOL gbEnableLog = FALSE;
|
|
BOOL gbDisableExisting = FALSE;
|
|
BOOL gbCreateRegistryStub = FALSE;
|
|
|
|
HINSTANCE ghInstance = NULL;
|
|
|
|
// forward function declarations
|
|
LRESULT CALLBACK DlgMain(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
|
void ViewFileLog(void);
|
|
void ClearFileLog(void);
|
|
void RunThatApp(void);
|
|
void GetNewCommandLine(HWND hDlg);
|
|
void EnumerateLayers(HWND hDlg);
|
|
void ParseFileLog(HWND hDlg);
|
|
LRESULT CALLBACK DlgParseLog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
|
void ShowHelp(void);
|
|
|
|
|
|
extern "C" int APIENTRY wWinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPWSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
LPWSTR *pszCommandItems = NULL;
|
|
int nArgs = 0;
|
|
int i;
|
|
WCHAR *szExt;
|
|
DWORD dwLen = 0;
|
|
BOOL bShowUI = FALSE;
|
|
OSVERSIONINFO osvi;
|
|
|
|
ghInstance = hInstance;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
|
|
GetVersionEx(&osvi);
|
|
|
|
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
|
|
//
|
|
// It runs on Win2k. Make sure to create the stub registry entry for
|
|
// the first EXE
|
|
//
|
|
gbCreateRegistryStub = TRUE;
|
|
}
|
|
|
|
if (!lpCmdLine || !lpCmdLine[0]) {
|
|
bShowUI = TRUE;
|
|
}
|
|
|
|
pszCommandItems = CommandLineToArgvW(lpCmdLine, &nArgs);
|
|
|
|
if (!pszCommandItems) {
|
|
bShowUI = TRUE;
|
|
} else {
|
|
if (pszCommandItems[0][0] == '-' || pszCommandItems[0][0] == '/') {
|
|
if (pszCommandItems[0][1] == '?' || pszCommandItems[0][1] == 'h' || pszCommandItems[0][1] == 'H') {
|
|
ShowHelp();
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (nArgs <= 1) {
|
|
bShowUI = TRUE;
|
|
}
|
|
if (nArgs >= 1) {
|
|
wcscpy(gszLayerName, pszCommandItems[0]);
|
|
}
|
|
}
|
|
|
|
if (bShowUI) {
|
|
HWND hMainDlg = CreateDialog(hInstance, (LPCTSTR)IDD_MAIN, NULL, (DLGPROC)DlgMain);
|
|
MSG msg;
|
|
|
|
// Main message loop:
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (!IsDialogMessage(hMainDlg, &msg)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
} else {
|
|
if (gszLayerName[0] == '!') {
|
|
// strip the bang, even though we'll put it back later,
|
|
// just so we disconnect the input syntax from the output syntax. That way we
|
|
// can change one or the other separately.
|
|
memmove(gszLayerName, gszLayerName + 1, wcslen(gszLayerName) * sizeof(WCHAR));
|
|
gbDisableExisting = TRUE;
|
|
} else {
|
|
gbDisableExisting = FALSE;
|
|
}
|
|
|
|
wcscpy(gszCommandLine, pszCommandItems[1]);
|
|
for (i = 2; i < nArgs; ++i) {
|
|
wcscat(gszCommandLine, L" ");
|
|
wcscat(gszCommandLine, pszCommandItems[i]);
|
|
}
|
|
|
|
RunThatApp();
|
|
|
|
}
|
|
|
|
out:
|
|
if (pszCommandItems) {
|
|
GlobalFree((HGLOBAL)pszCommandItems);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Message handler for main dialog.
|
|
LRESULT CALLBACK DlgMain(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hDC;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
CheckDlgButton(hDlg, IDC_CHECK_LOG, BST_CHECKED);
|
|
EnumerateLayers(hDlg);
|
|
|
|
HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_COMMAND_LINE);
|
|
if (hEdit) {
|
|
SetFocus(hEdit);
|
|
}
|
|
|
|
// we set the focus manually, so we can return FALSE here.
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch LOWORD(wParam) {
|
|
|
|
case IDC_BUTTON_VIEW_LOG:
|
|
ViewFileLog();
|
|
break;
|
|
|
|
case IDC_BUTTON_CLEAR_LOG:
|
|
ClearFileLog();
|
|
break;
|
|
|
|
case IDC_BUTTON_PARSE_LOG:
|
|
DialogBox(ghInstance, MAKEINTRESOURCE(IDD_LOG_INFO), hDlg, (DLGPROC)DlgParseLog);
|
|
break;
|
|
|
|
case IDC_BUTTON_BROWSE:
|
|
GetNewCommandLine(hDlg);
|
|
break;
|
|
|
|
case IDC_BUTTON_HELP:
|
|
ShowHelp();
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
int nSel;
|
|
|
|
GetDlgItemText(hDlg, IDC_EDIT_COMMAND_LINE, gszCommandLine, MAX_COMMAND_LINE);
|
|
gbEnableLog = (IsDlgButtonChecked(hDlg, IDC_CHECK_LOG) == BST_CHECKED);
|
|
gbDisableExisting = (IsDlgButtonChecked(hDlg, IDC_CHECK_DISABLE_EXISTING) == BST_CHECKED);
|
|
|
|
nSel = (int)SendDlgItemMessage(hDlg, IDC_LIST_LAYER, LB_GETCURSEL, 0, 0);
|
|
if (nSel != LB_ERR) {
|
|
if (LB_ERR != SendDlgItemMessage(hDlg, IDC_LIST_LAYER, LB_GETTEXT, nSel, (LPARAM)gszLayerName)) {
|
|
RunThatApp();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
PostQuitMessage(0);
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define APPCOMPAT_KEY L"System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
|
|
|
|
void
|
|
AddRegistryStubForExe(
|
|
void)
|
|
{
|
|
BOOL bBraket = FALSE;
|
|
WCHAR* pwsz = gszCommandLine;
|
|
WCHAR wszExeName[128];
|
|
|
|
while (*pwsz == L' ' || *pwsz == L'\t') {
|
|
pwsz++;
|
|
}
|
|
|
|
while (*pwsz != 0) {
|
|
|
|
if (*pwsz == L'\"') {
|
|
bBraket = !bBraket;
|
|
} else if (*pwsz == L' ' && !bBraket) {
|
|
break;
|
|
}
|
|
|
|
pwsz++;
|
|
}
|
|
//
|
|
// Now walk back to get the caracters
|
|
//
|
|
pwsz--;
|
|
|
|
if (*pwsz == L'\"') {
|
|
pwsz--;
|
|
}
|
|
|
|
WCHAR* pwszEnd;
|
|
WCHAR* pwszStart = gszCommandLine;
|
|
|
|
pwszEnd = pwsz + 1;
|
|
|
|
while (pwsz >= gszCommandLine) {
|
|
if (*pwsz == L'\\') {
|
|
pwszStart = pwsz + 1;
|
|
break;
|
|
}
|
|
pwsz--;
|
|
}
|
|
|
|
memcpy(wszExeName, pwszStart, (pwszEnd - pwszStart) * sizeof(WCHAR));
|
|
|
|
wszExeName[pwszEnd - pwszStart] = 0;
|
|
|
|
WCHAR wszKey[256];
|
|
HKEY hkey;
|
|
DWORD type;
|
|
DWORD cbData = 0;
|
|
|
|
swprintf(wszKey, L"%s\\%s", APPCOMPAT_KEY, wszExeName);
|
|
|
|
if (RegCreateKeyW(HKEY_LOCAL_MACHINE, wszKey, &hkey) != ERROR_SUCCESS) {
|
|
// DPF_Log((eDbgLevelError, "Failed to open/create the appcompat key \"%s\"", szKey));
|
|
} else {
|
|
if (RegQueryValueExW(hkey, L"DllPatch-x", NULL, &type, NULL, &cbData) != ERROR_SUCCESS) {
|
|
|
|
BYTE data[16] = {0x0c, 0, 0, 0, 0, 0, 0, 0,
|
|
0x06, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
//
|
|
// The value doesn't exist. Create it.
|
|
//
|
|
RegSetValueExW(hkey,
|
|
L"y",
|
|
NULL,
|
|
REG_BINARY,
|
|
data,
|
|
sizeof(data));
|
|
|
|
data[0] = 0;
|
|
|
|
RegSetValueExW(hkey,
|
|
L"DllPatch-y",
|
|
NULL,
|
|
REG_SZ,
|
|
data,
|
|
2);
|
|
}
|
|
}
|
|
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
void RunThatApp(void)
|
|
{
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
SHELLEXECUTEINFO ShellExecuteInfo;
|
|
WCHAR szLayer[MAX_LAYER];
|
|
BOOL bSuccess = TRUE;
|
|
int nExt = 0;
|
|
WCHAR *szExt = NULL;
|
|
|
|
// put a bang on the front if we're supposed to disable the existing
|
|
// shims in the database
|
|
if (gbDisableExisting) {
|
|
wcscpy(szLayer, L"!");
|
|
} else {
|
|
szLayer[0] = 0;
|
|
}
|
|
wcscat(szLayer, gszLayerName);
|
|
|
|
SetEnvironmentVariable(L"__COMPAT_LAYER", szLayer);
|
|
|
|
if (gbEnableLog) {
|
|
SetEnvironmentVariable(L"SHIM_FILE_LOG", L"shim.log");
|
|
} else {
|
|
SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
|
|
}
|
|
|
|
if (gbCreateRegistryStub) {
|
|
AddRegistryStubForExe();
|
|
}
|
|
|
|
// if the whole command line is a shell link, use
|
|
// ShellExecuteEx, but otherwise use CreateProcess
|
|
// for its simpler command-line handling
|
|
|
|
nExt = wcslen(gszCommandLine) - 4;
|
|
szExt = gszCommandLine + nExt;
|
|
if (nExt > 0 && _wcsicmp(szExt, L".lnk") == 0) {
|
|
|
|
ZeroMemory(&ShellExecuteInfo, sizeof(ShellExecuteInfo));
|
|
ShellExecuteInfo.cbSize = sizeof(ShellExecuteInfo);
|
|
ShellExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI;
|
|
ShellExecuteInfo.lpVerb = L"open";
|
|
ShellExecuteInfo.lpFile = gszCommandLine;
|
|
ShellExecuteInfo.nShow = SW_SHOW;
|
|
|
|
bSuccess = ShellExecuteEx(&ShellExecuteInfo);
|
|
|
|
} else {
|
|
|
|
//
|
|
// try to get the starting directory
|
|
//
|
|
LPWSTR *argv;
|
|
int argc;
|
|
WCHAR szWorkingBuffer[MAX_PATH];
|
|
WCHAR *pszWorkingDir = NULL;
|
|
|
|
//
|
|
// try to get the starting directory
|
|
//
|
|
argv = CommandLineToArgvW(gszCommandLine, &argc);
|
|
if (argv && argv[0] && argv[0][0] && argc) {
|
|
|
|
//
|
|
// we only set the working directory if they give us a full path.
|
|
//
|
|
if (argv[0][1] == L':' || argv[0][1] == L'\\') {
|
|
|
|
//
|
|
// get the working directory, if possible
|
|
//
|
|
WCHAR *szTemp = wcsrchr(argv[0], L'\\');
|
|
if (szTemp) {
|
|
wcsncpy(szWorkingBuffer, argv[0], szTemp - argv[0]);
|
|
szWorkingBuffer[szTemp - argv[0]] = 0;
|
|
pszWorkingDir = szWorkingBuffer;
|
|
}
|
|
}
|
|
|
|
GlobalFree(argv);
|
|
argv = NULL;
|
|
}
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
|
|
|
bSuccess = TRUE;
|
|
if (!CreateProcess(NULL,
|
|
gszCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
pszWorkingDir,
|
|
&StartupInfo,
|
|
&ProcessInfo)) {
|
|
|
|
bSuccess = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!bSuccess) {
|
|
DWORD dwErr;
|
|
WCHAR szMsg[1000];
|
|
WCHAR szErr[1000];
|
|
|
|
dwErr = GetLastError();
|
|
|
|
if (!FormatMessage(
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
dwErr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
szErr,
|
|
999,
|
|
NULL )) {
|
|
|
|
wcscpy(szErr, L"(unknown)");
|
|
}
|
|
|
|
wsprintf(szMsg, L"Error 0x%08X:\n\n%s\nwhile executing command line \"%s\".", dwErr, szErr, gszCommandLine);
|
|
MessageBox(NULL, szMsg, L"Error", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
|
|
SetEnvironmentVariable(L"__COMPAT_LAYER", NULL);
|
|
SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
|
|
}
|
|
|
|
void GetNewCommandLine(HWND hDlg)
|
|
{
|
|
OPENFILENAME ofn;
|
|
WCHAR szFullPath[1000];
|
|
|
|
szFullPath[0] = L'\0';
|
|
|
|
ZeroMemory(&ofn, sizeof(OPENFILENAME));
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hDlg;
|
|
ofn.lpstrFile = szFullPath;
|
|
ofn.nMaxFile = sizeof(szFullPath)/sizeof(szFullPath[0]);
|
|
ofn.lpstrFilter = L"Exe\0*.EXE\0All\0*.*\0";
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrTitle = L"Select EXE to run";
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NODEREFERENCELINKS;
|
|
|
|
// get the matching file name
|
|
if (GetOpenFileName(&ofn) == FALSE) {
|
|
return;
|
|
}
|
|
|
|
if (wcschr(szFullPath, L' ')) {
|
|
//
|
|
// if there are spaces in the path, quote the whole thing.
|
|
//
|
|
WCHAR szFullPathTemp[1002];
|
|
swprintf(szFullPathTemp, L"\"%s\"", szFullPath);
|
|
wcscpy(szFullPath, szFullPathTemp);
|
|
}
|
|
|
|
|
|
SetDlgItemText(hDlg, IDC_EDIT_COMMAND_LINE, szFullPath);
|
|
|
|
}
|
|
|
|
void ViewFileLog(void)
|
|
{
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
WCHAR szCommand[1000];
|
|
|
|
// make sure we aren't shimming Notepad.
|
|
SetEnvironmentVariable(L"__COMPAT_LAYER", NULL);
|
|
SetEnvironmentVariable(L"SHIM_FILE_LOG", NULL);
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
|
|
|
ExpandEnvironmentStringsW(L"notepad %windir%\\AppPatch\\shim.log", szCommand, 1000);
|
|
|
|
CreateProcess(NULL,
|
|
szCommand,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInfo);
|
|
}
|
|
|
|
void ClearFileLog(void)
|
|
{
|
|
WCHAR szPath[1000];
|
|
WCHAR szMsg[1000];
|
|
|
|
ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\shim.log", szPath, 1000);
|
|
|
|
DeleteFile(szPath);
|
|
|
|
wsprintf(szMsg, L"Deleted file \"%s\".", szPath);
|
|
|
|
MessageBox(NULL, szMsg, L"Cleared Log", MB_OK);
|
|
}
|
|
|
|
void EnumerateLayers(HWND hDlg)
|
|
{
|
|
PDB pdb = NULL;
|
|
WCHAR wszPath[MAX_PATH];
|
|
HWND hList;
|
|
TAGID tiDatabase;
|
|
TAGID tiLayer;
|
|
|
|
hList = GetDlgItem(hDlg, IDC_LIST_LAYER);
|
|
if (!hList) {
|
|
goto out;
|
|
}
|
|
|
|
ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\sysmain.sdb", wszPath, MAX_PATH);
|
|
|
|
pdb = SdbOpenDatabase(wszPath, DOS_PATH);
|
|
if (!pdb) {
|
|
goto out;
|
|
}
|
|
|
|
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
|
|
if (!tiDatabase) {
|
|
goto out;
|
|
}
|
|
|
|
tiLayer = SdbFindFirstTag(pdb, tiDatabase, TAG_LAYER);
|
|
while (tiLayer) {
|
|
TAGID tiName;
|
|
WCHAR wszName[MAX_PATH];
|
|
|
|
wszName[0] = 0;
|
|
tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);
|
|
if (tiName) {
|
|
|
|
SdbReadStringTag(pdb, tiName, wszName, MAX_PATH * sizeof(WCHAR));
|
|
}
|
|
if (wszName[0]) {
|
|
SendMessageW(hList, LB_ADDSTRING, 0, (LPARAM)wszName);
|
|
}
|
|
|
|
tiLayer = SdbFindNextTag(pdb, tiDatabase, tiLayer);
|
|
}
|
|
|
|
out:
|
|
if (pdb) {
|
|
SdbCloseDatabase(pdb);
|
|
}
|
|
|
|
// select the first item that begins with "Win9" or the first one in the list, if
|
|
// none match.
|
|
if (LB_ERR == SendMessageW(hList, LB_SELECTSTRING, -1, (LPARAM)L"Win9")) {
|
|
SendMessageW(hList, LB_SETCURSEL, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
void ParseFileLog(HWND hDlg)
|
|
{
|
|
WCHAR szPath[1000];
|
|
WCHAR szLine[1000];
|
|
WCHAR szDate[50];
|
|
WCHAR szTime[50];
|
|
WCHAR szDll[200];
|
|
DWORD dwLevel = 0;
|
|
int nLast = 0;
|
|
|
|
static WCHAR szHeader[10000];
|
|
static WCHAR szWarnings[10000];
|
|
static WCHAR szErrors[10000];
|
|
static WCHAR szOther[10000];
|
|
|
|
FILE *file = NULL;
|
|
BOOL bInHeader = FALSE;
|
|
|
|
ExpandEnvironmentStringsW(L"%windir%\\AppPatch\\shim.log", szPath, 1000);
|
|
|
|
file = _wfopen(szPath, L"rt");
|
|
|
|
if (!file) {
|
|
goto out;
|
|
}
|
|
|
|
while (fgetws(szLine, 1000, file)) {
|
|
if (wcsncmp(szLine, L"----", 4) == 0) {
|
|
bInHeader = !bInHeader;
|
|
if (bInHeader) {
|
|
|
|
// we just started a new header, so clear everything
|
|
szHeader[0] = 0;
|
|
szErrors[0] = 0;
|
|
szWarnings[0] = 0;
|
|
szOther[0] = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// remove the terminator(s)
|
|
nLast = wcslen(szLine) - 1;
|
|
while (nLast >= 0 && (szLine[nLast] == L'\n'|| szLine[nLast] == L'\r')) {
|
|
szLine[nLast] = L'\0';
|
|
nLast--;
|
|
}
|
|
|
|
if (bInHeader) {
|
|
wcscat(szHeader, szLine);
|
|
wcscat(szHeader, L"\r\n");
|
|
continue;
|
|
}
|
|
|
|
dwLevel = 0;
|
|
swscanf(szLine, L"%s %s %s %d", szDate, szTime, szDll, &dwLevel);
|
|
|
|
_wcslwr(szDll);
|
|
|
|
if (!wcsstr(szDll, L".dll") || dwLevel == 0) {
|
|
WCHAR szError[1000];
|
|
|
|
swprintf(szError, L"Unrecognized data in line: %s", szLine);
|
|
OutputDebugStringW(szError);
|
|
continue;
|
|
}
|
|
|
|
if (dwLevel == 1) {
|
|
if (!wcsstr(szErrors, szDll)) {
|
|
wcscat(szErrors, szDll);
|
|
wcscat(szErrors, L"\r\n");
|
|
}
|
|
} else if (dwLevel == 2) {
|
|
if (!wcsstr(szWarnings, szDll)) {
|
|
wcscat(szWarnings, szDll);
|
|
wcscat(szWarnings, L"\r\n");
|
|
}
|
|
} else {
|
|
if (!wcsstr(szOther, szDll)) {
|
|
wcscat(szOther, szDll);
|
|
wcscat(szOther, L"\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
SetDlgItemText(hDlg, IDC_EDIT_HEADER, szHeader);
|
|
SetDlgItemText(hDlg, IDC_EDIT_ERRORS, szErrors);
|
|
SetDlgItemText(hDlg, IDC_EDIT_WARNINGS, szWarnings);
|
|
SetDlgItemText(hDlg, IDC_EDIT_OTHER, szOther);
|
|
|
|
out:
|
|
if (file) {
|
|
fclose(file);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Message handler for parse dialog.
|
|
LRESULT CALLBACK DlgParseLog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hDC;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
ParseFileLog(hDlg);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch LOWORD(wParam) {
|
|
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void ShowHelp(void)
|
|
{
|
|
MessageBox(NULL, L"Apply a compatibility layer in addition to any fixes in the database:\n\n"
|
|
L" runcompat (layer name) (command line)\n\n"
|
|
L"Apply a compatibility layer and disable any existing fixes:\n\n"
|
|
L" runcompat !(layer name) (command line)\n\n"
|
|
L"Run GUI version:\n\n"
|
|
L" runcompat", L"Runcompat Command-Line Usage", MB_OK);
|
|
}
|
|
|