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
2109 lines
53 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.cpp
|
|
|
|
Abstract:
|
|
|
|
Various utility functions
|
|
|
|
Author:
|
|
|
|
kinshu created July 2, 2001
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "uxtheme.h" // needed for tab control theme support
|
|
|
|
extern "C" {
|
|
BOOL ShimdbcExecute(LPCWSTR lpszCmdLine);
|
|
}
|
|
|
|
//////////////////////// Extern variables /////////////////////////////////////
|
|
|
|
extern TAG g_Attributes[];
|
|
extern HANDLE g_arrhEventNotify[];
|
|
extern HANDLE g_hThreadWait;
|
|
extern HKEY g_hKeyNotify[];
|
|
extern HWND g_hDlg;
|
|
extern HINSTANCE g_hInstance;
|
|
extern TCHAR g_szAppPath[MAX_PATH];
|
|
extern BOOL g_bDeletingEntryTree;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////// Defines And Typedefs ////////////////////////////////
|
|
|
|
typedef void (CALLBACK *PFN_SHIMFLUSHCACHE)(HWND, HINSTANCE, LPSTR, int);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////// Function Declarations //////////////////////////////
|
|
|
|
BOOL
|
|
WriteXML(
|
|
CSTRING& szFilename,
|
|
CSTRINGLIST* pString
|
|
);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////// Global variables ////////////////////////////////////
|
|
|
|
// Process handle of the running exe. Set in InvokeExe
|
|
HANDLE g_hTestRunExec;
|
|
|
|
// The name of the program file that is to be executed
|
|
CSTRING g_strTestFile;
|
|
|
|
// Commandline for the program file that has to be executed
|
|
CSTRING g_strCommandLine;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
INT_PTR CALLBACK
|
|
TestRunWait(
|
|
IN HWND hWnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
TestRunWait
|
|
|
|
Desc: Handler for the Test Run Wait Dialog. This is the dialog that
|
|
says "Waiting for Application to Finish"
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam: 0 we do not want to show the wait dialog
|
|
|
|
Return: Standard dialog handler return
|
|
--*/
|
|
{
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
if (lParam == 0) {
|
|
|
|
//
|
|
// We do not wish to show the wait dialog
|
|
//
|
|
SendMessage(hWnd, WM_USER_TESTRUN_NODIALOG, 0, 0);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
|
|
} else {
|
|
|
|
ShowWindow(hWnd, SW_SHOWNORMAL);
|
|
SetTimer(hWnd, 0, 50, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
case WM_TIMER:
|
|
{
|
|
//
|
|
// Check if the app being test-run has terminated, if yes close the dialog
|
|
//
|
|
DWORD dwResult = WaitForSingleObject(g_hTestRunExec, 10);
|
|
|
|
if (dwResult == WAIT_OBJECT_0) {
|
|
KillTimer(hWnd, 0);
|
|
EndDialog(hWnd, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_USER_TESTRUN_NODIALOG:
|
|
{
|
|
|
|
//
|
|
// Wait till the app being test run is running and then close the dialog
|
|
//
|
|
WaitForSingleObject(g_hTestRunExec, INFINITE);
|
|
EndDialog(hWnd, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InvokeEXE(
|
|
IN PCTSTR szEXE,
|
|
IN PCTSTR szCommandLine,
|
|
IN BOOL bWait,
|
|
IN BOOL bDialog,
|
|
IN BOOL bConsole,
|
|
IN HWND hwndParent = NULL
|
|
)
|
|
/*++
|
|
InvokeEXE
|
|
|
|
Desc: Creates the process and shows the wait dialog box
|
|
|
|
Params:
|
|
IN PCTSTR szEXE: Name of the program that is being executed
|
|
IN PCTSTR szCommandLine: Exe name and the command-line for the exe
|
|
IN BOOL bWait: Should we wait till the app finishes?
|
|
IN BOOL bDialog: Should we show the wait dialog?
|
|
IN BOOL bConsole: If true, then we do not show any window
|
|
IN HWND hwndParent (NULL): The parent of the wait window, if it is created
|
|
If this is NULL, then we set the main app window as the parent
|
|
|
|
Return:
|
|
TRUE: ShellExecuteEx was successful
|
|
FALSE: Otherewise
|
|
|
|
Notes: If bWait is FALSE, then this function will return immediately, otherwise it will
|
|
return when the new process has terminated
|
|
|
|
--*/
|
|
{
|
|
BOOL bCreate;
|
|
SHELLEXECUTEINFO shEx;
|
|
|
|
ZeroMemory(&shEx, sizeof(SHELLEXECUTEINFO));
|
|
|
|
if (hwndParent == NULL) {
|
|
hwndParent = g_hDlg;
|
|
}
|
|
|
|
//
|
|
// We need to disable the main window. After CreateProcess() the wizard that was
|
|
// modal till now starts behaving like a modeless wizard. We do not want the user to change
|
|
// selections on the main dialog or start up some other wizard.
|
|
//
|
|
ENABLEWINDOW(g_hDlg, FALSE);
|
|
|
|
shEx.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
shEx.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
shEx.hwnd = hwndParent;
|
|
shEx.lpVerb = TEXT("open");
|
|
shEx.lpFile = szEXE;
|
|
shEx.lpParameters = szCommandLine;
|
|
shEx.nShow = SW_SHOWNORMAL;
|
|
|
|
|
|
bCreate = ShellExecuteEx(&shEx);
|
|
|
|
if (bCreate && bWait) {
|
|
//
|
|
// We need to wait till the process has terminated
|
|
//
|
|
g_hTestRunExec = shEx.hProcess;
|
|
|
|
//
|
|
// If we have to show the wait dialog, bDialog should be TRUE
|
|
//
|
|
DialogBoxParam(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_WAIT),
|
|
hwndParent,
|
|
TestRunWait,
|
|
(LPARAM)bDialog);
|
|
//
|
|
// Now the app has terminated
|
|
//
|
|
if (shEx.hProcess) {
|
|
CloseHandle(shEx.hProcess);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since the process has terminated let us now Enable the main window again
|
|
//
|
|
ENABLEWINDOW(g_hDlg, TRUE);
|
|
|
|
return bCreate ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL
|
|
InvokeCompiler(
|
|
IN CSTRING& szInCommandLine
|
|
)
|
|
/*++
|
|
InvokeCompiler
|
|
|
|
Desc: Runs the database compiler: shimdbc. Shimdbc.dll is statically linked
|
|
into CompatAdmin.exe
|
|
|
|
Params:
|
|
IN CSTRING& szInCommandLine: Commandline to the compiler
|
|
|
|
Return:
|
|
TRUE: The compiler was executed successfully
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
CSTRING szCommandLine = szInCommandLine;
|
|
|
|
szCommandLine.Sprintf(TEXT("shimdbc.exe %s"), (LPCTSTR)szInCommandLine);
|
|
|
|
if (!ShimdbcExecute(szCommandLine)) {
|
|
|
|
MessageBox(NULL,
|
|
CSTRING(IDS_COMPILER_ERROR),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
TestRunDlg(
|
|
IN HWND hWnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
TestRunWait
|
|
|
|
Desc: Dialog proc for the test run dialog box. This dialog box, takes the
|
|
name of the program to execute and the commandlines
|
|
|
|
Params: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam: The PDBENTRY for then entry being test run
|
|
|
|
Return:
|
|
TRUE: Pressed OK
|
|
FALSE: Pressed Cancel
|
|
--*/
|
|
{
|
|
static CSTRING s_strExeName;
|
|
|
|
switch (uMsg) {
|
|
case WM_INITDIALOG:
|
|
|
|
s_strExeName.Release();
|
|
|
|
if ((PDBENTRY)lParam) {
|
|
s_strExeName = ((PDBENTRY)lParam)->strExeName;
|
|
}
|
|
|
|
//
|
|
// Set the file name of the program. g_strTestFile is set in TestRun()
|
|
//
|
|
SetWindowText(GetDlgItem(hWnd, IDC_EXE), g_strTestFile);
|
|
|
|
//
|
|
// Change the OK button status properly
|
|
//
|
|
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_EXE, EN_CHANGE), 0);
|
|
|
|
SHAutoComplete(GetDlgItem(hWnd, IDC_EXE), AUTOCOMPLETE);
|
|
|
|
//
|
|
// Limit the length of the exe path
|
|
//
|
|
SendMessage(GetDlgItem(hWnd, IDC_EXE),
|
|
EM_LIMITTEXT,
|
|
(WPARAM)MAX_PATH - 1,
|
|
(LPARAM)0);
|
|
|
|
SendMessage(GetDlgItem(hWnd, IDC_COMMANDLINE),
|
|
EM_LIMITTEXT,
|
|
(WPARAM)MAX_PATH - 1,
|
|
(LPARAM)0);
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_EXE:
|
|
{
|
|
if (EN_CHANGE == HIWORD(wParam)) {
|
|
|
|
HWND hwndOkButton = GetDlgItem(hWnd, IDOK);
|
|
INT iLength = 0;
|
|
TCHAR szExeName[MAX_PATH];
|
|
|
|
*szExeName = 0;
|
|
|
|
//
|
|
// Disable the OK button if we do not have the complete path
|
|
//
|
|
GetDlgItemText(hWnd, IDC_EXE, szExeName, ARRAYSIZE(szExeName));
|
|
iLength = CSTRING::Trim(szExeName);
|
|
|
|
if (iLength < 3) {
|
|
//
|
|
// Cannot be a proper path
|
|
//
|
|
ENABLEWINDOW(hwndOkButton , FALSE);
|
|
} else {
|
|
//
|
|
// Ok button should be enabled if we have a
|
|
// Local path or network path
|
|
//
|
|
if (NotCompletePath(szExeName)) {
|
|
ENABLEWINDOW(hwndOkButton, FALSE);
|
|
} else {
|
|
ENABLEWINDOW(hwndOkButton, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_BROWSE:
|
|
{
|
|
HWND hwndFocus = GetFocus();
|
|
CSTRING szFilename;
|
|
TCHAR szBuffer[MAX_PATH] = TEXT("");
|
|
|
|
GetString(IDS_EXEFILTER, szBuffer, ARRAYSIZE(szBuffer));
|
|
|
|
if (GetFileName(hWnd,
|
|
CSTRING(IDS_FINDEXECUTABLE),
|
|
szBuffer,
|
|
g_strTestFile,
|
|
TEXT(""),
|
|
OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST,
|
|
TRUE,
|
|
szFilename)) {
|
|
|
|
g_strTestFile = szFilename;
|
|
|
|
SetWindowText(GetDlgItem(hWnd, IDC_EXE), g_strTestFile);
|
|
}
|
|
|
|
SetFocus(hwndFocus);
|
|
break;
|
|
}
|
|
|
|
case IDOK:
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
|
|
*szBuffer = 0;
|
|
|
|
GetWindowText(GetDlgItem(hWnd, IDC_EXE), szBuffer, ARRAYSIZE(szBuffer));
|
|
|
|
//
|
|
// Check if we are test running the correct file
|
|
//
|
|
if (s_strExeName != PathFindFileName(szBuffer)) {
|
|
//
|
|
// User did not give the complete path of the program being fixed.
|
|
//
|
|
MessageBox(hWnd, GetString(IDS_DOESNOTMATCH), g_szAppName, MB_ICONWARNING);
|
|
break;
|
|
}
|
|
|
|
g_strTestFile = szBuffer;
|
|
|
|
*szBuffer = 0;
|
|
GetWindowText(GetDlgItem(hWnd, IDC_COMMANDLINE), szBuffer, ARRAYSIZE(szBuffer));
|
|
|
|
g_strCommandLine = szBuffer;
|
|
|
|
EndDialog(hWnd, 1);
|
|
break;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hWnd, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
FlushCache(
|
|
void
|
|
)
|
|
/*++
|
|
FlushCache
|
|
|
|
Desc: Calls FlushCache from apphelp.dll to flush the shim cache. We should flush the
|
|
cache before doing a test run
|
|
|
|
--*/
|
|
{
|
|
PFN_SHIMFLUSHCACHE pShimFlushCache;
|
|
TCHAR szLibPath[MAX_PATH * 2];
|
|
HMODULE hAppHelp = NULL;
|
|
UINT uResult = 0;
|
|
K_SIZE k_size = MAX_PATH;
|
|
|
|
*szLibPath = 0;
|
|
|
|
uResult = GetSystemDirectory(szLibPath, k_size);
|
|
|
|
if (uResult == 0 || uResult >= k_size) {
|
|
|
|
Dbg(dlError, "%s - Failed to Execute GetSystemDirectory. Result was %d", __FUNCTION__, uResult);
|
|
|
|
return;
|
|
}
|
|
|
|
ADD_PATH_SEPARATOR(szLibPath, ARRAYSIZE(szLibPath));
|
|
|
|
StringCchCat(szLibPath, ARRAYSIZE(szLibPath), TEXT("apphelp.dll"));
|
|
|
|
hAppHelp = LoadLibrary(szLibPath);
|
|
|
|
if (hAppHelp) {
|
|
pShimFlushCache = (PFN_SHIMFLUSHCACHE)GetProcAddress(hAppHelp,
|
|
"ShimFlushCache");
|
|
|
|
if (pShimFlushCache) {
|
|
pShimFlushCache(NULL, NULL, NULL, 0);
|
|
}
|
|
|
|
FreeLibrary(hAppHelp);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
TestRun(
|
|
IN PDBENTRY pEntry,
|
|
IN OUT CSTRING* pszFile,
|
|
IN CSTRING* pszCommandLine,
|
|
IN HWND hwndParent,
|
|
IN CSTRINGLIST* pstrlXML //(NULL)
|
|
)
|
|
/*++
|
|
|
|
TestRun
|
|
|
|
Desc: Pops up the test run dialog that lets users to test run programs. This is
|
|
interface for test running programs
|
|
|
|
Params:
|
|
IN PDBENTRY pEntry: The entry that has to be test-run. We need this variable
|
|
because we need to call GetXML which takes this as a param. If pszFile is NULL, then we get
|
|
the name of the program file from pEntry->strExeName
|
|
|
|
IN OUT CSTRING* pszFile: The file name of the program that has to be test-run
|
|
IN CSTRING* pszCommandLine: The command line for the program that has to be test-run
|
|
IN HWND hwndParent: The intended parent for the actual test-run dialog
|
|
IN CSTRINGLIST* pstrlXML (NULL): LUA wizard likes to give the XML generated using
|
|
LuapGenerateTrackXML
|
|
|
|
Return:
|
|
TRUE: Success
|
|
FALSE: There was some error or the user pressed CANCEL in the test run dialog
|
|
|
|
Notes: pEntry will be NULL if we have to run an app from the disk search window,
|
|
but we no longer allow that.
|
|
|
|
--*/
|
|
{
|
|
CSTRING strCommandLine, strFile, strSdbPath;
|
|
CSTRINGLIST strlXMLTemp;
|
|
TCHAR szSystemDir[MAX_PATH * 2];
|
|
TCHAR szLogPath[MAX_PATH * 2];
|
|
HWND hwndFocus = GetFocus();
|
|
BOOL bResult = FALSE;
|
|
UINT uResult = 0;
|
|
|
|
g_strTestFile.Release();
|
|
g_strCommandLine.Release();
|
|
|
|
*szSystemDir = 0;
|
|
|
|
if (pszFile && pszFile->Length()) {
|
|
|
|
//
|
|
// Set the name of the program that has to be executed. This will be used by the test-run dialog
|
|
//
|
|
g_strTestFile = *pszFile;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have not been given the complete path, so we should get the name of the exe
|
|
// from pEntry
|
|
//
|
|
if (pEntry == NULL) {
|
|
goto End;
|
|
}
|
|
//
|
|
// Set the name of the program that has to be executed. This will be used by the test-run dialog
|
|
//
|
|
g_strTestFile = pEntry->strExeName;
|
|
}
|
|
|
|
if (pszCommandLine && pszCommandLine->Length()) {
|
|
|
|
//
|
|
// Set the name of the command line for the program that has to be executed.
|
|
// This will be used by the test-run dialog
|
|
//
|
|
g_strCommandLine = *pszCommandLine;
|
|
}
|
|
|
|
//
|
|
// Show the test run dialog
|
|
//
|
|
if (0 == DialogBoxParam(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_TESTRUN),
|
|
hwndParent,
|
|
TestRunDlg,
|
|
(LPARAM)pEntry)) {
|
|
//
|
|
// Cancel pressed
|
|
//
|
|
goto End;
|
|
}
|
|
|
|
*szLogPath = 0;
|
|
|
|
uResult = GetSystemWindowsDirectory(szLogPath, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "TestRun", "GetSystemWindowsDirectory failed");
|
|
bResult = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Set the SHIM_FILE_LOG env. variable so that we can show the shim log
|
|
//
|
|
ADD_PATH_SEPARATOR(szLogPath, ARRAYSIZE(szLogPath));
|
|
|
|
StringCchCat(szLogPath, ARRAYSIZE(szLogPath), TEXT("AppPatch\\") SHIM_FILE_LOG_NAME);
|
|
|
|
//
|
|
// Delete previous log file if any
|
|
//
|
|
DeleteFile(szLogPath);
|
|
|
|
SetEnvironmentVariable(TEXT("SHIM_FILE_LOG"), SHIM_FILE_LOG_NAME);
|
|
|
|
//
|
|
// If this is a system database, we do not need to create/install an sdb
|
|
// OR If we are calling this TestRun from the disk search window also then we do not
|
|
// need to get any xml If we are calling TestRun from the disk search window,
|
|
// then we will already have the complete path (but not the pointer to the entry)
|
|
// and in that case pEtnry can be NULL
|
|
//
|
|
if ((g_pPresentDataBase && g_pPresentDataBase->type == DATABASE_TYPE_GLOBAL)
|
|
|| pEntry == NULL) {
|
|
|
|
//
|
|
// Flush the shim cache, so that we do not get the previous fixes. We are flushing it here
|
|
// because we are not installing the test database as the entry resides in the system database
|
|
//
|
|
FlushCache();
|
|
|
|
if (!InvokeEXE((LPCTSTR)g_strTestFile, (LPCTSTR)g_strCommandLine, TRUE, TRUE, TRUE, hwndParent)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERROREXECUTE),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
|
|
bResult = FALSE;
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// We are done, now eject...
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if (pstrlXML == NULL) {
|
|
|
|
//
|
|
// LUA wizard will provides its own XML, for other cases we must get that
|
|
//
|
|
BOOL bReturn = GetXML(pEntry, FALSE, &strlXMLTemp, g_pPresentDataBase);
|
|
|
|
if (!bReturn) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
pstrlXML = &strlXMLTemp;
|
|
}
|
|
|
|
uResult = GetSystemWindowsDirectory(szSystemDir, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Write the XML into AppPatch\\Test.XML
|
|
//
|
|
ADD_PATH_SEPARATOR(szSystemDir, ARRAYSIZE(szSystemDir));
|
|
|
|
strFile.Sprintf(TEXT("%sAppPatch\\Test.XML"), szSystemDir);
|
|
|
|
if (!WriteXML(strFile, pstrlXML)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_UNABLETOSAVETEMP),
|
|
g_szAppName,
|
|
MB_OK);
|
|
|
|
goto End;
|
|
}
|
|
|
|
strCommandLine.Sprintf(TEXT("custom \"%sAppPatch\\Test.XML\" \"%sAppPatch\\Test.SDB\""),
|
|
szSystemDir,
|
|
szSystemDir);
|
|
|
|
if (!InvokeCompiler(strCommandLine)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERRORCOMPILE),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// No need to flush the shim cache, it is done when we install a database,
|
|
// sdbinst.exe does it for us.
|
|
//
|
|
|
|
// Note the space after AppPatch\\Test.SDB
|
|
strSdbPath.Sprintf(TEXT("%sAppPatch\\Test.SDB "),(LPCTSTR)szSystemDir);
|
|
|
|
//
|
|
// Install the test database
|
|
//
|
|
if (!InstallDatabase(strSdbPath, TEXT("-q"), FALSE)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERRORINSTALL),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Now execute the app to be test run
|
|
//
|
|
if (!InvokeEXE((LPCTSTR)g_strTestFile, (LPCTSTR)g_strCommandLine, TRUE, TRUE, TRUE, hwndParent)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERROREXECUTE),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
|
|
goto EXIT;
|
|
}
|
|
|
|
//
|
|
// Uninstall the test database
|
|
//
|
|
if (!InstallDatabase(strSdbPath, TEXT("-q -u"), FALSE)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERRORUNINSTALL),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
goto EXIT;
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
|
|
EXIT:
|
|
|
|
strCommandLine.Sprintf(TEXT("%sAppPatch\\Test.XML"), szSystemDir);
|
|
DeleteFile(strCommandLine);
|
|
|
|
strCommandLine.Sprintf(TEXT("%sAppPatch\\Test.SDB"), szSystemDir);
|
|
DeleteFile(strCommandLine);
|
|
|
|
//
|
|
// If caller wants it, then return the app path that we executed. Caller might need it
|
|
// because he was not having the compete path
|
|
//
|
|
if (bResult && pszFile) {
|
|
*pszFile = g_strTestFile;
|
|
}
|
|
|
|
End:
|
|
|
|
return bResult;
|
|
}
|
|
|
|
VOID
|
|
FormatVersion(
|
|
IN ULONGLONG ullBinVer,
|
|
OUT PTSTR pszText,
|
|
IN INT chBuffSize
|
|
)
|
|
/*++
|
|
|
|
FormatVersion
|
|
|
|
Desc: Formats a LARGE_INTEGER into a.b.c.d format
|
|
|
|
Params:
|
|
IN LARGE_INTEGER liBinVer: The LARGE_INTEGER to format
|
|
OUT LPTSTR pszText: The buffer that will store the complete formatted string
|
|
IN INT chBuffSize: The size of the buffer in characters
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
WORD dwWord = 0;
|
|
TCHAR szTemp[10];
|
|
|
|
if (chBuffSize < 16) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
*szTemp = 0;
|
|
|
|
dwWord = WORD(ullBinVer >> 48);
|
|
|
|
StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d"), dwWord);
|
|
StringCchCat(pszText, chBuffSize, szTemp);
|
|
|
|
dwWord = (WORD)(ullBinVer >> 32);
|
|
|
|
if (dwWord == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
|
|
StringCchCat(pszText, chBuffSize, szTemp);
|
|
|
|
dwWord = (WORD)(ullBinVer >> 16);
|
|
|
|
if (dwWord == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
|
|
StringCchCat(pszText, chBuffSize, szTemp);
|
|
|
|
dwWord = (WORD)(ullBinVer);
|
|
|
|
if (dwWord == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
StringCchPrintf(szTemp, ARRAYSIZE(szTemp), TEXT(".%d"), dwWord);
|
|
StringCchCat(pszText, chBuffSize, szTemp);
|
|
}
|
|
|
|
BOOL
|
|
InstallDatabase(
|
|
IN CSTRING& strPath,
|
|
IN PCTSTR szOptions,
|
|
IN BOOL bShowDialog
|
|
)
|
|
/*++
|
|
|
|
InstallDatabase
|
|
|
|
Desc: Installs or Uninstalls a database using sdbinst.exe. This guy lives in the system32 dir.
|
|
|
|
Params:
|
|
IN CSTRING& strPath: The path of the database (.sdb) file
|
|
IN PCTSTR szOptions: The options to be passed to sdbinst.exe
|
|
IN BOOL bShowDialog: Should we show the dialog after the install/uninstall is over?
|
|
We do not want to show that when we are doing a test run and we have to install the database
|
|
|
|
Return:
|
|
TRUE: Success
|
|
FALSE: There was some error
|
|
--*/
|
|
{
|
|
TCHAR szSystemDir[MAX_PATH];
|
|
CSTRING strSdbInstCommandLine;
|
|
CSTRING strSdbInstExe;
|
|
UINT uResult = 0;
|
|
|
|
*szSystemDir = 0;
|
|
|
|
uResult = GetSystemDirectory(szSystemDir, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
ADD_PATH_SEPARATOR(szSystemDir, ARRAYSIZE(szSystemDir));
|
|
|
|
strSdbInstExe.Sprintf(TEXT("%ssdbinst.exe"), szSystemDir);
|
|
|
|
strSdbInstCommandLine.Sprintf(TEXT("%s \"%s\" "),
|
|
szOptions,
|
|
strPath.pszString);
|
|
|
|
BOOL bOk = TRUE;
|
|
|
|
HWND hwndFocus = GetFocus();
|
|
|
|
//
|
|
// We do not want the installed database list and tree items to get refreshed
|
|
// when we are (un)installing
|
|
// a database because of Test Run. If user is actually (un)installing a database
|
|
// then we manually refresh the list in the handler for the corresonding WM_COMMAND
|
|
//
|
|
g_bUpdateInstalledRequired = FALSE;
|
|
|
|
//
|
|
// Stall the thread that refreshes the installed databases list and tree items
|
|
//
|
|
while (SuspendThread(g_hThreadWait) == -1) {
|
|
;
|
|
}
|
|
|
|
//
|
|
// Call sdbinst.exe
|
|
//
|
|
if (!InvokeEXE((LPCTSTR)strSdbInstExe, (LPCTSTR)strSdbInstCommandLine, TRUE, FALSE, FALSE, g_hDlg)) {
|
|
|
|
MessageBox(g_hDlg,
|
|
CSTRING(IDS_ERRORINSTALL),
|
|
g_szAppName,
|
|
MB_ICONERROR);
|
|
|
|
bOk = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Show the Dialog only if quiet mode is off
|
|
//
|
|
if (bShowDialog) {
|
|
|
|
CSTRING strMessage;
|
|
|
|
if (_tcschr(szOptions, TEXT('u')) || _tcschr(szOptions, TEXT('g'))) {
|
|
//
|
|
// Uninstalling database
|
|
//
|
|
strMessage.Sprintf(GetString(IDS_UINSTALL),
|
|
g_pPresentDataBase->strName);
|
|
|
|
} else {
|
|
//
|
|
// Installing database
|
|
//
|
|
strMessage.Sprintf(GetString(IDS_INSTALL),
|
|
g_pPresentDataBase->strName);
|
|
}
|
|
|
|
MessageBox(g_hDlg, strMessage.pszString, g_szAppName, MB_ICONINFORMATION);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Listen for app compat regsitry changes
|
|
//
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_ALLUSERS],
|
|
TRUE);
|
|
|
|
RegNotifyChangeKeyValue(g_hKeyNotify[IND_ALLUSERS],
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_arrhEventNotify[IND_PERUSER],
|
|
TRUE);
|
|
|
|
SetFocus(hwndFocus);
|
|
|
|
//
|
|
// Resume the thread that refreshes the installed databases list and tree items
|
|
//
|
|
ResumeThread(g_hThreadWait);
|
|
|
|
return bOk;
|
|
}
|
|
|
|
void
|
|
CenterWindow(
|
|
IN HWND hParent,
|
|
IN HWND hWnd
|
|
)
|
|
/*++
|
|
|
|
CenterWindow
|
|
|
|
Desc: Centers a dialog wrt to its parent
|
|
|
|
Params:
|
|
IN HWND hParent: The parent of the dialog to center
|
|
IN HWND hWnd: The dialog to center
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
RECT rRect;
|
|
RECT rParentRect;
|
|
|
|
GetWindowRect(hWnd, &rRect);
|
|
GetWindowRect(hParent, &rParentRect);
|
|
|
|
//
|
|
// Compute actual width and height
|
|
//
|
|
rRect.right -= rRect.left;
|
|
rRect.bottom -= rRect.top;
|
|
|
|
rParentRect.right -= rParentRect.left;
|
|
rParentRect.bottom -= rParentRect.top;
|
|
|
|
int nX;
|
|
int nY;
|
|
|
|
//
|
|
// Resolve X, Y location required to center whole window.
|
|
//
|
|
nX = (rParentRect.right - rRect.right) / 2;
|
|
nY = (rParentRect.bottom - rRect.bottom) / 2;
|
|
|
|
//
|
|
// Move the window to the center location.
|
|
//
|
|
MoveWindow(hWnd,
|
|
rRect.left + nX,
|
|
rRect.top + nY,
|
|
rRect.right,
|
|
rRect.bottom,
|
|
TRUE);
|
|
|
|
}
|
|
|
|
int
|
|
CDECL
|
|
MSGF(
|
|
IN HWND hwndParent,
|
|
IN PCTSTR pszCaption,
|
|
IN UINT uType,
|
|
IN PCTSTR pszFormat,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
MSGF
|
|
|
|
Desc: Variable argument MessageBox
|
|
|
|
Params:
|
|
IN HWND hwndParent: The parent for the message box
|
|
IN PCTSTR pszCaption: The caption for the message box
|
|
IN UINT uType: The messagebox type param
|
|
IN PCTSTR pszFormat: The format string
|
|
|
|
Return: Whatever MessageBox() returns
|
|
--*/
|
|
{
|
|
TCHAR szBuffer[1024];
|
|
|
|
*szBuffer = 0;
|
|
|
|
va_list pArgList;
|
|
va_start(pArgList, pszFormat);
|
|
|
|
StringCchVPrintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, pArgList);
|
|
|
|
va_end(pArgList);
|
|
|
|
return MessageBox(hwndParent,
|
|
szBuffer,
|
|
pszCaption,
|
|
uType);
|
|
|
|
}
|
|
|
|
void
|
|
EnableTabBackground(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
EnableTabBackground
|
|
|
|
Desc: Makes the back ground of a dialog blend with the tab background. Enables the texture
|
|
|
|
Params:
|
|
IN HWND hDlg: The dialog box to whose back ground we want to change
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
PFNEnableThemeDialogTexture pFnEnableThemeDialogTexture;
|
|
HMODULE hUxTheme;
|
|
TCHAR szThemeManager[MAX_PATH * 2];
|
|
UINT uResult = 0;
|
|
|
|
*szThemeManager = 0;
|
|
|
|
uResult = GetSystemDirectory(szThemeManager, MAX_PATH);
|
|
|
|
if (uResult == 0 || uResult >= MAX_PATH) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
ADD_PATH_SEPARATOR(szThemeManager, ARRAYSIZE(szThemeManager));
|
|
|
|
StringCchCat(szThemeManager, ARRAYSIZE(szThemeManager), _T("uxtheme.dll"));
|
|
|
|
hUxTheme = (HMODULE)LoadLibrary(szThemeManager);
|
|
|
|
if (hUxTheme) {
|
|
pFnEnableThemeDialogTexture = (PFNEnableThemeDialogTexture)
|
|
GetProcAddress(hUxTheme, "EnableThemeDialogTexture");
|
|
if (pFnEnableThemeDialogTexture) {
|
|
pFnEnableThemeDialogTexture(hDlg, 4 /*ETDT_USETABTEXTURE*/);
|
|
}
|
|
|
|
FreeLibrary(hUxTheme);
|
|
}
|
|
}
|
|
|
|
int
|
|
TagToIndex(
|
|
IN TAG tag
|
|
)
|
|
/*++
|
|
TagToIndex
|
|
|
|
Desc: Gets the The index in the attribute info array (g_rgAttributeTags)
|
|
|
|
Return: The index in the attribute info array (g_rgAttributeTags), if found.
|
|
-1: Otherwise
|
|
|
|
Note:
|
|
--*/
|
|
{
|
|
int i;
|
|
int iAttrCount = (int)ATTRIBUTE_COUNT;
|
|
|
|
for (i = 0; i < iAttrCount; i++) {
|
|
if (tag == g_Attributes[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
PTSTR
|
|
GetString(
|
|
IN UINT iResource,
|
|
OUT PTSTR pszStr, //(NULL)
|
|
IN INT nLength //(0) bytes
|
|
)
|
|
/*++
|
|
|
|
GetString
|
|
|
|
Desc: Wrapper for LoadString. If pszStr == NULL, then loads the resouce string in a static
|
|
TCHAR[1024] and returns the pointer to that.
|
|
|
|
Params:
|
|
IN UINT iResource: The string resource ID
|
|
OUT PTSTR pszStr (NULL): The buffer in which we might need to read
|
|
in the string resource
|
|
|
|
IN INT nLength (0) bytes: If pszStr is not NULL then this will contain
|
|
the size of the buffer in bytes.
|
|
|
|
Return:
|
|
The pointer to the string read.
|
|
--*/
|
|
{
|
|
static TCHAR s_szString[1024];
|
|
|
|
if (NULL == pszStr) {
|
|
|
|
*s_szString = 0;
|
|
LoadString(g_hInstance, iResource, s_szString, ARRAYSIZE(s_szString));
|
|
return s_szString;
|
|
}
|
|
|
|
*pszStr = 0;
|
|
LoadString(g_hInstance, iResource, pszStr, nLength);
|
|
|
|
return pszStr;
|
|
}
|
|
|
|
DWORD
|
|
WIN_MSG(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
WIN_MSG
|
|
|
|
Desc: Shows up the message for the last Windows error
|
|
|
|
--*/
|
|
|
|
{
|
|
LPVOID lpMsgBuf = NULL;
|
|
DWORD returnVal;
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
returnVal = GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(PTSTR) &lpMsgBuf,
|
|
0,
|
|
NULL);
|
|
|
|
//
|
|
// Prefix :-(
|
|
//
|
|
if (lpMsgBuf) {
|
|
MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
|
|
if (lpMsgBuf) {
|
|
LocalFree(lpMsgBuf);
|
|
lpMsgBuf = NULL;
|
|
}
|
|
|
|
return returnVal;
|
|
|
|
}
|
|
|
|
INT
|
|
Atoi(
|
|
IN PCTSTR pszStr,
|
|
OUT BOOL* pbValid
|
|
)
|
|
/*++
|
|
Atoi
|
|
|
|
Desc: Converts a string into a integer.
|
|
|
|
Params:
|
|
IN PCTSTR pszStr: The string to convert into a integer
|
|
OUT BOOL* pbValid: Will be FALSE, if the string was not an integer e.g. "foo",
|
|
otherwise this will be TRUE
|
|
|
|
Return:
|
|
The integer representation of the string
|
|
--*/
|
|
{
|
|
BOOL bNegative = FALSE;
|
|
INT result = 0, iIndex = 0;
|
|
|
|
if (pszStr == NULL) {
|
|
|
|
if (pbValid) {
|
|
*pbValid = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (pbValid) {
|
|
*pbValid = TRUE;
|
|
}
|
|
|
|
while (isspace(*pszStr)) {
|
|
pszStr++;
|
|
}
|
|
|
|
if (*pszStr == TEXT('-')) {
|
|
bNegative = TRUE;
|
|
++pszStr;
|
|
}
|
|
|
|
while (isspace(*pszStr)) {
|
|
pszStr++;
|
|
}
|
|
|
|
while (*pszStr) {
|
|
|
|
if (*pszStr >= TEXT('0') && *pszStr <= TEXT('9')) {
|
|
result = 10 * result + (*pszStr) - TEXT('0');
|
|
} else {
|
|
|
|
if (pbValid) {
|
|
*pbValid = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
++pszStr;
|
|
}
|
|
|
|
if (bNegative) {
|
|
return 0 - result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
NotCompletePath(
|
|
IN PCTSTR pszFileName
|
|
)
|
|
/*++
|
|
|
|
NotCompletePath
|
|
|
|
Desc: Checks if we have the complete path or just the file name
|
|
|
|
Params:
|
|
IN PCTSTR pszFileName: The file-name to check
|
|
|
|
Return:
|
|
TRUE: pszFileName is not a complete path
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
if (!pszFileName) {
|
|
assert(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
if (lstrlen(pszFileName) < 3) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((isalpha(pszFileName[0]) && pszFileName[1] == TEXT(':'))
|
|
|| (pszFileName[0] == TEXT('\\') && pszFileName[1] == TEXT('\\'))) {
|
|
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
TreeDeleteAll(
|
|
IN HWND hwndTree
|
|
)
|
|
/*++
|
|
|
|
TreeDeleteAll
|
|
|
|
Desc: Deletes all the items from this tree
|
|
|
|
Params:
|
|
IN HWND hwndTree: The handle to the tree view
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0);
|
|
|
|
if (hwndTree == g_hwndEntryTree) {
|
|
g_bDeletingEntryTree = TRUE;
|
|
}
|
|
|
|
TreeView_DeleteAllItems(hwndTree);
|
|
|
|
if (hwndTree == g_hwndEntryTree) {
|
|
g_bDeletingEntryTree = FALSE;
|
|
}
|
|
|
|
SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0);
|
|
SendMessage(hwndTree, WM_NCPAINT, 1, 0);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FormatDate(
|
|
IN PSYSTEMTIME pSysTime,
|
|
OUT PTSTR pszDate,
|
|
IN UINT cchDate
|
|
)
|
|
/*++
|
|
|
|
FormatDate
|
|
|
|
Desc: Formats a PSYSTEMTIME to a format that can be displayed.
|
|
The format is the form of day , month date, year, hr:minutes:seconds AM/PM
|
|
|
|
Params:
|
|
IN PSYSTEMTIME pSysTime: The time that we want to format
|
|
OUT PTSTR pszDate: The buffer that will hold the formatted string
|
|
IN UINT cchDate: The size of the buffer in characters
|
|
--*/
|
|
{
|
|
TCHAR szDay[128], szMonth[128];
|
|
|
|
if (pSysTime == NULL || pszDate == NULL) {
|
|
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
*szDay = *pszDate = *szMonth = 0;
|
|
|
|
GetString(IDS_DAYS + pSysTime->wDayOfWeek, szDay, ARRAYSIZE(szDay));
|
|
GetString(IDS_MONTHS + pSysTime->wMonth - 1, szMonth, ARRAYSIZE(szMonth));
|
|
|
|
StringCchPrintf(pszDate,
|
|
cchDate - 3,
|
|
TEXT("%s, %s %02d, %02d, %02d:%02d:%02d "),
|
|
szDay,
|
|
szMonth,
|
|
pSysTime->wDay,
|
|
pSysTime->wYear,
|
|
(pSysTime->wHour % 12) == 0 ? 12 : pSysTime->wHour % 12,
|
|
pSysTime->wMinute, pSysTime->wSecond);
|
|
|
|
if (pSysTime->wHour >= 12) {
|
|
StringCchCat(pszDate, cchDate, GetString(IDS_PM));
|
|
} else {
|
|
StringCchCat(pszDate, cchDate, GetString(IDS_AM));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
GetFileContents(
|
|
IN PCTSTR pszFileName,
|
|
OUT PWSTR* ppwszFileContents
|
|
)
|
|
/*++
|
|
GetFileContents
|
|
|
|
Desc: Given a file name, this function gets the contents in a unicode buffer.
|
|
|
|
Params:
|
|
IN PCTSTR pszFileName: The name of the file
|
|
OUT PWSTR* ppwszFileContents: The buffer in which to store the contents
|
|
|
|
Return:
|
|
TRUE: If we successfully copied the concents into a unicode buffer.
|
|
FALSE: Otherwise.
|
|
--*/
|
|
{
|
|
BOOL bIsSuccess = FALSE;
|
|
LPSTR pszFileContents = NULL;
|
|
LPWSTR pwszFileContents = NULL;
|
|
|
|
if (ppwszFileContents) {
|
|
*ppwszFileContents = 0;
|
|
}
|
|
|
|
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD cSize = 0;
|
|
|
|
// The file is small so we don't care about the high-order
|
|
// word of the file.
|
|
if ((cSize = GetFileSize(hFile, NULL)) == -1) {
|
|
Dbg(dlError, "[GetFileContents] Opening file %S failed: %d",
|
|
pszFileName, GetLastError());
|
|
goto EXIT;
|
|
} else {
|
|
DWORD cNumberOfBytesRead = 0;
|
|
pszFileContents = new CHAR [cSize];
|
|
|
|
if (pszFileContents) {
|
|
if (ReadFile(hFile, pszFileContents, cSize, &cNumberOfBytesRead, NULL)) {
|
|
// Convert to unicode.
|
|
DWORD cSizeUnicode = MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
pszFileContents,
|
|
cNumberOfBytesRead,
|
|
0,
|
|
0);
|
|
|
|
pwszFileContents = new WCHAR [cSizeUnicode + 1];
|
|
|
|
if (pwszFileContents) {
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
pszFileContents,
|
|
cNumberOfBytesRead,
|
|
pwszFileContents,
|
|
cSizeUnicode);
|
|
|
|
pwszFileContents[cSizeUnicode] = L'\0';
|
|
|
|
if (ppwszFileContents) {
|
|
*ppwszFileContents = pwszFileContents;
|
|
} else {
|
|
Dbg(dlError, "[GetFileContents] ppwszFileContents is NULL");
|
|
}
|
|
bIsSuccess = TRUE;
|
|
} else {
|
|
Dbg(dlError, "[GetFileContents] Error allocating memory");
|
|
|
|
goto EXIT;
|
|
}
|
|
} else {
|
|
Dbg(dlError, "[GetFileContents] Error reading the file contents: %d",
|
|
GetLastError());
|
|
|
|
goto EXIT;
|
|
}
|
|
} else {
|
|
Dbg(dlError, "[GetFileContents] Error allocating memory");
|
|
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
|
|
CloseHandle(hFile);
|
|
|
|
if (pszFileContents) {
|
|
delete [] pszFileContents;
|
|
}
|
|
|
|
if (bIsSuccess == FALSE) {
|
|
|
|
if (pwszFileContents) {
|
|
|
|
delete[] pwszFileContents;
|
|
pwszFileContents = NULL;
|
|
|
|
if (ppwszFileContents) {
|
|
*ppwszFileContents = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsSuccess;
|
|
}
|
|
|
|
void
|
|
TrimLeadingSpaces(
|
|
IN OUT LPCWSTR& pwsz
|
|
)
|
|
/*++
|
|
|
|
TrimLeadingSpaces
|
|
|
|
Desc: Removes the leading tabs and spaces
|
|
|
|
Params:
|
|
IN OUT LPCWSTR& pwsz: The string to be trimmed
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
|
|
{
|
|
if (pwsz) {
|
|
pwsz += wcsspn(pwsz, L" \t");
|
|
}
|
|
}
|
|
|
|
void
|
|
TrimTrailingSpaces(
|
|
IN OUT LPWSTR pwsz
|
|
)
|
|
/*++
|
|
|
|
TrimTrailingSpaces
|
|
|
|
Desc: Removes the trailing tabs and spaces
|
|
|
|
Params:
|
|
IN OUT LPWSTR pwsz: The string to be trimmed
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
if (pwsz) {
|
|
DWORD cLen = wcslen(pwsz);
|
|
LPWSTR pwszEnd = pwsz + cLen - 1;
|
|
|
|
while (pwszEnd >= pwsz && (*pwszEnd == L' ' || *pwszEnd == L'\t')) {
|
|
--pwszEnd;
|
|
}
|
|
|
|
*(++pwszEnd) = L'\0';
|
|
}
|
|
}
|
|
|
|
LPWSTR
|
|
GetNextLine(
|
|
IN LPWSTR pwszBuffer
|
|
)
|
|
/*++
|
|
|
|
GetNextLine
|
|
|
|
Desc: Given a buffer, get the contents up until "\r\n" or EOF.
|
|
Usage is the same as strtok.
|
|
|
|
Params:
|
|
IN LPWSTR pwszBuffer: Buffer to get next line from.
|
|
|
|
Return: Pointer to beginning of next line.
|
|
|
|
--*/
|
|
{
|
|
static LPWSTR pwsz;
|
|
LPWSTR pwszNextLineStart;
|
|
|
|
if (pwszBuffer) {
|
|
pwsz = pwszNextLineStart = pwszBuffer;
|
|
}
|
|
|
|
while (TRUE) {
|
|
// If we are at the end of the line, go to the next line
|
|
// if there's any.
|
|
if (*pwsz == L'\r') {
|
|
pwsz = pwsz + 2;
|
|
continue;
|
|
}
|
|
|
|
if (*pwsz == L'\0') {
|
|
return NULL;
|
|
}
|
|
|
|
pwszNextLineStart = pwsz;
|
|
|
|
while (*pwsz != L'\r' && *pwsz != L'\0') {
|
|
++pwsz;
|
|
}
|
|
|
|
if (*pwsz) {
|
|
// Set the end of the line.
|
|
*pwsz = L'\0';
|
|
pwsz = pwsz + 2;
|
|
}
|
|
|
|
return pwszNextLineStart;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LPWSTR GetNextToken(
|
|
IN OUT LPWSTR pwsz
|
|
)
|
|
/*++
|
|
|
|
Desc: Parse the commandline argument for the LUA shims using ' ' as the delimiter.
|
|
If a token has spaces, use double quotes around it. Use this function the
|
|
same way you use strtok except you don't have to specify the delimiter.
|
|
|
|
Params:
|
|
IN OUT LPWSTR pwsz: The string to parse.
|
|
|
|
Return Value: Pointer to the next token.
|
|
|
|
--*/
|
|
{
|
|
static LPWSTR pwszToken;
|
|
static LPWSTR pwszEndOfLastToken;
|
|
|
|
if (!pwsz) {
|
|
pwsz = pwszEndOfLastToken;
|
|
}
|
|
|
|
// Skip the white space.
|
|
while (*pwsz && *pwsz == ' ') {
|
|
++pwsz;
|
|
}
|
|
|
|
pwszToken = pwsz;
|
|
|
|
BOOL fInsideQuotes = 0;
|
|
|
|
while (*pwsz) {
|
|
switch(*pwsz) {
|
|
case L'"':
|
|
fInsideQuotes ^= 1;
|
|
|
|
if (fInsideQuotes) {
|
|
++pwszToken;
|
|
}
|
|
|
|
case L' ':
|
|
if (!fInsideQuotes) {
|
|
goto EXIT;
|
|
}
|
|
|
|
default:
|
|
++pwsz;
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
if (*pwsz) {
|
|
*pwsz = L'\0';
|
|
pwszEndOfLastToken = ++pwsz;
|
|
} else {
|
|
pwszEndOfLastToken = pwsz;
|
|
}
|
|
|
|
return pwszToken;
|
|
}
|
|
|
|
int CALLBACK
|
|
CompareItemsEx(
|
|
IN LPARAM lParam1,
|
|
IN LPARAM lParam2,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
CompareItemsEx
|
|
|
|
Desc: Used to sort items in the list view for a column
|
|
|
|
Params:
|
|
IN LPARAM lParam1: Index of the first item
|
|
IN LPARAM lParam2: Index of the second item
|
|
IN LPARAM lParam: ListView_SortItemEx's lParamSort parameter. This is a COLSORT*
|
|
|
|
Return: Return a negative value if the first item should precede the second,
|
|
a positive value if the first item should follow the second,
|
|
or zero if the two items are equivalent.
|
|
|
|
Notes: Our comparison is case-INsensitive. The COLSORT contains the handle of the list view,
|
|
the index of the column for which we are doing the sort and a bit array. The
|
|
bit arrays helps us to determine, which columns are sorted in which way and this
|
|
function should reverse the sort order. Initially we will assume that the cols
|
|
are sorted in ascending order (they might not be actually) and when we click on the
|
|
column for the first time, we will sort them in descending order.
|
|
|
|
--*/
|
|
{
|
|
COLSORT* pColSort = (COLSORT*)lParam;
|
|
TCHAR szBufferOne[512], szBufferTwo[512];
|
|
LVITEM lvi;
|
|
HWND hwndList = pColSort->hwndList;
|
|
CSTRING strExeNameOne, strExeNameTwo;
|
|
INT nVal = 0;
|
|
|
|
*szBufferOne = *szBufferTwo = 0;
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
lvi.mask = LVIF_TEXT;
|
|
lvi.iItem = (INT)lParam1;
|
|
lvi.iSubItem = pColSort->iCol;
|
|
lvi.pszText = szBufferOne;
|
|
lvi.cchTextMax = ARRAYSIZE(szBufferOne);
|
|
|
|
if (!ListView_GetItem(hwndList, &lvi)) {
|
|
|
|
assert(FALSE);
|
|
return -1;
|
|
}
|
|
|
|
lvi.mask = LVIF_TEXT;
|
|
lvi.iItem = (INT)lParam2;
|
|
lvi.iSubItem = pColSort->iCol;
|
|
lvi.pszText = szBufferTwo;
|
|
lvi.cchTextMax = ARRAYSIZE(szBufferTwo);
|
|
|
|
if (!ListView_GetItem(hwndList, &lvi)) {
|
|
|
|
assert(FALSE);
|
|
return -1;
|
|
}
|
|
|
|
nVal = lstrcmpi(szBufferOne, szBufferTwo);
|
|
|
|
if (nVal == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if ((pColSort->lSortColMask & (1L << pColSort->iCol)) == 0) {
|
|
|
|
// This is in ascending order right now, sort in descending order
|
|
nVal == -1 ? nVal = 1 : nVal = -1;
|
|
}
|
|
|
|
return nVal;
|
|
}
|
|
|
|
BOOL
|
|
SaveListViewToFile(
|
|
IN HWND hwndList,
|
|
IN INT iCols,
|
|
IN PCTSTR pszFile,
|
|
IN PCTSTR pszHeader
|
|
)
|
|
/*++
|
|
|
|
SaveListViewToFile
|
|
|
|
Desc: Saves the contents of list view to a file in tab separated form. Also prints the header
|
|
before writing the contents
|
|
|
|
Params:
|
|
IN HWND hwndList: The handle to the list view
|
|
IN INT iCols: Number of columns in the list view
|
|
IN PCTSTR pszFile: The path of the file in which we want to save the contents
|
|
IN PTSTR pszHeader: Any header to be written in the file before writing the contents
|
|
|
|
Return:
|
|
TRUE: Success
|
|
FALSE: Error
|
|
--*/
|
|
{
|
|
FILE* fp = _tfopen(pszFile, TEXT("w"));
|
|
TCHAR szBuffer[256];
|
|
LVCOLUMN lvCol;
|
|
LVITEM lvi;
|
|
|
|
*szBuffer = 0;
|
|
|
|
if (fp == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pszHeader) {
|
|
fwprintf(fp, TEXT("%s\n\r"), pszHeader);
|
|
}
|
|
|
|
//
|
|
// Print the column names first
|
|
//
|
|
ZeroMemory(&lvCol, sizeof(lvCol));
|
|
|
|
lvCol.mask = LVCF_TEXT;
|
|
lvCol.pszText = szBuffer;
|
|
lvCol.cchTextMax = sizeof(szBuffer)/sizeof(szBuffer[0]);
|
|
|
|
for (INT iIndex = 0; iIndex < iCols; ++iIndex) {
|
|
|
|
*szBuffer = 0;
|
|
ListView_GetColumn(hwndList, iIndex, &lvCol);
|
|
fwprintf(fp, TEXT("%s\t"), lvCol.pszText);
|
|
}
|
|
|
|
fwprintf(fp, TEXT("\n\n"));
|
|
|
|
INT iRowCount = ListView_GetItemCount(hwndList);
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
for (INT iRowIndex = 0; iRowIndex < iRowCount; ++ iRowIndex) {
|
|
for (INT iColIndex = 0; iColIndex < iCols; ++iColIndex) {
|
|
|
|
*szBuffer = 0;
|
|
|
|
lvi.mask = LVIF_TEXT;
|
|
lvi.pszText = szBuffer;
|
|
lvi.cchTextMax = sizeof(szBuffer)/sizeof(szBuffer[0]);
|
|
lvi.iItem = iRowIndex;
|
|
lvi.iSubItem = iColIndex;
|
|
|
|
if (!ListView_GetItem(hwndList, &lvi)) {
|
|
assert(FALSE);
|
|
}
|
|
|
|
fwprintf(fp, TEXT("%s\t"), szBuffer);
|
|
}
|
|
|
|
fwprintf(fp, TEXT("\n"));
|
|
}
|
|
|
|
fclose(fp);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ReplaceChar(
|
|
IN OUT PTSTR pszString,
|
|
IN TCHAR chCharToFind,
|
|
IN TCHAR chReplaceBy
|
|
)
|
|
/*++
|
|
|
|
ReplaceChar
|
|
|
|
Desc: Replaces all occurences of chCharToFind in pszString by chReplaceBy
|
|
|
|
Params:
|
|
IN OUT PTSTR pszString: The string in which the replace has to be made
|
|
IN TCHAR chCharToFind: The char to look for
|
|
IN TCHAR chReplaceBy: All occurences of chCharToFind will be replaced by this
|
|
|
|
Return:
|
|
TRUE: At least one replacement was done.
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
BOOL bChanged = FALSE; // Did the string change?
|
|
|
|
while (*pszString) {
|
|
|
|
if (*pszString == chCharToFind) {
|
|
*pszString = chReplaceBy;
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
++pszString;
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
INT
|
|
Tokenize(
|
|
IN PCTSTR szString,
|
|
IN INT cchLength,
|
|
IN PCTSTR szDelims,
|
|
OUT CSTRINGLIST& strlTokens
|
|
)
|
|
/*++
|
|
|
|
Tokenize
|
|
|
|
Desc: Tokenizes the string szString based on delimiters szDelims and puts the individual
|
|
tokens in strlTokens
|
|
|
|
Params:
|
|
IN PCTSTR szString: The string to tokenize
|
|
IN INT cchLength: The length of szString. Note that this is the length obtained using lstrlen
|
|
IN PCTSTR szDelims: The delimiter string
|
|
OUT CSTRINGLIST& strlTokens: Will contain the tokens
|
|
|
|
Return:
|
|
The count of tokens produced
|
|
|
|
Note: Please note that the tokens are always trimmed, so we cannot have a token that begins
|
|
or ends with a tab or a space
|
|
--*/
|
|
{
|
|
TCHAR* pszCopyBeg = NULL; // Pointer to the pszCopy so that we can free it
|
|
TCHAR* pszCopy = NULL; // Will contain the copy of szString
|
|
TCHAR* pszEnd = NULL; // Pointer to end of token
|
|
INT iCount = 0; // Total tokens found
|
|
BOOL bNullFound = FALSE;
|
|
CSTRING strTemp;
|
|
K_SIZE k_pszCopy = (cchLength + 1);
|
|
|
|
strlTokens.DeleteAll();
|
|
|
|
pszCopy = new TCHAR[k_pszCopy];
|
|
|
|
if (pszCopy == NULL) {
|
|
MEM_ERR;
|
|
goto End;
|
|
}
|
|
|
|
SafeCpyN(pszCopy, szString, k_pszCopy);
|
|
pszCopyBeg = pszCopy;
|
|
|
|
//
|
|
// Search for tokens
|
|
//
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Ignore leading delimiters
|
|
//
|
|
while (*pszCopy && _tcschr(szDelims, *pszCopy)) {
|
|
pszCopy++;
|
|
}
|
|
|
|
if (*pszCopy == NULL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find the end of the token
|
|
//
|
|
pszEnd = pszCopy + _tcscspn(pszCopy, szDelims);
|
|
|
|
if (*pszEnd == 0) {
|
|
//
|
|
// No more tokens will be found, we have found the last token
|
|
//
|
|
bNullFound = TRUE;
|
|
}
|
|
|
|
*pszEnd = 0;
|
|
|
|
++iCount;
|
|
|
|
strTemp = pszCopy;
|
|
strTemp.Trim();
|
|
|
|
strlTokens.AddString(strTemp);
|
|
|
|
if (bNullFound == FALSE) {
|
|
//
|
|
// There might be still some tokens that we will get
|
|
//
|
|
pszCopy = pszEnd + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
End:
|
|
|
|
if (pszCopyBeg) {
|
|
delete[] pszCopyBeg;
|
|
pszCopyBeg = NULL;
|
|
}
|
|
|
|
return iCount;
|
|
}
|
|
|
|
void
|
|
ShowInlineHelp(
|
|
IN LPCTSTR pszInlineHelpHtmlFile
|
|
)
|
|
/*++
|
|
ShowInlineHelp
|
|
|
|
Desc:
|
|
Shows in line help by loading the specified html file
|
|
|
|
Params:
|
|
IN LPCTSTR pszInlineHelpHtmlFile: The html file that contained the
|
|
help
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
TCHAR szPath[MAX_PATH * 2], szDir[MAX_PATH], szDrive[MAX_PATH * 2];
|
|
INT iType = 0;
|
|
|
|
if (pszInlineHelpHtmlFile == NULL) {
|
|
return;
|
|
}
|
|
|
|
*szDir = *szDrive = 0;
|
|
|
|
_tsplitpath(g_szAppPath, szDrive, szDir, NULL, NULL);
|
|
|
|
StringCchPrintf(szPath,
|
|
ARRAYSIZE(szPath),
|
|
TEXT("%s%sCompatAdmin.chm::/%s"),
|
|
szDrive,
|
|
szDir,
|
|
pszInlineHelpHtmlFile);
|
|
|
|
HtmlHelp(GetDesktopWindow(), szPath, HH_DISPLAY_TOPIC, 0);
|
|
}
|
|
|
|
PTSTR
|
|
GetSpace(
|
|
IN OUT PTSTR pszSpace,
|
|
IN INT iSpaces,
|
|
IN INT iBuffSize
|
|
)
|
|
/*++
|
|
GetSpace
|
|
|
|
Desc:
|
|
Fills in pszSpace with iSpaces number of spaces
|
|
|
|
Params:
|
|
IN OUT PTSTR pszSpace: The buffer that will be filled with spaces
|
|
IN INT iSpaces: The number of spaces to fill
|
|
IN INT iBuffSize: Size of buffer in TCHARS
|
|
|
|
|
|
Return:
|
|
The modified buffer
|
|
--*/
|
|
{
|
|
if (pszSpace == NULL) {
|
|
//
|
|
// Error..
|
|
//
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Fill the buffer with spaces
|
|
//
|
|
for (INT iLoop = 0; iLoop < min(iSpaces, iBuffSize - 1); ++iLoop) {
|
|
|
|
*(pszSpace + iLoop) = TEXT(' ');
|
|
}
|
|
|
|
//
|
|
// Put the terminating NULL
|
|
//
|
|
*(pszSpace + min(iSpaces, iBuffSize - 1)) = 0;
|
|
|
|
End:
|
|
return pszSpace;
|
|
|
|
}
|
|
|
|
BOOL
|
|
ValidInput(
|
|
IN PCTSTR pszStr
|
|
)
|
|
/*++
|
|
ValidInput
|
|
|
|
Desc: Checks if the input contains chars other than space, tab, new line and carriage return
|
|
|
|
Params:
|
|
IN PCTSTR pszStr: The input that we want to check for validity
|
|
|
|
Return:
|
|
TRUE: Valid input
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
BOOL bOk = FALSE;
|
|
|
|
if (pszStr == NULL) {
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
while (*pszStr) {
|
|
if (*pszStr != TEXT(' ') && *pszStr != TEXT('\t') && *pszStr != TEXT('\n') && *pszStr != TEXT('\r')) {
|
|
bOk = TRUE;
|
|
goto End;
|
|
}
|
|
|
|
++pszStr;
|
|
}
|
|
|
|
End:
|
|
return bOk;
|
|
}
|
|
|