reactos/dll/appcompat/apphelp/apphelp.c

477 lines
14 KiB
C

/*
* 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.
*/
BOOL
WINAPI
ApphelpCheckRunAppEx(
_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");
SdbFreeDatabaseInformation(&Information);
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);
}
SdbFreeDatabaseInformation(&Information);
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();
}