windows-nt/Source/XPSP1/NT/windows/appcompat/tools/sdbinst/sdbinst.cpp
2020-09-26 16:20:57 +08:00

3536 lines
91 KiB
C++

/*--
Copyright (c) 1999 Microsoft Corporation
Module Name:
sdbinst.cpp
Abstract:
installs custom SDB files into AppPatch\Custom, and adds registry entries to point
to them
Author:
dmunsil 12/29/2000
Revision History:
Many people contributed over time.
(in alphabetical order: clupu, dmunsil, rparsons, vadimb)
Notes:
--*/
#define _UNICODE
#define WIN
#define FLAT_32
#define TRUE_IF_WIN32 1
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#define _WINDOWS
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <tchar.h>
#include <aclapi.h>
#include "resource.h"
extern "C" {
#include "shimdb.h"
}
BOOL g_bQuiet;
BOOL g_bWin2K;
WCHAR g_wszCustom[MAX_PATH];
BOOL g_bAllowPatches = FALSE;
HINSTANCE g_hInst;
HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
typedef enum _INSTALL_MODE {
MODE_INSTALL,
MODE_UNINSTALL,
MODE_CLEANUP,
MODE_CONVERT_FORMAT_NEW,
MODE_CONVERT_FORMAT_OLD
} INSTALL_MODE;
#define UNINSTALL_KEY_PATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
#define APPCOMPAT_KEY L"System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
DWORD g_dwWow64Key = (DWORD)-1;
void
__cdecl
vPrintError(
UINT unRes,
...
)
{
WCHAR szT[1024];
WCHAR wszFormat[1024];
WCHAR wszCaption[1024];
va_list arglist;
if (!g_bQuiet) {
if (!LoadStringW(g_hInst, IDS_APP_ERROR_TITLE, wszCaption, 1024)) {
return;
}
if (LoadStringW(g_hInst, unRes, wszFormat, 1024)) {
va_start(arglist, unRes);
_vsnwprintf(szT, 1023, wszFormat, arglist);
szT[1022] = 0;
va_end(arglist);
MessageBoxW(NULL, szT, wszCaption, MB_OK | MB_ICONWARNING);
}
}
}
void
__cdecl
vPrintMessage(
UINT unRes,
...
)
{
WCHAR szT[1024];
WCHAR wszFormat[1024];
WCHAR wszCaption[1024];
va_list arglist;
if (!g_bQuiet) {
if (!LoadStringW(g_hInst, IDS_APP_TITLE, wszCaption, 1024)) {
return;
}
if (LoadStringW(g_hInst, unRes, wszFormat, 1024)) {
va_start(arglist, unRes);
_vsnwprintf(szT, 1023, wszFormat, arglist);
szT[1022] = 0;
va_end(arglist);
MessageBoxW(NULL, szT, wszCaption, MB_OK | MB_ICONINFORMATION);
}
}
}
void
__cdecl
vLogMessage(
LPCSTR pwszFormat,
...
)
{
CHAR szT[1024];
va_list arglist;
int nLength;
va_start(arglist, pwszFormat);
nLength = _vsnprintf(szT, CHARCOUNT(szT), pwszFormat, arglist);
if (nLength < 0) {
szT[1023] = '\0';
nLength = sizeof(szT);
} else {
nLength *= sizeof(szT[0]);
}
va_end(arglist);
if (g_hLogFile != INVALID_HANDLE_VALUE) {
DWORD dwWritten;
WriteFile(g_hLogFile, (LPVOID)szT, (DWORD)nLength, &dwWritten, NULL);
}
OutputDebugStringA(szT);
}
DWORD
GetWow64Flag(
void
)
{
if (g_dwWow64Key == (DWORD)-1) {
if (g_bWin2K) {
g_dwWow64Key = 0; // no flag since there is no wow64 on win2k
} else {
g_dwWow64Key = KEY_WOW64_64KEY;
}
}
return g_dwWow64Key;
}
VOID
OpenLogFile(
VOID
)
{
WCHAR wszLogFile[MAX_PATH];
CHAR szBuffer[1024];
GetSystemWindowsDirectoryW(wszLogFile, CHARCOUNT(wszLogFile));
wcscat(wszLogFile, L"\\AppPatch\\SdbInst.Log");
g_hLogFile = CreateFileW(wszLogFile,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
VOID
CloseLogFile(
VOID
)
{
if (g_hLogFile != INVALID_HANDLE_VALUE) {
CloseHandle(g_hLogFile);
}
g_hLogFile = INVALID_HANDLE_VALUE;
}
void
vPrintHelp(
WCHAR* szAppName
)
{
vPrintMessage(IDS_HELP_TEXT, szAppName);
}
typedef void (CALLBACK *pfn_ShimFlushCache)(HWND, HINSTANCE, LPSTR, int);
void
vFlushCache(
void
)
{
HMODULE hAppHelp;
pfn_ShimFlushCache pShimFlushCache;
hAppHelp = LoadLibraryW(L"apphelp.dll");
if (hAppHelp) {
pShimFlushCache = (pfn_ShimFlushCache)GetProcAddress(hAppHelp, "ShimFlushCache");
if (pShimFlushCache) {
pShimFlushCache(NULL, NULL, NULL, 0);
}
}
}
BOOL
bSearchGroupForSID(
DWORD dwGroup,
BOOL* pfIsMember
)
{
PSID pSID = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
BOOL fRes = TRUE;
if (!AllocateAndInitializeSid(&SIDAuth,
2,
SECURITY_BUILTIN_DOMAIN_RID,
dwGroup,
0,
0,
0,
0,
0,
0,
&pSID)) {
return FALSE;
}
if (!pSID) {
return FALSE;
}
if (!CheckTokenMembership(NULL, pSID, pfIsMember)) {
fRes = FALSE;
}
FreeSid(pSID);
return fRes;
}
BOOL
bCanRun(
void
)
{
BOOL fIsAdmin;
if (!bSearchGroupForSID(DOMAIN_ALIAS_RID_ADMINS, &fIsAdmin))
{
return FALSE;
}
return fIsAdmin;
}
WCHAR*
wszGetFileFromPath(
WCHAR* wszPath
)
{
WCHAR* szTemp = wcsrchr(wszPath, L'\\');
if (szTemp) {
return szTemp + 1;
}
return NULL;
}
BOOL
bIsAlreadyInstalled(
WCHAR* wszPath
)
{
DWORD dwCustomLen;
DWORD dwInputLen;
DWORD dwPos;
dwCustomLen = wcslen(g_wszCustom);
dwInputLen = wcslen(wszPath);
if (_wcsnicmp(wszPath, g_wszCustom, dwCustomLen) != 0) {
//
// it's not in the custom directory
//
return FALSE;
}
for (dwPos = dwCustomLen; dwPos < dwInputLen; ++dwPos) {
if (wszPath[dwPos] == L'\\') {
//
// it's in a subdirectory of Custom,
//
return FALSE;
}
}
return TRUE;
}
BOOL
bGuidToPath(
GUID* pGuid,
WCHAR* wszPath
)
{
UNICODE_STRING ustrGuid;
if (!NT_SUCCESS(RtlStringFromGUID(*pGuid, &ustrGuid))) {
return FALSE;
}
wcscpy(wszPath, g_wszCustom);
wcscat(wszPath, ustrGuid.Buffer);
wcscat(wszPath, L".sdb");
RtlFreeUnicodeString(&ustrGuid);
return TRUE;
}
BOOL
bGetGuid(
WCHAR* wszSDB,
GUID* pGuid
)
{
PDB pdb = NULL;
TAGID tiDatabase;
TAGID tiID;
BOOL bRet = FALSE;
pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
if (!pdb) {
vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
bRet = FALSE;
goto out;
}
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (!tiDatabase) {
vPrintError(IDS_NO_DB_TAG, wszSDB);
bRet = FALSE;
goto out;
}
ZeroMemory(pGuid, sizeof(GUID));
tiID = SdbFindFirstTag(pdb, tiDatabase, TAG_DATABASE_ID);
if (tiID) {
if (SdbReadBinaryTag(pdb, tiID, (PBYTE)pGuid, sizeof(GUID))) {
bRet = TRUE;
}
}
if (!bRet) {
vPrintError(IDS_NO_DB_ID, wszSDB);
}
out:
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
return bRet;
}
typedef enum _TIME_COMPARE {
FILE_NEWER,
FILE_SAME,
FILE_OLDER
} TIME_COMPARE;
BOOL
bOldSdbInstalled(
WCHAR* wszPath,
WCHAR* wszOldPath
)
{
WIN32_FIND_DATAW FindData;
GUID guidMain;
BOOL bRet = FALSE;
HANDLE hFind;
//
// get the guid from the DB we're installing
//
if (!bGetGuid(wszPath, &guidMain)) {
//
// there's no info in this DB, so no way to tell.
//
return FALSE;
}
//
// get the path to the current file
//
if (!bGuidToPath(&guidMain, wszOldPath)) {
//
// couldn't convert to path
//
return FALSE;
}
//
// check to see if the file exists
//
hFind = FindFirstFileW(wszOldPath, &FindData);
if (hFind != INVALID_HANDLE_VALUE) {
//
// yup
//
bRet = TRUE;
FindClose(hFind);
}
return bRet;
}
BOOL
IsKnownDatabaseGUID(
GUID* pGuid
)
{
const GUID* rgpGUID[] = {
&GUID_SYSMAIN_SDB,
&GUID_APPHELP_SDB,
&GUID_SYSTEST_SDB,
&GUID_DRVMAIN_SDB,
&GUID_MSIMAIN_SDB
};
int i;
for (i = 0; i < ARRAYSIZE(rgpGUID); ++i) {
if (*rgpGUID[i] == *pGuid) {
return TRUE;
}
}
return FALSE;
}
BOOL
DatabaseContainsPatch(
WCHAR* wszSDB
)
{
PDB pdb = NULL;
TAGID tiDatabase = TAGID_NULL;
TAGID tiLibrary = TAGID_NULL;
TAGID tiPatch = TAGID_NULL;
BOOL bRet = FALSE;
pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
if (!pdb) {
vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
bRet = FALSE;
goto out;
}
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (!tiDatabase) {
vPrintError(IDS_NO_DB_TAG, wszSDB);
bRet = FALSE;
goto out;
}
tiLibrary = SdbFindFirstTag(pdb, tiDatabase, TAG_LIBRARY);
if (!tiLibrary) {
//
// this isn't an error -- no library just means no patches
//
bRet = FALSE;
goto out;
}
tiPatch = SdbFindFirstTag(pdb, tiLibrary, TAG_PATCH);
if (tiPatch) {
bRet = TRUE;
} else {
bRet = FALSE;
}
out:
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
return bRet;
}
BOOL
bGetInternalNameAndID(
WCHAR* wszSDB,
WCHAR* wszInternalName,
GUID* pGuid
)
{
PDB pdb = NULL;
TAGID tiDatabase;
TAGID tiName;
TAGID tiID;
BOOL bRet = FALSE;
WCHAR* wszTemp;
pdb = SdbOpenDatabase(wszSDB, DOS_PATH);
if (!pdb) {
vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSDB);
bRet = FALSE;
goto out;
}
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (!tiDatabase) {
vPrintError(IDS_NO_DB_TAG, wszSDB);
bRet = FALSE;
goto out;
}
tiName = SdbFindFirstTag(pdb, tiDatabase, TAG_NAME);
if (tiName) {
wszTemp = SdbGetStringTagPtr(pdb, tiName);
}
if (wszTemp) {
wcscpy(wszInternalName, wszTemp);
} else {
wszInternalName[0] = 0;
}
ZeroMemory(pGuid, sizeof(GUID));
tiID = SdbFindFirstTag(pdb, tiDatabase, TAG_DATABASE_ID);
if (!tiID) {
bRet = FALSE;
goto out;
}
if (!SdbReadBinaryTag(pdb, tiID, (PBYTE)pGuid, sizeof(GUID))) {
bRet = FALSE;
goto out;
}
bRet = TRUE;
out:
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
return bRet;
}
BOOL
bFriendlyNameToFile(
WCHAR* wszFriendlyName,
WCHAR* wszFile,
WCHAR* wszPath
)
{
WCHAR wszSearchPath[MAX_PATH];
WIN32_FIND_DATAW FindData;
BOOL bRet = FALSE;
WCHAR wszInternalTemp[256];
WCHAR wszFileTemp[MAX_PATH];
GUID guidTemp;
HANDLE hFind;
wcscpy(wszSearchPath, g_wszCustom);
wcscat(wszSearchPath, L"*.sdb");
hFind = FindFirstFileW(wszSearchPath, &FindData);
if (hFind == INVALID_HANDLE_VALUE) {
return FALSE;
}
while (hFind != INVALID_HANDLE_VALUE) {
wcscpy(wszFileTemp, g_wszCustom);
wcscat(wszFileTemp, FindData.cFileName);
if (!bGetInternalNameAndID(wszFileTemp, wszInternalTemp, &guidTemp)) {
goto nextFile;
}
if (_wcsicmp(wszInternalTemp, wszFriendlyName) == 0) {
bRet = TRUE;
wcscpy(wszFile, FindData.cFileName);
wcscpy(wszPath, wszFileTemp);
FindClose(hFind);
break;
}
nextFile:
if (!FindNextFileW(hFind, &FindData)) {
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
}
return bRet;
}
BOOL
bFindInstallName(
WCHAR* wszPath,
WCHAR* wszInstallPath
)
{
GUID guidMain;
//
// get the guid from the DB we're installing
//
if (!bGetGuid(wszPath, &guidMain)) {
//
// there's no info in this DB, so no way to tell.
//
return FALSE;
}
//
// get the path to the current file
//
if (!bGuidToPath(&guidMain, wszInstallPath)) {
//
// couldn't convert to path
//
return FALSE;
}
return TRUE;
}
//
// this function is necessary because RegDeleteKey doesn't work right with
// a 32-bit app deleting 64-bit reg keys
//
LONG
LocalRegDeleteKeyW (
IN HKEY hKey,
IN LPCWSTR lpSubKey
)
{
LONG lRes;
HKEY hSubKey = NULL;
lRes = RegOpenKeyExW(hKey,
lpSubKey,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
&hSubKey);
if (lRes != ERROR_SUCCESS) {
return lRes;
}
lRes = NtDeleteKey(hSubKey);
RegCloseKey(hSubKey);
return lRes;
}
VOID
InstallW2KData(
WCHAR* pszEntryName,
LPCWSTR pszGuidDB
)
{
HKEY hKey;
WCHAR wszRegPath[MAX_PATH];
DWORD dwDisposition, cbData;
LONG lResult = 0;
BYTE data[16] = {0x0c, 0, 0, 0, 0, 0, 0, 0,
0x06, 0, 0, 0, 0, 0, 0, 0};
//
// This is Windows 2000 - attempt to add custom SDB specific data.
//
wsprintfW(wszRegPath, L"%s\\%s", APPCOMPAT_KEY, pszEntryName);
lResult = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
wszRegPath,
0,
NULL,
0,
KEY_SET_VALUE,
NULL,
&hKey,
&dwDisposition);
if (ERROR_SUCCESS != lResult) {
if (ERROR_ACCESS_DENIED == lResult) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
return;
} else {
vPrintError(IDS_CANT_CREATE_REG_KEY, pszEntryName);
return;
}
}
//
// Set the registry values.
//
lResult = RegSetValueExW(hKey,
pszGuidDB,
0,
REG_BINARY,
data,
sizeof(data));
if (ERROR_SUCCESS != lResult) {
RegCloseKey(hKey);
if (ERROR_ACCESS_DENIED == lResult) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
} else {
//
// djm - can't use new strings for XP SP1
//
//vPrintError(IDS_CANT_SET_REG_VALUE, pszEntryName);
vPrintError(IDS_CANT_CREATE_VALUE, pszEntryName);
}
return;
}
data[0] = 0;
wsprintfW(wszRegPath, L"DllPatch-%s", pszGuidDB);
lResult = RegSetValueExW(hKey,
wszRegPath,
0,
REG_SZ,
data,
2 * sizeof(WCHAR));
if (ERROR_SUCCESS != lResult) {
RegCloseKey(hKey);
if (ERROR_ACCESS_DENIED == lResult) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
} else {
//
// djm - can't use new strings for XP SP1
//
//vPrintError(IDS_CANT_SET_REG_VALUE, pszEntryName);
vPrintError(IDS_CANT_CREATE_VALUE, pszEntryName);
}
return;
}
RegCloseKey(hKey);
}
VOID
RemoveW2KData(
WCHAR* pszEntryName,
LPCWSTR pszGuidDB
)
{
HKEY hKey;
WCHAR wszRegPath[MAX_PATH];
LONG lResult = 0;
DWORD dwValues;
//
// This is Windows 2000 - attempt to remove custom SDB specific data.
//
wsprintfW(wszRegPath, L"%s\\%s", APPCOMPAT_KEY, pszEntryName);
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
wszRegPath,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
&hKey);
if (ERROR_SUCCESS != lResult) {
if (ERROR_ACCESS_DENIED == lResult) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
return;
} else {
vPrintError(IDS_CANT_OPEN_REG_KEY, wszRegPath);
return;
}
}
RegDeleteValueW(hKey, pszGuidDB);
wsprintfW(wszRegPath, L"DllPatch-%s", pszGuidDB);
RegDeleteValueW(hKey, wszRegPath);
//
// Figure out if we should delete the key, if there aren't any more values left
//
lResult = RegQueryInfoKey(hKey,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&dwValues,
NULL,
NULL,
NULL,
NULL);
RegCloseKey(hKey);
hKey = NULL;
if (dwValues != 0) {
return;
}
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
APPCOMPAT_KEY,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
&hKey);
if (ERROR_SUCCESS == lResult) {
lResult = LocalRegDeleteKeyW(hKey, pszEntryName);
}
if (lResult != ERROR_SUCCESS) {
vPrintError(IDS_CANT_DELETE_REG_KEY, pszEntryName, APPCOMPAT_KEY);
}
RegCloseKey(hKey);
}
// Caller is responsible for freeing the memory using delete [].
LPWSTR
ExpandItem(
LPCWSTR pwszItem
)
{
// Get the required length.
DWORD cLenExpand = ExpandEnvironmentStringsW(pwszItem, NULL, 0);
if (!cLenExpand)
{
return NULL;
}
//
// Make room for "\\?\"
//
cLenExpand += 4;
LPWSTR pwszItemExpand = new WCHAR [cLenExpand];
if (!pwszItemExpand)
{
return NULL;
}
LPWSTR pwszTemp = pwszItemExpand;
DWORD cTemp = cLenExpand;
wcscpy(pwszItemExpand, L"\\\\?\\");
pwszTemp += 4;
cTemp -= 4;
if (!ExpandEnvironmentStringsW(pwszItem, pwszTemp, cTemp))
{
return NULL;
}
return pwszItemExpand;
}
DWORD
GiveUsersWriteAccess(
LPWSTR pwszDir
)
{
DWORD dwRes;
PACL pOldDACL;
PACL pNewDACL = NULL;
SECURITY_DESCRIPTOR sd;
PSECURITY_DESCRIPTOR pSD = &sd;
GUID guidChildObjectType; // GUID of object to control creation of
PSID pTrusteeSID; // trustee for new ACE
EXPLICIT_ACCESS ea;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
PSID pUsersSID = NULL;
if ((dwRes = GetNamedSecurityInfoW(
pwszDir,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pOldDACL,
NULL,
&pSD)) != ERROR_SUCCESS) {
goto Cleanup;
}
if(!AllocateAndInitializeSid(
&SIDAuth,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_USERS,
0,
0,
0,
0,
0,
0,
&pUsersSID)) {
dwRes = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
//
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = FILE_ALL_ACCESS;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = (LPTSTR) pUsersSID;
//
// Create a new ACL that merges the new ACE
// into the existing DACL.
//
if ((dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) != ERROR_SUCCESS) {
goto Cleanup;
}
dwRes = SetNamedSecurityInfoW(
pwszDir,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
pNewDACL,
NULL);
Cleanup:
if (pUsersSID) {
FreeSid(pUsersSID);
}
return dwRes;
}
BOOL
SetupLUAAllUserDir(
LPCWSTR pwszAllUserDir
)
{
BOOL bRes = FALSE;
LPWSTR pwszExpandedDir = ExpandItem(pwszAllUserDir);
if (!pwszExpandedDir) {
//
// djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
//
//vPrintError(IDS_CANT_EXPAND_DIR, pwszAllUserDir);
vPrintError(IDS_APP_ERROR_TITLE);
return FALSE;
}
//
// Create the directory if it doesn't already exist.
//
DWORD dwAttributes = GetFileAttributesW(pwszExpandedDir);
if (dwAttributes != -1) {
if (!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
//
// djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
//
//vPrintError(IDS_OBJECT_ALREADY_EXISTS, pwszExpandedDir);
vPrintError(IDS_APP_ERROR_TITLE);
goto Cleanup;
}
} else {
if (!CreateDirectoryW(pwszExpandedDir, NULL)) {
//
// djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
//
//vPrintError(IDS_CANT_CREATE_DIRECTORY, pwszExpandedDir, GetLastError());
vPrintError(IDS_APP_ERROR_TITLE);
goto Cleanup;
}
}
//
// Give the Users group full control access (power users can already modify
// files in this directory).
//
if (GiveUsersWriteAccess((LPWSTR)pwszExpandedDir) != ERROR_SUCCESS) {
//
// djm - replacing new error messages with generic ones so we don't run afoul of the mui guys
//
//vPrintError(IDS_CANT_SET_ACLS, pwszExpandedDir);
vPrintError(IDS_APP_ERROR_TITLE);
goto Cleanup;
}
bRes = TRUE;
Cleanup:
delete [] pwszExpandedDir;
return bRes;
}
// buffer size is in characters (unicode)
BOOL
InstallSdbEntry(
WCHAR* szEntryName, // entry name (foo.exe or layer name)
LPCWSTR pszGuidDB, // guid database id in string format
PDB pdb,
TAGID tiAction, // the ACTION node.
ULONGLONG ullSdbTimeStamp, // representation of a timestamp
BOOL bLayer // true if layer name
)
{
LONG lRes;
WCHAR szRegPath[MAX_PATH];
WCHAR szDBName[MAX_PATH]; // this is used in older (win2k) versions
INT nch;
BOOL bReturn = FALSE;
HKEY hKey = NULL;
wcsncpy(szDBName, pszGuidDB, MAX_PATH);
szDBName[MAX_PATH - 1] = 0;
wcsncat(szDBName, L".sdb", MAX_PATH);
szDBName[MAX_PATH - 1] = 0;
pszGuidDB = szDBName;
//
// If this is Win2K, add data to the AppCompatibility key.
//
if (g_bWin2K) {
InstallW2KData(szEntryName, pszGuidDB);
}
// else we have a string
nch = _snwprintf(szRegPath,
sizeof(szRegPath)/sizeof(szRegPath[0]),
(bLayer ? L"%s\\Layers\\%s": L"%s\\%s"),
APPCOMPAT_KEY_PATH_CUSTOM_W,
szEntryName);
if (nch < 0) {
// error
vPrintError(IDS_BUFFER_TOO_SMALL);
goto HandleError;
}
lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
szRegPath,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS|GetWow64Flag(),
NULL,
&hKey,
NULL);
//
// on install, we want to quit if we hit an error.
// BUGBUG - should we undo whatever we've already completed?
//
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_REG_KEY, szRegPath);
goto HandleError;
}
lRes = RegSetValueExW(hKey,
pszGuidDB,
0,
REG_QWORD,
(PBYTE)&ullSdbTimeStamp,
sizeof(ullSdbTimeStamp));
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_VALUE, szRegPath);
goto HandleError;
}
LPWSTR szAllUserDir = NULL;
if (tiAction) {
//
// the ACTION node in the EXE shimmed with LUA looks like this:
//
// <ACTION NAME="REDIRECT" TYPE="ChangeACLs">
// <DATA NAME="AllUserDir" VALUETYPE="STRING"
// VALUE="%ALLUSERSPROFILE%\Application Data\Fireworks 3"/>
// </ACTION>
//
TAGID tiName, tiType, tiData, tiValue;
LPWSTR szName, szType, szData;
if ((tiName = SdbFindFirstTag(pdb, tiAction, TAG_NAME)) &&
(szName = SdbGetStringTagPtr(pdb, tiName))) {
if (!wcscmp(szName, L"REDIRECT")) {
if ((tiType = SdbFindFirstTag(pdb, tiAction, TAG_ACTION_TYPE)) &&
(szType = SdbGetStringTagPtr(pdb, tiType))) {
if (!wcscmp(szType, L"ChangeACLs")) {
if ((tiData = SdbFindFirstTag(pdb, tiAction, TAG_DATA)) &&
(tiValue = SdbFindFirstTag(pdb, tiData, TAG_DATA_STRING)) &&
(szAllUserDir = SdbGetStringTagPtr(pdb, tiValue))) {
if (!SetupLUAAllUserDir(szAllUserDir)) {
goto HandleError;
}
}
}
}
}
}
}
bReturn = TRUE;
HandleError:
if (hKey != NULL) {
RegCloseKey(hKey);
}
return bReturn;
}
BOOL
UninstallSdbEntry(
WCHAR* szEntryName, // foo.exe or layer name
LPCWSTR pszGuidDB, // guid (database id) in string format
ULONGLONG ullSdbTimeStamp, // 1ABFCA7ADC99FC timestamp
BOOL bLayer // true is layer
)
{
LONG lRes;
WCHAR szRegPath[MAX_PATH];
WCHAR szDBName[MAX_PATH];
INT nch;
BOOL bReturn = FALSE;
HKEY hKey = NULL;
DWORD dwValues;
WCHAR szOldInstallName[MAX_PATH];
wcsncpy(szDBName, pszGuidDB, MAX_PATH);
szDBName[MAX_PATH - 1] = 0;
wcsncat(szDBName, L".sdb", MAX_PATH);
szDBName[MAX_PATH - 1] = 0;
pszGuidDB = szDBName;
if (g_bWin2K) {
RemoveW2KData(szEntryName, pszGuidDB);
}
nch = _snwprintf(szRegPath,
sizeof(szRegPath)/sizeof(szRegPath[0]),
(bLayer ? L"%s\\Layers\\%s": L"%s\\%s"),
APPCOMPAT_KEY_PATH_CUSTOM_W,
szEntryName);
if (nch < 0) {
// error
vPrintError(IDS_BUFFER_TOO_SMALL);
goto Out;
}
lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
szRegPath,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
&hKey);
//
// if we fail to open a key on uninstall, keep going, so
// hopefully we can get as much uninstalled as possible.
//
if (lRes != ERROR_SUCCESS) {
if (lRes == ERROR_ACCESS_DENIED) {
vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
goto HandleError;
} else {
//
// DO NOT report an error - this key might have been cleaned up during the
// previous path, such as when identical exe names appear in the same db
// for instance, two setup.exe's -- the first pass will clean up the key,
// second path will fail to open them right here
//
// vPrintError(IDS_CANT_OPEN_REG_KEY, szRegPath);
goto Out;
}
}
lRes = RegDeleteValueW(hKey, pszGuidDB);
if (lRes != ERROR_SUCCESS) {
if (lRes == ERROR_ACCESS_DENIED) {
vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
goto HandleError; // fatal error
} else {
//
// bugbug - pszSdbInstallName
//
if (lRes == ERROR_FILE_NOT_FOUND) {
WCHAR wszOldFormat[MAX_PATH];
//
// aha, value's not there, try old format
//
wcscpy(wszOldFormat, pszGuidDB);
wcscat(wszOldFormat, L".sdb");
lRes = RegDeleteValueW(hKey, wszOldFormat);
}
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_DELETE_REG_VALUE, pszGuidDB, szRegPath);
}
}
}
//
// figure out if we should delete the key, if there aren't any more values left
//
lRes = RegQueryInfoKey(hKey,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&dwValues,
NULL,
NULL,
NULL,
NULL);
if (dwValues == 0) {
RegCloseKey(hKey);
hKey = NULL;
nch = _snwprintf(szRegPath,
sizeof(szRegPath)/sizeof(szRegPath[0]),
(bLayer ? L"%s\\Layers": L"%s"),
APPCOMPAT_KEY_PATH_CUSTOM_W);
if (nch < 0) {
// error
vPrintError(IDS_BUFFER_TOO_SMALL);
goto Out;
}
lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegPath, 0, KEY_WRITE|GetWow64Flag(), &hKey);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_OPEN_REG_KEY, szRegPath);
goto Out;
}
lRes = LocalRegDeleteKeyW(hKey, szEntryName);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_DELETE_REG_KEY, szEntryName, szRegPath);
}
}
Out:
bReturn = TRUE;
HandleError:
if (hKey != NULL) {
RegCloseKey(hKey);
}
return bReturn;
UNREFERENCED_PARAMETER(ullSdbTimeStamp);
}
/*++
GetTimeStampByGuid
This function recovers installed database timestamp.
If the value for the database timestamp (DatabaseInstallTimeStamp) does not exist -
it is created using the last write time on the key associated with a particular installed
database
[out] pTimeStamp - receives the timestamp of a database
[in] pGuidDB - guid id of a database
returns TRUE if successful
--*/
BOOL
GetTimeStampByGuid(
PULONGLONG pTimeStamp,
GUID* pGuidDB
)
{
NTSTATUS Status;
WCHAR szKeyPath[MAX_PATH];
UNICODE_STRING ustrGuid = { 0 };
LONG lResult;
HKEY hKey = NULL;
BOOL bSuccess = FALSE;
DWORD dwType;
DWORD dwBufferSize;
Status = RtlStringFromGUID(*pGuidDB, &ustrGuid);
if (!NT_SUCCESS(Status)) {
// no way to tell what's up with this guid, perhaps out of memory --
// or we're
return FALSE;
}
//
// construct the key
//
wcscpy (szKeyPath, APPCOMPAT_KEY_PATH_INSTALLEDSDB_W);
wcscat (szKeyPath, L"\\");
wcsncat(szKeyPath, ustrGuid.Buffer, ustrGuid.Length / sizeof(WCHAR));
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
szKeyPath,
0,
KEY_READ|GetWow64Flag(),
&hKey);
//
// query for value with timestamp
//
if (lResult != ERROR_SUCCESS) {
//
// oops -- can't open the key -- error condition
//
goto cleanup;
}
dwBufferSize = sizeof(*pTimeStamp);
lResult = RegQueryValueExW(hKey,
L"DatabaseInstallTimeStamp",
NULL,
&dwType,
(LPBYTE)pTimeStamp,
&dwBufferSize);
if (lResult != ERROR_SUCCESS || dwType != REG_BINARY) {
//
// we can try exhaustive search using values at this point
// there (apparently) is no install timestamp
//
if (lResult == ERROR_FILE_NOT_FOUND) {
FILETIME ftTimeStamp;
ULARGE_INTEGER liTimeStamp;
lResult = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &ftTimeStamp);
if (lResult == STATUS_SUCCESS) {
liTimeStamp.LowPart = ftTimeStamp.dwLowDateTime;
liTimeStamp.HighPart = ftTimeStamp.dwHighDateTime;
*pTimeStamp = liTimeStamp.QuadPart;
}
} else if (lResult == ERROR_SUCCESS && dwType != REG_BINARY) {
lResult = ERROR_INVALID_DATA;
}
if (lResult != ERROR_SUCCESS) {
goto cleanup;
}
}
//
// success
//
bSuccess = TRUE;
cleanup:
if (ustrGuid.Buffer != NULL) {
RtlFreeUnicodeString(&ustrGuid);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return bSuccess;
}
NTSTATUS
SDBAPI
FindCharInUnicodeString(
ULONG Flags,
PCUNICODE_STRING StringToSearch,
PCUNICODE_STRING CharSet,
USHORT* NonInclusivePrefixLength
)
{
LPCWSTR pch;
//
// implement only the case when we move backward
//
if (Flags != RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END) {
return STATUS_NOT_IMPLEMENTED;
}
pch = StringToSearch->Buffer + StringToSearch->Length / sizeof(WCHAR);
while (pch >= StringToSearch->Buffer) {
if (_tcschr(CharSet->Buffer, *pch)) {
//
// got the char
//
if (NonInclusivePrefixLength) {
*NonInclusivePrefixLength = (USHORT)(pch - StringToSearch->Buffer) * sizeof(WCHAR);
}
return STATUS_SUCCESS;
}
pch--;
}
//
// We haven't found it. Return failure.
//
return STATUS_NOT_FOUND;
}
//
// Database list entry
// Used to represent a particular installed database
//
typedef struct tagSDBLISTENTRY {
LIST_ENTRY ListEntry; // link list stuff
ULONGLONG ullTimeStamp; // database install timestamp
GUID guidDB; // database guid
WCHAR szTimeStamp[32]; // time stamp in string form
WCHAR szGuidDB[64]; // guid in string form
WCHAR szDatabasePath[1]; // database path - we store only the name
} SDBLISTENTRY, *PSDBLISTENTRY;
/*++
AddSdbListEntry
Adds a particular database to the list of installed sdbs (maintained internally)
parses database path to retrieve database name
[in out] pHeadList - pointer to the associated list head for the installed sdbs
[in] guidDB - database guid
[in] TimeStamp - database time stamp
[in] pszDatabasePath - final database path
returns true if success
--*/
BOOL
AddSdbListEntry(
PLIST_ENTRY pHeadList,
GUID& guidDB,
ULONGLONG& TimeStamp,
LPCWSTR pszDatabasePath
)
{
//
// out of database path, recover the database name
//
UNICODE_STRING ustrPath = { 0 };
USHORT uPrefix;
UNICODE_STRING ustrPathSep = RTL_CONSTANT_STRING(L"\\/");
NTSTATUS Status;
UNICODE_STRING ustrGUID = { 0 };
if (pszDatabasePath != NULL) {
RtlInitUnicodeString(&ustrPath, pszDatabasePath);
Status = FindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
&ustrPath,
&ustrPathSep,
&uPrefix);
if (NT_SUCCESS(Status) && (uPrefix + sizeof(WCHAR)) < ustrPath.Length) {
//
// uPrefix is number of character preceding the one we found not including it
//
ustrPath.Buffer += uPrefix / sizeof(WCHAR) + 1;
ustrPath.Length -= (uPrefix + sizeof(WCHAR));
ustrPath.MaximumLength -= (uPrefix + sizeof(WCHAR));
}
//
// at this point ustrPath has just the filename -- this is what we shall use
//
}
PBYTE Buffer = new BYTE[sizeof(SDBLISTENTRY) + ustrPath.Length];
if (Buffer == NULL) {
vLogMessage("[AddSdbListEntry] Failed to allocate 0x%lx bytes\n",
sizeof(SDBLISTENTRY) + ustrPath.Length);
return FALSE;
}
PSDBLISTENTRY pSdbEntry = (PSDBLISTENTRY)Buffer;
pSdbEntry->guidDB = guidDB;
pSdbEntry->ullTimeStamp = TimeStamp;
Status = RtlStringFromGUID(guidDB, &ustrGUID);
if (!NT_SUCCESS(Status)) {
//
// we can't convert guid to string? memory allocation failure
//
vLogMessage("[AddSdbListEntry] Failed to convert guid to string Status 0x%lx\n",
Status);
delete[] Buffer;
return FALSE;
}
RtlCopyMemory(&pSdbEntry->szGuidDB[0], &ustrGUID.Buffer[0], ustrGUID.Length);
pSdbEntry->szGuidDB[ustrGUID.Length/sizeof(WCHAR)] = L'\0';
RtlFreeUnicodeString(&ustrGUID);
wsprintfW(pSdbEntry->szTimeStamp, L"%.16I64X", TimeStamp);
RtlCopyMemory(&pSdbEntry->szDatabasePath[0], &ustrPath.Buffer[0], ustrPath.Length);
pSdbEntry->szDatabasePath[ustrPath.Length / sizeof(WCHAR)] = L'\0';
InsertHeadList(pHeadList, &pSdbEntry->ListEntry);
return TRUE;
}
//
// only pGuidDB OR pwszGuid is allowed
//
/*++
FindSdbListEntry
Finds and returns an sdb list entry given a guid (in string or binary form)
Whenever possible pwszGuid is used (if it's supplied). If pwszGuid happens to be
an arbitrary filename -- it is assumed that it's the name of an installed sdb file
as registered.
[in] pHeadList - list of the installed sdbs
[in] pwszGuid - guid or guid.sdb
[out] ppSdbListEntry - if found, this receives a pointer to sdb list entry
[in] pGuidDB - guid in binary form
returns true if matching database has been located in the list
--*/
BOOL
FindSdbListEntry(
PLIST_ENTRY pHeadList,
LPCWSTR pwszGuid, // guid, possibly with trailing '.sdb'
PSDBLISTENTRY* ppSdbListEntry,
GUID* pGuidDB // guid
)
{
UNICODE_STRING ustrDot = RTL_CONSTANT_STRING(L".");
UNICODE_STRING ustrPath;
USHORT uPrefix;
NTSTATUS Status;
PLIST_ENTRY pEntry;
PSDBLISTENTRY pSdbEntry;
GUID guidDB;
BOOL bGuidSearch = TRUE;
BOOL bFound = FALSE;
LPCWSTR pch;
if (pGuidDB == NULL) {
RtlInitUnicodeString(&ustrPath, pwszGuid);
Status = FindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
&ustrPath,
&ustrDot,
&uPrefix);
if (NT_SUCCESS(Status)) {
//
// uPrefix is number of character preceding the one we found not including it
//
ustrPath.Length = uPrefix;
}
//
// convert to guid, but check first
//
pch = pwszGuid + wcsspn(pwszGuid, L" \t");
if (*pch != L'{') { // not a guid, why convert ?
bGuidSearch = FALSE;
} else {
Status = RtlGUIDFromString(&ustrPath, &guidDB);
if (!NT_SUCCESS(Status)) {
//
// failed, so use database path instead
//
bGuidSearch = FALSE;
}
}
} else {
guidDB = *pGuidDB; // guid search only
}
pEntry = pHeadList->Flink;
while (pEntry != pHeadList && !bFound) {
//
// convert entry by subtracting the offset of the list entry
//
pSdbEntry = (PSDBLISTENTRY)((PBYTE)pEntry - OFFSETOF(SDBLISTENTRY, ListEntry));
//
// compare db guids or paths
//
if (bGuidSearch) {
bFound = RtlEqualMemory(&pSdbEntry->guidDB, &guidDB, sizeof(GUID));
} else {
bFound = !_wcsicmp(pSdbEntry->szDatabasePath, pwszGuid);
}
pEntry = pEntry->Flink;
}
//
// we have found an entry ? return it -- note that pEntry would have advanced while pSdbEntry
// still points to the entry we have found
//
if (bFound) {
*ppSdbListEntry = pSdbEntry;
}
return bFound;
}
/*++
CleanupSdbList
Performs cleanup for the installed sdb list
returns nothing
--*/
VOID
CleanupSdbList(
PLIST_ENTRY pSdbListHead
)
{
PLIST_ENTRY pEntry;
PSDBLISTENTRY pSdbEntry;
PBYTE Buffer;
pEntry = pSdbListHead->Flink;
if (pEntry == NULL) {
return;
}
while (pEntry != pSdbListHead) {
pSdbEntry = (PSDBLISTENTRY)((PBYTE)pEntry - OFFSETOF(SDBLISTENTRY, ListEntry));
pEntry = pEntry->Flink;
Buffer = (PBYTE)pSdbEntry;
delete[] Buffer;
}
}
/*++
ConvertInstalledSdbsToNewFormat
Converts installed sdbs to new format, which involves storing (or verifying) the
timestamp for each installed sdb file. This function also builds a list of sdbs
used elsewhere
[in] hKey - a key handle for hklm/..../InstalledSdb
[in out] pSdbListHead - list head for the installed sdbs
returns true if successful
--*/
BOOL
ConvertInstalledSdbsToNewFormat(
HKEY hKey, // hklm/.../InstalledSdb
PLIST_ENTRY pSdbListHead // we fill this list with our sdbs for later
)
{
DWORD dwIndex = 0;
WCHAR szSubKeyName[MAX_PATH];
PWCHAR pwszKeyName;
DWORD dwBufferSize;
FILETIME ftLastWriteTime;
HKEY hKeyEntry = NULL;
LONG lResult;
ULARGE_INTEGER liTimeStamp;
UNICODE_STRING ustrGuid;
GUID guidDB;
NTSTATUS Status;
WCHAR szDatabasePath[MAX_PATH];
PWCHAR pszDatabasePath;
DWORD dwType;
BOOL bSuccess = TRUE;
while (TRUE) {
dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
lResult = RegEnumKeyExW(hKey,
dwIndex,
szSubKeyName,
&dwBufferSize,
NULL, NULL, NULL,
&ftLastWriteTime);
++dwIndex;
if (lResult != ERROR_SUCCESS) {
//
// done if no more keys, else some sort of error
// bugbug
//
if (lResult == ERROR_NO_MORE_ITEMS) {
//
// we are done, clean
//
break;
}
//
// this is unexpected
//
vLogMessage("[ConvertInstalledSdbsToNewFormat] RegEnumKeyExW for index 0x%lx returned unexpected error 0x%lx\n",
dwIndex, lResult);
break;
}
RtlInitUnicodeString(&ustrGuid, szSubKeyName);
Status = RtlGUIDFromString(&ustrGuid, &guidDB);
if (!NT_SUCCESS(Status)) {
//
// BUGBUG - failed to convert the guid (subkey name!)
// extraneous entry, log warning
//
vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to convert string to guid for \"%ls\" status 0x%lx\n",
szSubKeyName, Status);
continue;
}
//
// for this db entry we have to set the timestamp
//
lResult = RegOpenKeyExW(hKey,
szSubKeyName,
0,
KEY_READ|KEY_WRITE|GetWow64Flag(),
&hKeyEntry);
if (lResult != ERROR_SUCCESS) {
//
// bad error ?
// BUGBUG
vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to open subkey \"%ls\" error 0x%lx\n",
szSubKeyName, lResult);
continue;
}
//
// now check the value
//
dwBufferSize = sizeof(liTimeStamp.QuadPart);
lResult = RegQueryValueExW(hKeyEntry,
L"DatabaseInstallTimeStamp",
NULL,
&dwType,
(PBYTE)&liTimeStamp.QuadPart,
&dwBufferSize);
if (lResult != ERROR_SUCCESS || dwType != REG_BINARY) {
//
// we may either have this value already -- if not, set it up now
//
liTimeStamp.LowPart = ftLastWriteTime.dwLowDateTime;
liTimeStamp.HighPart = ftLastWriteTime.dwHighDateTime;
vLogMessage("[Info] Database \"%ls\" receives timestamp \"%.16I64X\"\n",
szSubKeyName, liTimeStamp.QuadPart);
lResult = RegSetValueExW(hKeyEntry,
L"DatabaseInstallTimeStamp",
0,
REG_BINARY,
(PBYTE)&liTimeStamp.QuadPart,
sizeof(liTimeStamp.QuadPart));
if (lResult != ERROR_SUCCESS) {
//
// error, ignore for now
//
vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to set timestamp value for database \"%ls\" value \"%.16I64X\" error 0x%lx\n",
szSubKeyName, liTimeStamp.QuadPart, lResult);
}
}
//
// at this point we have :
// sdb guid (in szSubKeyName)
// time stamp in liTimeStamp
//
//
// query also database path
//
pszDatabasePath = &szDatabasePath[0];
dwBufferSize = sizeof(szDatabasePath);
lResult = RegQueryValueExW(hKeyEntry,
L"DatabasePath",
NULL,
&dwType,
(PBYTE)pszDatabasePath,
&dwBufferSize);
if (lResult != ERROR_SUCCESS || dwType != REG_SZ) {
//
// no database path
// warn basically corrupt database path
//
vLogMessage("[ConvertInstalledSdbsToNewFormat] Failed to query database path for \"%s\" error 0x%lx\n", szSubKeyName, lResult);
pszDatabasePath = NULL;
}
//
// optional check: we can check here whether the sdb file does exist
//
//
// add this sdb to our cache
//
if (!AddSdbListEntry(pSdbListHead, guidDB, liTimeStamp.QuadPart, pszDatabasePath)) {
//
// failed to add list entry - we cannot continue
//
bSuccess = FALSE;
break;
}
RegCloseKey(hKeyEntry);
hKeyEntry = NULL;
}
if (hKeyEntry != NULL) {
RegCloseKey(hKeyEntry);
}
//
// we are done converting entries -- and we have also collected cache of sdb info
//
return bSuccess;
}
//
// this stucture is used to cache values associated with any particular entry (exe)
//
typedef struct tagSDBVALUEENTRY {
LIST_ENTRY ListEntry; // link
PSDBLISTENTRY pSdbEntry; // this entry belongs to this database
WCHAR szValueName[1]; // value name as we got it from registry
} SDBVALUEENTRY, *PSDBVALUEENTRY;
/*++
AddValueEntry
Adds an new link list element to the list of values
[in out] pValueListHead - link list of values
[in] pSdbEntry - pointer to a cached entry from sdb list
[in] pwszValueName - value name as we got it from the db (something like {guid} or {guid}.sdb)
returns true if successful
--*/
BOOL
AddValueEntry(
PLIST_ENTRY pValueListHead,
PSDBLISTENTRY pSdbEntry,
LPCWSTR pwszValueName
)
{
PSDBVALUEENTRY pValueEntry;
PBYTE Buffer;
DWORD dwSize;
dwSize = sizeof(SDBVALUEENTRY) + wcslen(pwszValueName) * sizeof(WCHAR);
Buffer = new BYTE[dwSize];
if (Buffer == NULL) {
//
// out of memory
//
vLogMessage("[AddValueEntry] Failed to allocate buffer for %ls 0x%lx bytes\n",
pwszValueName, dwSize);
return FALSE;
}
pValueEntry = (PSDBVALUEENTRY)Buffer;
pValueEntry->pSdbEntry = pSdbEntry;
wcscpy(pValueEntry->szValueName, pwszValueName);
InsertHeadList(pValueListHead, &pValueEntry->ListEntry);
return TRUE;
}
/*++
WriteEntryValue
Writes value for a particular entry (exe or layer name), deletes old value associated with
this particular database for this exe (or layer)
[in] hKey - handle for an entry (for instance
hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom/Notepad.exe)
[in] pValueEntry - pointer to a value entry element from the value list
[in] bWriteNewFormat - whether we are asked to write new or old format
returns true if successful
--*/
BOOL
WriteEntryValue(
HKEY hKey,
PSDBVALUEENTRY pValueEntry,
BOOL bWriteNewFormat // if true -- write new format else old format
)
{
LONG lResult;
BOOL bSuccess = FALSE;
LPCWSTR pValueName;
if (bWriteNewFormat) {
pValueName = pValueEntry->pSdbEntry->szGuidDB;
lResult = RegSetValueExW(hKey,
pValueName,
0,
REG_QWORD,
(PBYTE)&pValueEntry->pSdbEntry->ullTimeStamp,
sizeof(pValueEntry->pSdbEntry->ullTimeStamp));
if (lResult != ERROR_SUCCESS) {
//
// we can't do this entry ?
//
vLogMessage("[WriteEntryValue] Failed to write qword value \"%ls\"=\"%.16I64X\" error 0x%lx\n",
pValueEntry->pSdbEntry->szGuidDB, pValueEntry->pSdbEntry->ullTimeStamp, lResult);
goto cleanup;
}
//
// nuke old entry
//
} else {
//
// old style format please
//
pValueName = pValueEntry->pSdbEntry->szDatabasePath;
lResult = RegSetValueExW(hKey,
pValueName,
0,
REG_SZ,
(PBYTE)L"",
sizeof(WCHAR));
if (lResult != ERROR_SUCCESS) {
//
// trouble -- error
//
vLogMessage("[WriteEntryValue] Failed to write string value \"%ls\" error 0x%lx\n",
pValueEntry->pSdbEntry->szDatabasePath, lResult);
goto cleanup;
}
}
//
// if we are here -- success, check to see if we can delete the old value
//
if (_wcsicmp(pValueEntry->szValueName, pValueName) != 0) {
lResult = RegDeleteValueW(hKey, pValueEntry->szValueName);
if (lResult != ERROR_SUCCESS) {
vLogMessage("[WriteEntryValue] Failed to delete value \"%ls\" error 0x%lx\n",
pValueEntry->szValueName, lResult);
}
}
bSuccess = TRUE;
cleanup:
return bSuccess;
}
/*++
ConvertEntryToNewFormat
Converts a particular entry (layer or exe)
[in] hKeyParent - key handle for a parent key (for instance
hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom when
pwszEntryName == "Notepad.exe" or
hklm/Software/Microsoft/Windows NT/CurrentVersion/AppcompatFlags/Custom/Layers when
pwszEntryName == "RunLayer"
[in] pwszEntryName - Either exe name or layer name
[in] pSdbListHead - cached list of installed databases
[in] bNewFormat - whether to use new or old format
returns true if successful
--*/
BOOL
ConvertEntryToNewFormat(
HKEY hKeyParent,
LPCWSTR pwszEntryName,
PLIST_ENTRY pSdbListHead,
BOOL bConvertToNewFormat // true if converting to new format, false if reverting
)
{
LONG lResult;
DWORD dwValues;
DWORD dwMaxValueNameLen;
DWORD dwMaxValueLen;
DWORD dwType;
DWORD dwValueNameSize;
DWORD dwValueSize;
LPWSTR pwszValueName = NULL;
LPBYTE pValue = NULL;
PSDBLISTENTRY pSdbEntry;
DWORD dwIndex;
LIST_ENTRY ValueList = { 0 };
PSDBVALUEENTRY pValueEntry;
PLIST_ENTRY pValueList;
PBYTE Buffer;
BOOL bSuccess = FALSE;
HKEY hKey = NULL;
//
// loop through values, for each value - find sdb and write out new entry
// then delete old entry
//
lResult = RegOpenKeyExW(hKeyParent,
pwszEntryName,
0,
KEY_READ|KEY_WRITE|GetWow64Flag(),
&hKey);
if (lResult != ERROR_SUCCESS) {
vLogMessage("[ConvertEntryToNewFormat] Failed to open key \"%ls\" error 0x%lx\n",
pwszEntryName, lResult);
goto cleanup;
}
lResult = RegQueryInfoKeyW(hKey,
NULL, NULL, // class/class buffer
NULL, // reserved
NULL, NULL, // subkeys/max subkey length
NULL, // max class len
&dwValues, // value count
&dwMaxValueNameLen,
&dwMaxValueLen,
NULL, NULL);
if (lResult != ERROR_SUCCESS) {
//
// failed to query the key, very bad
// bugbug
vLogMessage("[ConvertEntryToNewFormat] Failed to query key information \"%ls\" error 0x%lx\n",
pwszEntryName, lResult);
goto cleanup;
}
//
// allocate buffers
//
pwszValueName = new WCHAR[dwMaxValueNameLen + 1];
pValue = new BYTE[dwMaxValueLen];
if (pValue == NULL || pwszValueName == NULL) {
//
// bugbug
//
vLogMessage("[ConvertEntryToNewFormat] Failed to allocate memory buffer entry \"%ls\" (0x%lx, 0x%lx)\n",
pwszEntryName, dwMaxValueNameLen, dwMaxValueLen);
goto cleanup;
}
InitializeListHead(&ValueList);
//
// we have dwValues -- the count of values
//
for (dwIndex = 0; dwIndex < dwValues; ++dwIndex) {
dwValueNameSize = dwMaxValueNameLen + 1;
dwValueSize = dwMaxValueLen;
lResult = RegEnumValueW(hKey,
dwIndex,
pwszValueName,
&dwValueNameSize,
NULL,
&dwType,
(PBYTE)pValue,
&dwValueSize);
//
// check if we are successful
//
if (lResult != ERROR_SUCCESS) {
if (lResult == ERROR_NO_MORE_ITEMS) {
//
// oops -- we ran out of values!!! Unexpected, but ok
//
vLogMessage("[ConvertEntryToNewFormat] RegEnumValue unexpectedly reports no more items for \"%ls\" index 0x%lx\n",
pwszEntryName, dwIndex);
break;
}
//
// log error and continue
//
vLogMessage("[ConvertEntryToNewFormat] RegEnumValue failed for \"%ls\" index 0x%lx error 0x%lx\n",
pwszEntryName, dwIndex, lResult);
continue;
}
if (bConvertToNewFormat) {
if (dwType != REG_SZ) {
//
// bad entry for sure -- this could be a new entry
// log warning
//
if (dwType == REG_QWORD || (dwType == REG_BINARY && dwValueSize == sizeof(ULONGLONG))) {
//
// new style entry ?
//
if (wcsrchr(pwszValueName, L'.') == NULL &&
*pwszValueName == L'{' &&
*(pwszValueName + wcslen(pwszValueName) - 1) == L'}') {
vLogMessage("[Info] Entry \"%ls\" value \"%ls\" already in new format.\n",
pwszEntryName, pwszValueName);
continue;
}
}
//
// very likely - some entry we do not understand
//
vLogMessage("[ConvertEntryToNewFormat] Bad value type (0x%lx) for entry \"%ls\" value \"%ls\" index 0x%lx\n",
dwType, pwszEntryName, pwszValueName, dwIndex);
continue;
}
//
// search by pwszValueName (which happens to be the GUID.sdb)
// this may be any kind of a string -- not nec. guid
//
if (!FindSdbListEntry(pSdbListHead, pwszValueName, &pSdbEntry, NULL)) {
//
// error - sdb not found!
//
vLogMessage("[ConvertEntryToNewFormat] Failed to find database \"%ls\" for entry \"%ls\" index 0x%lx\n",
pwszValueName, pwszEntryName, dwIndex);
continue;
}
} else {
//
// check the type first, if this is a new style entry - this will be bin
//
if (dwType == REG_SZ &&
wcsrchr(pwszValueName, L'.') != NULL &&
*(LPCWSTR)pValue == L'\0') {
vLogMessage("[Info] Entry \"%ls\" value \"%ls\" is already in required (old) format.\n",
pwszEntryName, pwszValueName);
continue;
}
if (dwType != REG_QWORD &&
(dwType != REG_BINARY || dwValueSize < sizeof(ULONGLONG))) {
//
// error -- we don't know what this entry is, go to the next one
// print warning actually
//
vLogMessage("[ConvertEntryToNewFormat] Bad value type (0x%lx) or size (0x%lx) for entry \"%ls\" value \"%ls\" index 0x%lx\n",
dwType, dwValueSize, pwszEntryName, pwszValueName, dwIndex);
continue;
}
if (!FindSdbListEntry(pSdbListHead, pwszValueName, &pSdbEntry, NULL)) {
//
// we're in trouble -- an entry has no registered database
//
vLogMessage("[ConvertEntryToNewFormat] Failed to find database for value \"%ls\" for entry \"%ls\" index 0x%lx\n",
pwszValueName, pwszEntryName, dwIndex);
continue;
}
}
//
// we have found entry and we're ready to write it out, queue it up
//
if (!AddValueEntry(&ValueList, pSdbEntry, pwszValueName)) {
//
// bugbug can't add value entry
//
vLogMessage("[ConvertEntryToNewFormat] Failed to add value \"%ls\" for entry \"%ls\" index 0x%lx\n",
pwszValueName, pwszEntryName, dwIndex);
goto cleanup;
}
}
//
// we have gone through all the values, write loop
//
bSuccess = TRUE;
pValueList = ValueList.Flink;
while (pValueList != &ValueList) {
pValueEntry = (PSDBVALUEENTRY)((PBYTE)pValueList - OFFSETOF(SDBVALUEENTRY, ListEntry));
//
// we can point to the next entry now
//
if (!WriteEntryValue(hKey, pValueEntry, bConvertToNewFormat)) {
//
// error, can't convert entry
// continue though so that we cleanout the list
vLogMessage("[ConvertEntryToNewFormat] Failed to write value for entry \"%ls\"\n",
pwszEntryName);
}
pValueList = pValueList->Flink;
}
cleanup:
if (ValueList.Flink) {
pValueList = ValueList.Flink;
while (pValueList != &ValueList) {
Buffer = (PBYTE)pValueList - OFFSETOF(SDBVALUEENTRY, ListEntry);
pValueList = pValueList->Flink;
delete[] Buffer;
}
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (pwszValueName != NULL) {
delete[] pwszValueName;
}
if (pValue != NULL) {
delete[] pValue;
}
return bSuccess;
}
/*++
ConvertFormat
This function handles format conversions
[in] bConvertToNewFormat - true if conversion old->new, false otherwise
returns true if success
--*/
BOOL
ConvertFormat(
BOOL bConvertToNewFormat
)
{
LIST_ENTRY SdbList = { 0 }; // installed sdbs cache
HKEY hKey;
LONG lResult;
DWORD dwIndex;
WCHAR szSubKeyName[MAX_PATH];
DWORD dwBufferSize;
WCHAR szKeyPath[MAX_PATH];
BOOL bSuccess = FALSE;
//
// first convert installed sdbs
// open installed sdb key
//
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
APPCOMPAT_KEY_PATH_INSTALLEDSDB_W, // path to InstalledSDB
0,
KEY_READ|KEY_WRITE|GetWow64Flag(),
&hKey);
if (lResult != ERROR_SUCCESS) {
//
// perhaps no dbs are installed ?
//
if (lResult == ERROR_FILE_NOT_FOUND) {
//
// no installed sdbs -- no problem
//
vLogMessage("[ConvertFormat] No Installed sdbs found\n");
return TRUE;
}
//
// some sort of error has occured
//
vLogMessage("[ConvertFormat] Failed to open key \"%ls\" Error 0x%lx\n",
APPCOMPAT_KEY_PATH_INSTALLEDSDB_W, lResult);
return FALSE;
}
//
// note that ConvertInstalledSdbsToNewFormat works properly for both install and uninstall cases
//
InitializeListHead(&SdbList);
if (!ConvertInstalledSdbsToNewFormat(hKey, &SdbList)) {
goto cleanup;
}
// done with Installed sdbs
RegCloseKey(hKey);
hKey = NULL;
//
// next up is entry conversion -- first enum exes, then layers
//
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
APPCOMPAT_KEY_PATH_CUSTOM_W,
0,
KEY_READ|KEY_WRITE|GetWow64Flag(),
&hKey);
if (lResult != ERROR_SUCCESS) {
//
// what is this?
//
if (lResult == ERROR_FILE_NOT_FOUND && !IsListEmpty(&SdbList)) {
vLogMessage("[ConvertFormat] Failed to open \"%ls\" - check consistency\n",
APPCOMPAT_KEY_PATH_CUSTOM_W);
} else {
vLogMessage("[ConvertFormat] Failed to open \"%ls\" error 0x%lx\n",
APPCOMPAT_KEY_PATH_CUSTOM_W, lResult);
}
goto cleanup;
}
dwIndex = 0;
while (TRUE) {
dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
lResult = RegEnumKeyExW(hKey,
dwIndex,
szSubKeyName,
&dwBufferSize,
NULL, NULL, NULL,
NULL);
++dwIndex;
if (lResult != ERROR_SUCCESS) {
if (lResult == ERROR_NO_MORE_ITEMS) {
break;
}
//
// some sort of error, log and continue
//
vLogMessage("[ConvertFormat] RegEnumKey (entries) returned error for index 0x%lx error 0x%lx\n",
dwIndex, lResult);
break;
}
//
// skip layers for now
//
if (!_wcsicmp(szSubKeyName, L"Layers")) {
continue;
}
// for each of these -- call fixup function
if (!ConvertEntryToNewFormat(hKey, szSubKeyName, &SdbList, bConvertToNewFormat)) {
vLogMessage("[ConvertFormat] Failed to convert entry \"%ls\"\n", szSubKeyName);
}
}
RegCloseKey(hKey);
hKey = NULL;
//
// next up - layers
//
wcscpy(szKeyPath, APPCOMPAT_KEY_PATH_CUSTOM_W);
wcscat(szKeyPath, L"\\Layers");
//
// open and enum layers
//
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
szKeyPath,
0,
KEY_READ|KEY_WRITE|GetWow64Flag(),
&hKey);
if (lResult != ERROR_SUCCESS) {
// maybe dead ?
if (lResult == ERROR_FILE_NOT_FOUND) {
//
// it's ok, maybe we have none of those ?
//
vLogMessage("[ConvertFormat] No layers found\n");
goto ConvertComplete;
}
vLogMessage("[ConvertFormat] Failed to open \"%ls\" error 0x%lx\n", szKeyPath, lResult);
goto cleanup;
}
dwIndex = 0;
while (TRUE) {
dwBufferSize = sizeof(szSubKeyName)/sizeof(szSubKeyName[0]);
lResult = RegEnumKeyExW(hKey,
dwIndex,
szSubKeyName,
&dwBufferSize,
NULL, NULL, NULL,
NULL);
++dwIndex;
if (lResult != ERROR_SUCCESS) {
// check if this was the last entry
if (lResult == ERROR_NO_MORE_ITEMS) {
// clean break
break;
}
// some sort of error, log and continue
vLogMessage("[ConvertFormat] RegEnumKey (layers) returned error for index 0x%lx error 0x%lx\n",
dwIndex, lResult);
break;
}
// for each of these -- call fixup function
if (!ConvertEntryToNewFormat(hKey, szSubKeyName, &SdbList, bConvertToNewFormat)) {
vLogMessage("[ConvertFormat] Failed to convert entry \"%ls\"\n", szSubKeyName);
}
}
RegCloseKey(hKey);
hKey = NULL;
ConvertComplete:
bSuccess = TRUE;
cleanup:
if (hKey != NULL) {
RegCloseKey(hKey);
}
//
// free SdbList
//
CleanupSdbList(&SdbList);
return bSuccess;
}
BOOL
ProcessMSIPackages(
PDB pdb,
TAGID tiDatabase,
LPCWSTR pszGuidDB,
ULONGLONG ullSdbTimeStamp,
INSTALL_MODE eMode)
{
TAGID tiMsiPackage;
TAGID tiMsiPackageID;
GUID* pGuidID;
WCHAR szRegPath[MAX_PATH];
BOOL bReturn = TRUE;
WCHAR wszGuid[64];
UNICODE_STRING ustrGuid = { 0 };
tiMsiPackage = SdbFindFirstTag(pdb, tiDatabase, TAG_MSI_PACKAGE);
while (tiMsiPackage && bReturn) {
//
// we have a package, extract/find TAG_MSI_PACKAGE_ID
//
tiMsiPackageID = SdbFindFirstTag(pdb, tiMsiPackage, TAG_MSI_PACKAGE_ID);
if (!tiMsiPackageID) {
vPrintError(IDS_MISSING_PACKAGE_ID);
if (eMode == MODE_CLEANUP) {
goto NextPackage;
} else {
bReturn = FALSE;
break;
}
}
pGuidID = (GUID*)SdbGetBinaryTagData(pdb, tiMsiPackageID);
if (pGuidID == NULL) {
vPrintError(IDS_MISSING_PACKAGE_ID);
if (eMode == MODE_CLEANUP) {
goto NextPackage;
} else {
bReturn = FALSE;
break;
}
}
if (!NT_SUCCESS(RtlStringFromGUID(*pGuidID, &ustrGuid))) {
vPrintError(IDS_GUID_BAD_FORMAT);
bReturn = FALSE;
break;
}
RtlCopyMemory(wszGuid, ustrGuid.Buffer, ustrGuid.Length);
wszGuid[ustrGuid.Length / sizeof(WCHAR)] = TEXT('\0');
if (eMode == MODE_INSTALL) {
bReturn = InstallSdbEntry(wszGuid, pszGuidDB, 0, 0, ullSdbTimeStamp, FALSE);
} else {
bReturn = UninstallSdbEntry(wszGuid, pszGuidDB, ullSdbTimeStamp, FALSE);
}
RtlFreeUnicodeString(&ustrGuid);
NextPackage:
tiMsiPackage = SdbFindNextTag(pdb, tiDatabase, tiMsiPackage);
}
return bReturn;
}
#define MAX_FRIENDLY_NAME_LEN 256
BOOL
bHandleInstall(
WCHAR* wszSdbPath,
INSTALL_MODE eMode,
WCHAR* wszSdbInstallPath
)
{
PDB pdb = NULL;
int i;
WCHAR wszSdbName[MAX_PATH];
WCHAR wszSdbInstallName[MAX_PATH];
HKEY hKey = NULL;
LONG lRes;
TAGID tiDatabase, tiExe, tiLayer;
TAGID tiDBName = TAGID_NULL;
WCHAR* pszDBName = NULL;
WCHAR wszFriendlyName[MAX_FRIENDLY_NAME_LEN];
WCHAR* wszTemp;
GUID guidDB;
NTSTATUS Status;
FILETIME SystemTime;
BOOL bRet = TRUE;
UNICODE_STRING ustrGUID;
ULARGE_INTEGER TimeStamp = { 0 };
//
// determine the timestamp (for the install case)
//
if (eMode == MODE_INSTALL) {
GetSystemTimeAsFileTime(&SystemTime);
TimeStamp.LowPart = SystemTime.dwLowDateTime;
TimeStamp.HighPart = SystemTime.dwHighDateTime;
}
assert(wszSdbPath && wszSdbInstallPath);
if (!wszSdbPath || !wszSdbInstallPath) {
bRet = FALSE;
goto quickOut;
}
ZeroMemory(wszFriendlyName, sizeof(wszFriendlyName));
//
// get the full path from the file name
//
wszTemp = wszGetFileFromPath(wszSdbPath);
if (!wszTemp) {
vPrintMessage(IDS_UNABLE_TO_GET_FILE);
bRet = FALSE;
goto quickOut;
}
wcscpy(wszSdbName, wszTemp);
if (wcscmp(wszSdbName, L"sysmain.sdb") == 0) {
vPrintError(IDS_CANT_INSTALL_SYS);
bRet = FALSE;
goto quickOut;
}
if (GetFileAttributesW(wszSdbPath) != -1 && bIsAlreadyInstalled(wszSdbPath)) {
if (eMode == MODE_INSTALL) {
//
// they asked us to install, it's installed, so we're done
//
vPrintMessage(IDS_ALREADY_INSTALLED, wszSdbPath);
goto quickOut;
}
} else {
if (eMode == MODE_UNINSTALL) {
//
// they asked us to uninstall, it's not installed, so we're done
//
vPrintMessage(IDS_NOT_INSTALLED, wszSdbPath);
goto quickOut;
}
}
if (eMode == MODE_INSTALL) {
//
// find out what file name we're going to use for installing
//
if (!bFindInstallName(wszSdbPath, wszSdbInstallPath)) {
bRet = FALSE;
goto quickOut;
}
} else if (eMode == MODE_CLEANUP) {
//
// we're cleaning up a bad install, so we need to get the install name from the
// install path
//
wszTemp = wszGetFileFromPath(wszSdbInstallPath);
if (!wszTemp) {
vPrintMessage(IDS_UNABLE_TO_GET_FILE);
bRet = FALSE;
goto quickOut;
}
} else {
//
// we're uninstalling, so the install name is the given name
// and the install path is the given path
//
wcscpy(wszSdbInstallPath, wszSdbPath);
}
//
// try to get the guid for later
//
if (!bGetGuid(wszSdbPath, &guidDB)) {
bRet = FALSE;
goto out;
}
//
// check whether the guid is coopted from one of the known databases
//
if (IsKnownDatabaseGUID(&guidDB)) {
vPrintError(IDS_CANT_INSTALL_SYS);
bRet = FALSE;
goto quickOut;
}
//
// in all cases, install name is the db GUID
//
Status = RtlStringFromGUID(guidDB, &ustrGUID);
if (!NT_SUCCESS(Status)) {
bRet = FALSE;
goto out;
}
RtlCopyMemory(wszSdbInstallName, ustrGUID.Buffer, ustrGUID.Length);
wszSdbInstallName[ustrGUID.Length/sizeof(WCHAR)] = L'\0';
RtlFreeUnicodeString(&ustrGUID);
//
// if we're installing, make sure the root tags are in place
//
if (eMode == MODE_INSTALL) {
lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
APPCOMPAT_KEY_PATH_W,
0,
NULL,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
NULL,
&hKey,
NULL);
if (lRes != ERROR_SUCCESS) {
if (lRes == ERROR_ACCESS_DENIED) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
} else {
vPrintError(IDS_CANT_CREATE_REG_KEY, APPCOMPAT_KEY_PATH_W);
}
bRet = FALSE;
goto out;
}
RegCloseKey(hKey);
hKey = NULL;
lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
APPCOMPAT_KEY_PATH_CUSTOM_W,
0,
NULL,
0,
KEY_ALL_ACCESS|GetWow64Flag(),
NULL,
&hKey,
NULL);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_REG_KEY, APPCOMPAT_KEY_PATH_CUSTOM_W);
bRet = FALSE;
goto out;
}
RegCloseKey(hKey);
hKey = NULL;
}
// Open the DB.
pdb = SdbOpenDatabase(wszSdbPath, DOS_PATH);
if (pdb == NULL) {
vPrintError(IDS_UNABLE_TO_OPEN_FILE, wszSdbPath);
bRet = FALSE;
goto out;
}
tiDatabase = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
if (!tiDatabase) {
vPrintError(IDS_NO_DB_TAG, wszSdbPath);
bRet = FALSE;
goto out;
}
//
// get the friendly name of the database
//
tiDBName = SdbFindFirstTag(pdb, tiDatabase, TAG_NAME);
if (tiDBName) {
pszDBName = SdbGetStringTagPtr(pdb, tiDBName);
}
//
// if we don't find a friendly name, use the SDB file name
//
if (pszDBName) {
wcsncpy(wszFriendlyName, pszDBName, MAX_FRIENDLY_NAME_LEN);
wszFriendlyName[MAX_FRIENDLY_NAME_LEN - 1] = 0;
} else {
wcsncpy(wszFriendlyName, wszSdbName, MAX_FRIENDLY_NAME_LEN);
wszFriendlyName[MAX_FRIENDLY_NAME_LEN - 1] = 0;
}
tiExe = SdbFindFirstTag(pdb, tiDatabase, TAG_EXE);
while (tiExe) {
WCHAR szRegPath[MAX_PATH];
TAGID tiName, tiAction;
WCHAR *szName;
tiName = SdbFindFirstTag(pdb, tiExe, TAG_NAME);
if (!tiName) {
vPrintError(IDS_NO_EXE_NAME);
bRet = FALSE;
if (eMode == MODE_CLEANUP) {
goto nextExe;
} else {
goto quickOut;
}
}
szName = SdbGetStringTagPtr(pdb, tiName);
if (!szName) {
vPrintError(IDS_NO_EXE_NAME_PTR);
bRet = FALSE;
if (eMode == MODE_CLEANUP) {
goto nextExe;
} else {
goto quickOut;
}
}
//
// See if this EXE has an ACTION node. Currently only EXEs shimmed with the LUA
// shims have ACTION nodes.
//
tiAction = SdbFindFirstTag(pdb, tiExe, TAG_ACTION);
if (eMode == MODE_INSTALL) {
if (!InstallSdbEntry(szName, wszSdbInstallName, pdb, tiAction, TimeStamp.QuadPart, FALSE)) {
bRet = FALSE;
goto out;
}
} else {
if (!UninstallSdbEntry(szName, wszSdbInstallName, TimeStamp.QuadPart, FALSE)) {
goto quickOut;
}
}
nextExe:
tiExe = SdbFindNextTag(pdb, tiDatabase, tiExe);
}
//
// Loop through the published layers
//
tiLayer = SdbFindFirstTag(pdb, tiDatabase, TAG_LAYER);
while (tiLayer) {
WCHAR szRegPath[MAX_PATH];
TAGID tiName;
WCHAR* szName;
tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);
if (!tiName) {
vPrintError(IDS_NO_EXE_NAME);
bRet = FALSE;
if (eMode == MODE_CLEANUP) {
goto nextLayer;
} else {
goto quickOut;
}
}
szName = SdbGetStringTagPtr(pdb, tiName);
if (!szName) {
vPrintError(IDS_NO_EXE_NAME_PTR);
bRet = FALSE;
if (eMode == MODE_CLEANUP) {
goto nextLayer;
} else {
goto quickOut;
}
}
if (eMode == MODE_INSTALL) {
if (!InstallSdbEntry(szName, wszSdbInstallName, 0, 0, TimeStamp.QuadPart, TRUE)) {
bRet = FALSE;
goto out;
}
} else {
if (!UninstallSdbEntry(szName, wszSdbInstallName, TimeStamp.QuadPart, TRUE)) {
goto quickOut;
}
}
nextLayer:
tiLayer = SdbFindNextTag(pdb, tiDatabase, tiLayer);
}
if (!ProcessMSIPackages(pdb, tiDatabase, wszSdbInstallName, TimeStamp.QuadPart, eMode)) {
bRet = FALSE;
goto quickOut;
}
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
//
// now that we've handled the registry keys, copy the file
//
if (eMode == MODE_INSTALL) {
//
// ensure the directory exists
//
CreateDirectoryW(g_wszCustom, NULL);
if (!CopyFileW(wszSdbPath, wszSdbInstallPath, TRUE)) {
vPrintError(IDS_CANT_COPY_FILE, wszSdbInstallPath);
bRet = FALSE;
goto out;
}
} else {
//
// ensure that we don't fail because of read-only files
//
SetFileAttributesW(wszSdbInstallPath, FILE_ATTRIBUTE_NORMAL);
if (!DeleteFileW(wszSdbInstallPath)) {
vPrintError(IDS_CANT_DELETE_FILE, wszSdbInstallPath);
bRet = FALSE;
}
}
//
// set up or delete the uninstall registry keys
//
if (eMode == MODE_INSTALL) {
WCHAR wszSDBInstPath[MAX_PATH];
WCHAR wszUninstallPath[MAX_PATH];
WCHAR wszUninstallString[MAX_PATH];
//
// goofball hack required because of crazy redirection strategy on IA64
//
wszSDBInstPath[0] = 0;
GetSystemWindowsDirectoryW(wszSDBInstPath, MAX_PATH);
wcscat(wszSDBInstPath, L"\\SysWow64\\sdbinst.exe");
if (GetFileAttributesW(wszSDBInstPath) == -1) {
//
// there's no SysWow64 directory, so we'll just use system32
//
wszSDBInstPath[0] = 0;
GetSystemWindowsDirectoryW(wszSDBInstPath, MAX_PATH);
wcscat(wszSDBInstPath, L"\\system32\\sdbinst.exe");
}
wcscpy(wszUninstallPath, UNINSTALL_KEY_PATH);
wcscat(wszUninstallPath, wszSdbInstallName);
wcscat(wszUninstallPath, L".sdb");
lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
wszUninstallPath,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS|GetWow64Flag(),
NULL,
&hKey,
NULL);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_REG_KEY, wszUninstallPath);
bRet = FALSE;
goto out;
}
lRes = RegSetValueExW(hKey,
L"DisplayName",
0,
REG_SZ,
(PBYTE)wszFriendlyName,
(wcslen(wszFriendlyName) + 1) * sizeof(WCHAR));
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_VALUE, wszUninstallPath);
bRet = FALSE;
goto out;
}
swprintf(wszUninstallString, L"%s -u \"%s\"", wszSDBInstPath, wszSdbInstallPath);
lRes = RegSetValueExW(hKey, L"UninstallString", 0, REG_SZ,
(PBYTE)wszUninstallString, (wcslen(wszUninstallString) + 1) * sizeof(WCHAR));
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_CREATE_VALUE, wszUninstallPath);
bRet = FALSE;
goto out;
}
RegCloseKey(hKey);
hKey = NULL;
} else {
WCHAR wszUninstallPath[MAX_PATH];
lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, UNINSTALL_KEY_PATH, 0, KEY_ALL_ACCESS|GetWow64Flag(), &hKey);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_OPEN_REG_KEY, UNINSTALL_KEY_PATH);
bRet = FALSE;
goto out;
}
//
// create sdb path name
//
wcscpy(wszUninstallPath, wszSdbInstallName);
wcscat(wszUninstallPath, L".sdb");
lRes = LocalRegDeleteKeyW(hKey, wszUninstallPath);
if (lRes != ERROR_SUCCESS) {
vPrintError(IDS_CANT_DELETE_REG_KEY, wszSdbInstallName, UNINSTALL_KEY_PATH);
}
RegCloseKey(hKey);
hKey = NULL;
}
//
// register or unregister the DB
//
if (eMode == MODE_INSTALL) {
if (!SdbRegisterDatabaseEx(wszSdbInstallPath, SDB_DATABASE_SHIM, &TimeStamp.QuadPart)) {
vPrintError(IDS_CANT_REGISTER_DB, wszFriendlyName);
bRet = FALSE;
goto out;
}
} else {
if (!SdbUnregisterDatabase(&guidDB)) {
vPrintError(IDS_CANT_UNREGISTER_DB, wszFriendlyName);
}
}
if (eMode == MODE_INSTALL) {
vPrintMessage(IDS_INSTALL_COMPLETE, wszFriendlyName);
} else {
vPrintMessage(IDS_UNINSTALL_COMPLETE, wszFriendlyName);
}
out:
//
// always silently delete the file on uninstall, whether we failed to remove the
// registry entries or not.
//
if (eMode != MODE_INSTALL) {
//
// need to make sure the pdb is closed before deleting it.
//
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
//
// ensure that we don't fail because of read-only files
//
SetFileAttributesW(wszSdbInstallPath, FILE_ATTRIBUTE_NORMAL);
DeleteFileW(wszSdbInstallPath);
}
quickOut:
//
// these cleanup steps are not strictly necessary, as they'll be cleaned up
// on exit anyway. But what the heck.
//
if (pdb) {
SdbCloseDatabase(pdb);
pdb = NULL;
}
if (hKey) {
RegCloseKey(hKey);
hKey = NULL;
}
return bRet;
}
extern "C" int APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nCmdShow
)
{
int i;
int nReturn = 0;
WCHAR wszSdbName[MAX_PATH];
WCHAR wszSdbPath[MAX_PATH];
WCHAR wszSdbInstallPath[MAX_PATH];
WCHAR wszOldSdbPath[MAX_PATH];
TAGID tiDBName = TAGID_NULL;
WCHAR* pszDBName = NULL;
WCHAR wszFriendlyName[256];
WCHAR wszGuid[100];
OSVERSIONINFO osvi;
LPWSTR szCommandLine;
LPWSTR* argv;
int argc;
INSTALL_MODE eMode;
g_hInst = hInstance;
//
// init custom directory
//
g_wszCustom[0] = 0;
GetSystemWindowsDirectoryW(g_wszCustom, MAX_PATH);
#if defined(_WIN64)
wcscat(g_wszCustom, L"\\AppPatch\\Custom\\IA64\\");
#else
wcscat(g_wszCustom, L"\\AppPatch\\Custom\\");
#endif // _WIN64
RtlZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
g_bWin2K = TRUE;
}
//
// Note that this memory isn't freed because it will automatically
// be freed on exit anyway, and there are a lot of exit cases from this application
//
szCommandLine = GetCommandLineW();
argv = CommandLineToArgvW(szCommandLine, &argc);
if (!argv) {
vPrintError(IDS_CANT_GET_ARGS);
return 1;
}
if (argc < 2) {
vPrintHelp(argv[0]);
return 0;
}
g_bQuiet = FALSE;
eMode = MODE_INSTALL;
wszSdbName[0] = 0;
wszGuid[0] = 0;
wszFriendlyName[0] = 0;
for (i = 1; i < argc; ++i) {
if (argv[i][0] == L'-' || argv[i][0] == L'/') {
switch (tolower(argv[i][1])) {
case L'?':
vPrintHelp(argv[0]);
return 0;
break;
case L'c':
//
// convert entries to new format
//
eMode = MODE_CONVERT_FORMAT_NEW;
break;
case L'g':
i++;
if (i >= argc) {
vPrintError(IDS_NEED_ARG, argv[i-1]);
vPrintHelp(argv[0]);
return 1;
}
eMode = MODE_UNINSTALL;
wcscpy(wszGuid, argv[i]);
break;
case L'n':
i++;
if (i >= argc) {
vPrintError(IDS_NEED_ARG, argv[i-1]);
vPrintHelp(argv[0]);
return 1;
}
eMode = MODE_UNINSTALL;
wcscpy(wszFriendlyName, argv[i]);
break;
case L'p':
g_bAllowPatches = TRUE;
break;
case L'r':
//
// revert to old format
//
eMode = MODE_CONVERT_FORMAT_OLD;
break;
case L'q':
g_bQuiet = TRUE;
break;
case L'u':
eMode = MODE_UNINSTALL;
break;
default:
vPrintError(IDS_INVALID_SWITCH, argv[i]);
vPrintHelp(argv[0]);
return 1;
}
} else {
if (wszSdbName[0]) {
vPrintError(IDS_TOO_MANY_ARGS);
vPrintHelp(argv[0]);
return 1;
}
wcscpy(wszSdbName, argv[i]);
}
}
//
// check to make sure the user is an administrator
//
if (!bCanRun()) {
if (eMode == MODE_INSTALL) {
vPrintError(IDS_NEED_INSTALL_PERMISSION);
} else {
vPrintError(IDS_NEED_UNINSTALL_PERMISSION);
}
return 1;
}
//
// check if we are running in a special 'setup' mode (converting or reverting the entries)
//
if (eMode == MODE_CONVERT_FORMAT_NEW || eMode == MODE_CONVERT_FORMAT_OLD) {
OpenLogFile();
if (!ConvertFormat(eMode == MODE_CONVERT_FORMAT_NEW)) {
nReturn = 1;
}
CloseLogFile();
return nReturn;
}
if (eMode == MODE_INSTALL && !wszSdbName[0]) {
vPrintError(IDS_MUST_SPECIFY_SDB);
vPrintHelp(argv[0]);
return 1;
}
if (eMode == MODE_UNINSTALL && !wszSdbName[0] && !wszGuid[0] && !wszFriendlyName[0]) {
vPrintError(IDS_MUST_SPECIFY_SDB);
vPrintHelp(argv[0]);
return 1;
}
if (wszSdbName[0]) {
if (wszSdbName[1] == L':' || wszSdbName[1] == L'\\') {
//
// this is a full path name, so just copy it
//
wcscpy(wszSdbPath, wszSdbName);
} else {
DWORD dwRet;
//
// this is a relative path name, so get the full one
//
if (!_wfullpath(wszSdbPath, wszSdbName, MAX_PATH)) {
vPrintError(IDS_CANT_GET_FULL_PATH);
return 1;
}
}
}
//
// First, get the real file name from other params, if necessary
//
if (eMode == MODE_UNINSTALL) {
if (wszGuid[0]) {
DWORD dwLen = wcslen(wszGuid);
if (dwLen != 38 || wszGuid[0] != L'{' || wszGuid[dwLen - 1] != L'}' ||
wszGuid[9] != L'-' || wszGuid[14] != L'-' || wszGuid[19] != L'-' ||
wszGuid[24] != L'-') {
vPrintError(IDS_GUID_BAD_FORMAT);
return 1;
}
wcscpy(wszSdbName, wszGuid);
wcscat(wszSdbName, L".sdb");
wcscpy(wszSdbPath, g_wszCustom);
wcscat(wszSdbPath, wszSdbName);
} else if (wszFriendlyName[0]) {
if (!bFriendlyNameToFile(wszFriendlyName, wszSdbName, wszSdbPath)) {
vPrintError(IDS_NO_FRIENDLY_NAME, wszFriendlyName);
return 1;
}
} else {
if (!bIsAlreadyInstalled(wszSdbPath)) {
WCHAR wszSdbPathTemp[MAX_PATH];
//
// they're not giving us an installed file, so get the GUID and convert to a file
//
if (!bFindInstallName(wszSdbPath, wszSdbPathTemp)) {
return 1;
}
wcscpy(wszSdbName, wszSdbPathTemp); // name and path are the same
wcscpy(wszSdbPath, wszSdbPathTemp);
}
}
}
if (eMode == MODE_INSTALL &&
GetFileAttributesW(wszSdbPath) != -1 &&
bIsAlreadyInstalled(wszSdbPath)) {
//
// they asked us to install, it's installed, so we're done
//
vPrintMessage(IDS_ALREADY_INSTALLED, wszSdbPath);
goto quickOut;
}
if (eMode == MODE_UNINSTALL && GetFileAttributesW(wszSdbPath) == -1) {
//
// they asked us to uninstall, it's not installed, so we're done
//
vPrintMessage(IDS_NOT_INSTALLED, wszSdbName);
goto quickOut;
}
if (eMode == MODE_INSTALL && DatabaseContainsPatch(wszSdbPath) && !g_bAllowPatches) {
//
// we can't install because the SDB contains a patch and the user hasn't authorized it.
//
// djm - can't use new text in XP SP1, so we'll print a generic error
//
//vPrintMessage(IDS_NO_PATCHES_ALLOWED);
vPrintMessage(IDS_APP_ERROR_TITLE);
goto quickOut;
}
if (eMode == MODE_INSTALL && bOldSdbInstalled(wszSdbPath, wszOldSdbPath)) {
//
// we should ask if we're going to uninstall the old one,
// unless we're in quiet mode.
//
int nRet;
WCHAR wszCaption[1024];
WCHAR wszText[1024];
if (g_bQuiet) {
nRet = IDYES;
} else {
if (!LoadStringW(g_hInst, IDS_APP_TITLE, wszCaption, 1024)) {
return 1;
}
if (!LoadStringW(g_hInst, IDS_FOUND_SAME_ID, wszText, 1024)) {
return 1;
}
nRet = MessageBoxW(NULL,
wszText,
wszCaption,
MB_YESNO | MB_ICONQUESTION);
}
if (nRet == IDNO) {
return 0;
} else if (nRet == IDYES) {
if (!bHandleInstall(wszOldSdbPath, MODE_UNINSTALL, wszSdbInstallPath)) {
vPrintError(IDS_FAILED_UNINSTALL);
return 1;
}
}
}
wszSdbInstallPath[0] = 0;
if (!bHandleInstall(wszSdbPath, eMode, wszSdbInstallPath)) {
if (eMode == MODE_INSTALL) {
//
// we need to clean up; the install failed.
//
g_bQuiet = TRUE;
bHandleInstall(wszSdbPath, MODE_CLEANUP, wszSdbInstallPath);
}
nReturn = 1;
}
//
// no matter what happens, flush the cache
//
vFlushCache();
quickOut:
return nReturn;
}