Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476/* * PROJECT: ReactOS Application compatibility module * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: apphelp entrypoint / generic interface functions * COPYRIGHT: Copyright 2011 André Hentschel * Copyright 2013 Mislav Blaževic * Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org) */
#define WIN32_NO_STATUS#include "windef.h"#include "winbase.h"#include "winreg.h"#include "strsafe.h"#include "apphelp.h"#include <ndk/rtlfuncs.h>#include <ndk/cmfuncs.h>#include <ndk/obfuncs.h>#include <ndk/kdtypes.h>
ACCESS_MASK Wow64QueryFlag(void);
const UNICODE_STRING InstalledSDBKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB");
/* from dpfilter.h */#define DPFLTR_APPCOMPAT_ID 123
#define MAX_GUID_STRING_LEN RTL_NUMBER_OF("{12345678-1234-1234-0123-456789abcdef}")C_ASSERT(MAX_GUID_STRING_LEN == 39); // See psdk/cfgmgr32.h
#ifndef NT_SUCCESS#define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0)#endif
ULONG g_ShimDebugLevel = ~0;HMODULE g_hInstance;
void ApphelppInitDebugLevel(void){ static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL"); UNICODE_STRING DebugValue; NTSTATUS Status; ULONG NewLevel = SHIM_ERR; WCHAR Buffer[40];
RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
if (NT_SUCCESS(Status)) { if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel))) NewLevel = SHIM_ERR; } g_ShimDebugLevel = NewLevel;}
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ){ switch (reason) { case DLL_PROCESS_ATTACH: g_hInstance = hinst; DisableThreadLibraryCalls(hinst); SdbpHeapInit(); break; case DLL_PROCESS_DETACH: SdbpHeapDeinit(); break; } return TRUE;}
BOOL WINAPI ApphelpCheckInstallShieldPackage(void* ptr, LPCWSTR path){ SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr, path); return TRUE;}
BOOL WINAPI ApphelpCheckShellObject(REFCLSID ObjectCLSID, BOOL bShimIfNecessary, ULONGLONG *pullFlags){ WCHAR GuidString[MAX_GUID_STRING_LEN]; if (!ObjectCLSID || !SdbGUIDToString(ObjectCLSID, GuidString, RTL_NUMBER_OF(GuidString))) GuidString[0] = L'\0'; SHIM_WARN("stub: ObjectCLSID='%S', bShimIfNecessary=%d, pullFlags=%p)\n", GuidString, bShimIfNecessary, pullFlags);
if (pullFlags) *pullFlags = 0;
return TRUE;}
/** * Outputs diagnostic info. * * @param [in] Level The level to log this message with, choose any of [SHIM_ERR, * SHIM_WARN, SHIM_INFO]. * @param [in] FunctionName The function this log should be attributed to. * @param [in] Format The format string. * @param ... Variable arguments providing additional information. * * @return Success: TRUE Failure: FALSE. */BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...){ char Buffer[512]; va_list ArgList; char* Current = Buffer; const char* LevelStr; size_t Length = sizeof(Buffer);
if (g_ShimDebugLevel == ~0) ApphelppInitDebugLevel();
if (Level > g_ShimDebugLevel) return FALSE;
switch (Level) { case SHIM_ERR: LevelStr = "Err "; Level = DPFLTR_MASK | (1 << DPFLTR_ERROR_LEVEL); break; case SHIM_WARN: LevelStr = "Warn"; Level = DPFLTR_MASK | (1 << DPFLTR_WARNING_LEVEL); break; case SHIM_INFO: LevelStr = "Info"; Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL); break; default: LevelStr = "User"; Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL); break; } StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName);
va_start(ArgList, Format); StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList); va_end(ArgList);
#if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer));#else DbgPrint("%s", Buffer); return TRUE;#endif}
#define APPHELP_DONTWRITE_REASON 2#define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */#define APPHELP_IGNORE_ENVIRONMENT 0x400
#define APPHELP_VALID_RESULT 0x10000#define APPHELP_RESULT_NOTFOUND 0x20000#define APPHELP_RESULT_FOUND 0x40000
/** * Lookup Shims / Fixes for the specified application * * @param [in] FileHandle Handle to the file to check. * @param [in] Unk1 * @param [in] Unk2 * @param [in] ApplicationName Exe to check * @param [in] Environment The environment variables to use, or NULL to use the current environment. * @param [in] ExeType Exe type (MACHINE_TYPE_XXXX) * @param [in,out] Reason Input/output flags * @param [in] SdbQueryAppCompatData The resulting data. * @param [in] SdbQueryAppCompatDataSize The resulting data size. * @param [in] SxsData TODO * @param [in] SxsDataSize TODO * @param [in] FusionFlags TODO * @param [in] SomeFlag1 TODO * @param [in] SomeFlag2 TODO * * @return TRUE if the application is allowed to run. */BOOLWINAPIApphelpCheckRunAppEx( _In_ HANDLE FileHandle, _In_opt_ PVOID Unk1, _In_opt_ PVOID Unk2, _In_opt_z_ PCWSTR ApplicationName, _In_opt_ PVOID Environment, _In_opt_ USHORT ExeType, _Inout_opt_ PULONG Reason, _Out_opt_ PVOID* SdbQueryAppCompatData, _Out_opt_ PULONG SdbQueryAppCompatDataSize, _Out_opt_ PVOID* SxsData, _Out_opt_ PULONG SxsDataSize, _Out_opt_ PULONG FusionFlags, _Out_opt_ PULONG64 SomeFlag1, _Out_opt_ PULONG SomeFlag2){ SDBQUERYRESULT* result = NULL; HSDB hsdb = NULL; DWORD dwFlags = 0;
if (SxsData) *SxsData = NULL; if (SxsDataSize) *SxsDataSize = 0; if (FusionFlags) *FusionFlags = 0; if (SomeFlag1) *SomeFlag1 = 0; if (SomeFlag2) *SomeFlag2 = 0; if (Reason) dwFlags = *Reason;
dwFlags &= ~APPHELP_CLEARBITS;
*SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT)); if (SdbQueryAppCompatDataSize) *SdbQueryAppCompatDataSize = sizeof(*result);
hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL); if (hsdb) { BOOL FoundMatch; DWORD MatchingExeFlags = 0;
if (dwFlags & APPHELP_IGNORE_ENVIRONMENT) MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT;
FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result); if (FileHandle != INVALID_HANDLE_VALUE) { dwFlags |= APPHELP_VALID_RESULT; dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND); }
SdbReleaseDatabase(hsdb); }
if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON)) *Reason = dwFlags;
/* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */ return TRUE;}
/** * @name SdbRegisterDatabaseEx * Register an application compatibility database * * @param pszDatabasePath The database. Required * @param dwDatabaseType The database type. SDB_DATABASE_* * @param pTimeStamp The timestamp. When this argument is not provided, the system time is used. * @return TRUE on success, or FALSE on failure. */BOOL WINAPI SdbRegisterDatabaseEx( _In_ LPCWSTR pszDatabasePath, _In_ DWORD dwDatabaseType, _In_opt_ const PULONGLONG pTimeStamp){ PDB pdb; DB_INFORMATION Information; WCHAR GuidBuffer[MAX_GUID_STRING_LEN]; UNICODE_STRING KeyName; ACCESS_MASK KeyAccess; OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE); NTSTATUS Status; HANDLE InstalledSDBKey;
pdb = SdbOpenDatabase(pszDatabasePath, DOS_PATH); if (!pdb) { SHIM_ERR("Unable to open DB %S\n", pszDatabasePath); return FALSE; }
if (!SdbGetDatabaseInformation(pdb, &Information) || !(Information.dwFlags & DB_INFO_FLAGS_VALID_GUID)) { SHIM_ERR("Unable to retrieve DB info\n"); SdbCloseDatabase(pdb); return FALSE; }
if (!SdbGUIDToString(&Information.Id, GuidBuffer, RTL_NUMBER_OF(GuidBuffer))) { SHIM_ERR("Unable to Convert GUID to string\n"); SdbCloseDatabase(pdb); return FALSE; }
KeyName = InstalledSDBKeyName; KeyAccess = Wow64QueryFlag() | KEY_WRITE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS; Status = NtCreateKey(&InstalledSDBKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL); if (NT_SUCCESS(Status)) { HANDLE DbKey;
RtlInitUnicodeString(&KeyName, GuidBuffer); ObjectKey.RootDirectory = InstalledSDBKey;
Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL); if (NT_SUCCESS(Status)) { UNICODE_STRING DatabasePathKey = RTL_CONSTANT_STRING(L"DatabasePath"); UNICODE_STRING DatabaseInstallTimeStampKey = RTL_CONSTANT_STRING(L"DatabaseInstallTimeStamp"); UNICODE_STRING DatabaseTypeKey = RTL_CONSTANT_STRING(L"DatabaseType"); UNICODE_STRING DatabaseDescriptionKey = RTL_CONSTANT_STRING(L"DatabaseDescription");
Status = NtSetValueKey(DbKey, &DatabasePathKey, 0, REG_SZ, (PVOID)pszDatabasePath, ((ULONG)wcslen(pszDatabasePath) + 1) * sizeof(WCHAR)); if (!NT_SUCCESS(Status)) SHIM_ERR("Unable to write %wZ\n", &DatabasePathKey);
if (NT_SUCCESS(Status)) { ULARGE_INTEGER ulTimeStamp; if (pTimeStamp) { ulTimeStamp.QuadPart = *pTimeStamp; } else { FILETIME fi; GetSystemTimeAsFileTime(&fi); ulTimeStamp.LowPart = fi.dwLowDateTime; ulTimeStamp.HighPart = fi.dwHighDateTime; } Status = NtSetValueKey(DbKey, &DatabaseInstallTimeStampKey, 0, REG_QWORD, &ulTimeStamp.QuadPart, sizeof(ulTimeStamp)); if (!NT_SUCCESS(Status)) SHIM_ERR("Unable to write %wZ\n", &DatabaseInstallTimeStampKey); }
if (NT_SUCCESS(Status)) { Status = NtSetValueKey(DbKey, &DatabaseTypeKey, 0, REG_DWORD, &dwDatabaseType, sizeof(dwDatabaseType)); if (!NT_SUCCESS(Status)) SHIM_ERR("Unable to write %wZ\n", &DatabaseTypeKey); }
if (NT_SUCCESS(Status) && Information.Description) { Status = NtSetValueKey(DbKey, &DatabaseDescriptionKey, 0, REG_SZ, (PVOID)Information.Description, ((ULONG)wcslen(Information.Description) + 1) * sizeof(WCHAR)); if (!NT_SUCCESS(Status)) SHIM_ERR("Unable to write %wZ\n", &DatabaseDescriptionKey); }
NtClose(DbKey);
if (NT_SUCCESS(Status)) { SHIM_INFO("Installed %wS as %wZ\n", pszDatabasePath, &KeyName); } } else { SHIM_ERR("Unable to create key %wZ\n", &KeyName); }
NtClose(InstalledSDBKey); } else { SHIM_ERR("Unable to create key %wZ\n", &KeyName); }
SdbCloseDatabase(pdb);
return NT_SUCCESS(Status);}
/** * @name SdbRegisterDatabase * Register an application compatibility database * * @param pszDatabasePath The database. Required * @param dwDatabaseType The database type. SDB_DATABASE_* * @return TRUE on success, or FALSE on failure. */BOOL WINAPI SdbRegisterDatabase( _In_ LPCWSTR pszDatabasePath, _In_ DWORD dwDatabaseType){ return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL);}
/** * @name SdbUnregisterDatabase * * * @param pguidDB * @return */BOOL WINAPI SdbUnregisterDatabase(_In_ const GUID *pguidDB){ WCHAR KeyBuffer[MAX_PATH], GuidBuffer[MAX_GUID_STRING_LEN]; UNICODE_STRING KeyName; ACCESS_MASK KeyAccess; OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE); NTSTATUS Status; HANDLE DbKey;
if (!SdbGUIDToString(pguidDB, GuidBuffer, RTL_NUMBER_OF(GuidBuffer))) { SHIM_ERR("Unable to Convert GUID to string\n"); return FALSE; }
RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer)); RtlAppendUnicodeStringToString(&KeyName, &InstalledSDBKeyName); RtlAppendUnicodeToString(&KeyName, L"\\"); RtlAppendUnicodeToString(&KeyName, GuidBuffer);
KeyAccess = Wow64QueryFlag() | DELETE; Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL); if (!NT_SUCCESS(Status)) { SHIM_ERR("Unable to open %wZ\n", &KeyName); return FALSE; }
Status = NtDeleteKey(DbKey); if (!NT_SUCCESS(Status)) SHIM_ERR("Unable to delete %wZ\n", &KeyName);
NtClose(DbKey); return NT_SUCCESS(Status);}
/* kernel32.dll */BOOL WINAPI BaseDumpAppcompatCache(VOID);BOOL WINAPI BaseFlushAppcompatCache(VOID);
/** * @name ShimDumpCache * Dump contents of the shim cache. * * @param hwnd Unused, pass 0 * @param hInstance Unused, pass 0 * @param lpszCmdLine Unused, pass 0 * @param nCmdShow Unused, pass 0 * @return */BOOL WINAPI ShimDumpCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow){ return BaseDumpAppcompatCache();}
/*** @name ShimFlushCache* Flush the shim cache. Call this after installing a new shim database** @param hwnd Unused, pass 0* @param hInstance Unused, pass 0* @param lpszCmdLine Unused, pass 0* @param nCmdShow Unused, pass 0* @return*/BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow){ return BaseFlushAppcompatCache();}