1 /* 2 * PROJECT: ReactOS Application compatibility module 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: apphelp entrypoint / generic interface functions 5 * COPYRIGHT: Copyright 2011 André Hentschel 6 * Copyright 2013 Mislav Blaževic 7 * Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org) 8 */ 9 10 #define WIN32_NO_STATUS 11 #include "windef.h" 12 #include "winbase.h" 13 #include "winver.h" 14 #include "strsafe.h" 15 #include "apphelp.h" 16 #include "ndk/rtlfuncs.h" 17 #include "ndk/kdtypes.h" 18 19 20 /* from dpfilter.h */ 21 #define DPFLTR_APPCOMPAT_ID 123 22 23 #ifndef NT_SUCCESS 24 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0) 25 #endif 26 27 ULONG g_ShimDebugLevel = 0xffffffff; 28 HMODULE g_hInstance; 29 30 void ApphelppInitDebugLevel(void) 31 { 32 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL"); 33 UNICODE_STRING DebugValue; 34 NTSTATUS Status; 35 ULONG NewLevel = SHIM_ERR; 36 WCHAR Buffer[40]; 37 38 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer)); 39 40 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue); 41 42 if (NT_SUCCESS(Status)) 43 { 44 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel))) 45 NewLevel = SHIM_ERR; 46 } 47 g_ShimDebugLevel = NewLevel; 48 } 49 50 51 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) 52 { 53 switch (reason) 54 { 55 case DLL_PROCESS_ATTACH: 56 g_hInstance = hinst; 57 DisableThreadLibraryCalls( hinst ); 58 SdbpHeapInit(); 59 break; 60 case DLL_PROCESS_DETACH: 61 SdbpHeapDeinit(); 62 break; 63 } 64 return TRUE; 65 } 66 67 BOOL WINAPI ApphelpCheckInstallShieldPackage(void* ptr, LPCWSTR path) 68 { 69 SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr, path); 70 return TRUE; 71 } 72 73 74 BOOL WINAPI ApphelpCheckShellObject(REFCLSID ObjectCLSID, BOOL bShimIfNecessary, ULONGLONG *pullFlags) 75 { 76 WCHAR GuidString[100]; 77 if (!ObjectCLSID || !SdbGUIDToString(ObjectCLSID, GuidString, 100)) 78 GuidString[0] = L'\0'; 79 SHIM_WARN("stub: ObjectCLSID='%S', bShimIfNecessary=%d, pullFlags=%p)\n", GuidString, bShimIfNecessary, pullFlags); 80 81 if (pullFlags) 82 *pullFlags = 0; 83 84 return TRUE; 85 } 86 87 /** 88 * Outputs diagnostic info. 89 * 90 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR, 91 * SHIM_WARN, SHIM_INFO]. 92 * @param [in] FunctionName The function this log should be attributed to. 93 * @param [in] Format The format string. 94 * @param ... Variable arguments providing additional information. 95 * 96 * @return Success: TRUE Failure: FALSE. 97 */ 98 BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...) 99 { 100 char Buffer[512]; 101 va_list ArgList; 102 char* Current = Buffer; 103 const char* LevelStr; 104 size_t Length = sizeof(Buffer); 105 106 if (g_ShimDebugLevel == 0xffffffff) 107 ApphelppInitDebugLevel(); 108 109 if (Level > g_ShimDebugLevel) 110 return FALSE; 111 112 switch (Level) 113 { 114 case SHIM_ERR: 115 LevelStr = "Err "; 116 Level = DPFLTR_MASK | (1 << DPFLTR_ERROR_LEVEL); 117 break; 118 case SHIM_WARN: 119 LevelStr = "Warn"; 120 Level = DPFLTR_MASK | (1 << DPFLTR_WARNING_LEVEL); 121 break; 122 case SHIM_INFO: 123 LevelStr = "Info"; 124 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL); 125 break; 126 default: 127 LevelStr = "User"; 128 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL); 129 break; 130 } 131 StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName); 132 133 va_start(ArgList, Format); 134 StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList); 135 va_end(ArgList); 136 137 #if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX 138 return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer)); 139 #else 140 DbgPrint("%s", Buffer); 141 return TRUE; 142 #endif 143 } 144 145 146 #define APPHELP_DONTWRITE_REASON 2 147 #define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */ 148 #define APPHELP_IGNORE_ENVIRONMENT 0x400 149 150 #define APPHELP_VALID_RESULT 0x10000 151 #define APPHELP_RESULT_NOTFOUND 0x20000 152 #define APPHELP_RESULT_FOUND 0x40000 153 154 /** 155 * Lookup Shims / Fixes for the specified application 156 * 157 * @param [in] FileHandle Handle to the file to check. 158 * @param [in] Unk1 159 * @param [in] Unk2 160 * @param [in] ApplicationName Exe to check 161 * @param [in] Environment The environment variables to use, or NULL to use the current environment. 162 * @param [in] ExeType Exe type (MACHINE_TYPE_XXXX) 163 * @param [in,out] Reason Input/output flags 164 * @param [in] SdbQueryAppCompatData The resulting data. 165 * @param [in] SdbQueryAppCompatDataSize The resulting data size. 166 * @param [in] SxsData TODO 167 * @param [in] SxsDataSize TODO 168 * @param [in] FusionFlags TODO 169 * @param [in] SomeFlag1 TODO 170 * @param [in] SomeFlag2 TODO 171 * 172 * @return TRUE if the application is allowed to run. 173 */ 174 BOOL 175 WINAPI 176 ApphelpCheckRunAppEx( 177 _In_ HANDLE FileHandle, 178 _In_opt_ PVOID Unk1, 179 _In_opt_ PVOID Unk2, 180 _In_opt_z_ PWCHAR ApplicationName, 181 _In_opt_ PVOID Environment, 182 _In_opt_ USHORT ExeType, 183 _Inout_opt_ PULONG Reason, 184 _Out_opt_ PVOID* SdbQueryAppCompatData, 185 _Out_opt_ PULONG SdbQueryAppCompatDataSize, 186 _Out_opt_ PVOID* SxsData, 187 _Out_opt_ PULONG SxsDataSize, 188 _Out_opt_ PULONG FusionFlags, 189 _Out_opt_ PULONG64 SomeFlag1, 190 _Out_opt_ PULONG SomeFlag2) 191 { 192 SDBQUERYRESULT* result = NULL; 193 HSDB hsdb = NULL; 194 DWORD dwFlags = 0; 195 196 if (SxsData) 197 *SxsData = NULL; 198 if (SxsDataSize) 199 *SxsDataSize = 0; 200 if (FusionFlags) 201 *FusionFlags = 0; 202 if (SomeFlag1) 203 *SomeFlag1 = 0; 204 if (SomeFlag2) 205 *SomeFlag2 = 0; 206 if (Reason) 207 dwFlags = *Reason; 208 209 dwFlags &= ~APPHELP_CLEARBITS; 210 211 *SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT)); 212 if (SdbQueryAppCompatDataSize) 213 *SdbQueryAppCompatDataSize = sizeof(*result); 214 215 216 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL); 217 if (hsdb) 218 { 219 BOOL FoundMatch; 220 DWORD MatchingExeFlags = 0; 221 222 if (dwFlags & APPHELP_IGNORE_ENVIRONMENT) 223 MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT; 224 225 FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result); 226 if (FileHandle != INVALID_HANDLE_VALUE) 227 { 228 dwFlags |= APPHELP_VALID_RESULT; 229 dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND); 230 } 231 232 SdbReleaseDatabase(hsdb); 233 } 234 235 if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON)) 236 *Reason = dwFlags; 237 238 239 /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */ 240 return TRUE; 241 } 242 243 244 /** 245 * @name SdbRegisterDatabaseEx 246 * Register an application compatibility database 247 * 248 * @param pszDatabasePath The database. Required 249 * @param dwDatabaseType The database type. SDB_DATABASE_* 250 * @param pTimeStamp The timestamp. When this argument is not provided, the system time is used. 251 * @return TRUE on success, or FALSE on failure. 252 */ 253 BOOL WINAPI SdbRegisterDatabaseEx( 254 _In_ LPCTSTR pszDatabasePath, 255 _In_ DWORD dwDatabaseType, 256 _In_opt_ PULONGLONG pTimeStamp) 257 { 258 SHIM_ERR("UNIMPLEMENTED, pszDatabasePath=%ws, dwDatabaseType=0x%x, pTimeStamp=%p\n", 259 pszDatabasePath, dwDatabaseType, pTimeStamp); 260 261 return FALSE; 262 } 263 264 265 /** 266 * @name SdbRegisterDatabase 267 * Register an application compatibility database 268 * 269 * @param pszDatabasePath The database. Required 270 * @param dwDatabaseType The database type. SDB_DATABASE_* 271 * @return TRUE on success, or FALSE on failure. 272 */ 273 BOOL WINAPI SdbRegisterDatabase( 274 _In_ LPCTSTR pszDatabasePath, 275 _In_ DWORD dwDatabaseType) 276 { 277 return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL); 278 } 279 280 281 /** 282 * @name SdbUnregisterDatabase 283 * 284 * 285 * @param pguidDB 286 * @return 287 */ 288 BOOL WINAPI SdbUnregisterDatabase(_In_ GUID *pguidDB) 289 { 290 SHIM_ERR("UNIMPLEMENTED, pguidDB = %p\n", pguidDB); 291 292 return FALSE; 293 } 294 295 296 /* kernel32.dll */ 297 BOOL WINAPI BaseDumpAppcompatCache(VOID); 298 BOOL WINAPI BaseFlushAppcompatCache(VOID); 299 300 301 /** 302 * @name ShimDumpCache 303 * Dump contents of the shim cache. 304 * 305 * @param hwnd Unused, pass 0 306 * @param hInstance Unused, pass 0 307 * @param lpszCmdLine Unused, pass 0 308 * @param nCmdShow Unused, pass 0 309 * @return 310 */ 311 BOOL WINAPI ShimDumpCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow) 312 { 313 return BaseDumpAppcompatCache(); 314 } 315 316 /** 317 * @name ShimFlushCache 318 * Flush the shim cache. Call this after installing a new shim database 319 * 320 * @param hwnd Unused, pass 0 321 * @param hInstance Unused, pass 0 322 * @param lpszCmdLine Unused, pass 0 323 * @param nCmdShow Unused, pass 0 324 * @return 325 */ 326 BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow) 327 { 328 return BaseFlushAppcompatCache(); 329 } 330