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)
10 #define WIN32_NO_STATUS
16 #include <ndk/rtlfuncs.h>
17 #include <ndk/cmfuncs.h>
18 #include <ndk/obfuncs.h>
19 #include <ndk/kdtypes.h>
22 ACCESS_MASK
Wow64QueryFlag(void);
24 const UNICODE_STRING InstalledSDBKeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB");
27 #define DPFLTR_APPCOMPAT_ID 123
29 #define MAX_GUID_STRING_LEN sizeof("{12345678-1234-1234-0123-456789abcdef}")
32 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0)
35 ULONG g_ShimDebugLevel
= ~0;
38 void ApphelppInitDebugLevel(void)
40 static const UNICODE_STRING DebugKey
= RTL_CONSTANT_STRING(L
"SHIM_DEBUG_LEVEL");
41 UNICODE_STRING DebugValue
;
43 ULONG NewLevel
= SHIM_ERR
;
46 RtlInitEmptyUnicodeString(&DebugValue
, Buffer
, sizeof(Buffer
));
48 Status
= RtlQueryEnvironmentVariable_U(NULL
, &DebugKey
, &DebugValue
);
50 if (NT_SUCCESS(Status
))
52 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue
, 10, &NewLevel
)))
55 g_ShimDebugLevel
= NewLevel
;
59 BOOL WINAPI
DllMain( HINSTANCE hinst
, DWORD reason
, LPVOID reserved
)
63 case DLL_PROCESS_ATTACH
:
65 DisableThreadLibraryCalls(hinst
);
68 case DLL_PROCESS_DETACH
:
75 BOOL WINAPI
ApphelpCheckInstallShieldPackage(void* ptr
, LPCWSTR path
)
77 SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr
, path
);
82 BOOL WINAPI
ApphelpCheckShellObject(REFCLSID ObjectCLSID
, BOOL bShimIfNecessary
, ULONGLONG
*pullFlags
)
84 WCHAR GuidString
[MAX_GUID_STRING_LEN
];
85 if (!ObjectCLSID
|| !SdbGUIDToString(ObjectCLSID
, GuidString
, RTL_NUMBER_OF(GuidString
)))
86 GuidString
[0] = L
'\0';
87 SHIM_WARN("stub: ObjectCLSID='%S', bShimIfNecessary=%d, pullFlags=%p)\n", GuidString
, bShimIfNecessary
, pullFlags
);
96 * Outputs diagnostic info.
98 * @param [in] Level The level to log this message with, choose any of [SHIM_ERR,
99 * SHIM_WARN, SHIM_INFO].
100 * @param [in] FunctionName The function this log should be attributed to.
101 * @param [in] Format The format string.
102 * @param ... Variable arguments providing additional information.
104 * @return Success: TRUE Failure: FALSE.
106 BOOL WINAPIV
ShimDbgPrint(SHIM_LOG_LEVEL Level
, PCSTR FunctionName
, PCSTR Format
, ...)
110 char* Current
= Buffer
;
111 const char* LevelStr
;
112 size_t Length
= sizeof(Buffer
);
114 if (g_ShimDebugLevel
== ~0)
115 ApphelppInitDebugLevel();
117 if (Level
> g_ShimDebugLevel
)
124 Level
= DPFLTR_MASK
| (1 << DPFLTR_ERROR_LEVEL
);
128 Level
= DPFLTR_MASK
| (1 << DPFLTR_WARNING_LEVEL
);
132 Level
= DPFLTR_MASK
| (1 << DPFLTR_INFO_LEVEL
);
136 Level
= DPFLTR_MASK
| (1 << DPFLTR_INFO_LEVEL
);
139 StringCchPrintfExA(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, "[%s][%-20s] ", LevelStr
, FunctionName
);
141 va_start(ArgList
, Format
);
142 StringCchVPrintfExA(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
145 #if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX
146 return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID
, Level
, "%s", Buffer
));
148 DbgPrint("%s", Buffer
);
154 #define APPHELP_DONTWRITE_REASON 2
155 #define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */
156 #define APPHELP_IGNORE_ENVIRONMENT 0x400
158 #define APPHELP_VALID_RESULT 0x10000
159 #define APPHELP_RESULT_NOTFOUND 0x20000
160 #define APPHELP_RESULT_FOUND 0x40000
163 * Lookup Shims / Fixes for the specified application
165 * @param [in] FileHandle Handle to the file to check.
168 * @param [in] ApplicationName Exe to check
169 * @param [in] Environment The environment variables to use, or NULL to use the current environment.
170 * @param [in] ExeType Exe type (MACHINE_TYPE_XXXX)
171 * @param [in,out] Reason Input/output flags
172 * @param [in] SdbQueryAppCompatData The resulting data.
173 * @param [in] SdbQueryAppCompatDataSize The resulting data size.
174 * @param [in] SxsData TODO
175 * @param [in] SxsDataSize TODO
176 * @param [in] FusionFlags TODO
177 * @param [in] SomeFlag1 TODO
178 * @param [in] SomeFlag2 TODO
180 * @return TRUE if the application is allowed to run.
184 ApphelpCheckRunAppEx(
185 _In_ HANDLE FileHandle
,
188 _In_opt_z_ PWCHAR ApplicationName
,
189 _In_opt_ PVOID Environment
,
190 _In_opt_ USHORT ExeType
,
191 _Inout_opt_ PULONG Reason
,
192 _Out_opt_ PVOID
* SdbQueryAppCompatData
,
193 _Out_opt_ PULONG SdbQueryAppCompatDataSize
,
194 _Out_opt_ PVOID
* SxsData
,
195 _Out_opt_ PULONG SxsDataSize
,
196 _Out_opt_ PULONG FusionFlags
,
197 _Out_opt_ PULONG64 SomeFlag1
,
198 _Out_opt_ PULONG SomeFlag2
)
200 SDBQUERYRESULT
* result
= NULL
;
217 dwFlags
&= ~APPHELP_CLEARBITS
;
219 *SdbQueryAppCompatData
= result
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SDBQUERYRESULT
));
220 if (SdbQueryAppCompatDataSize
)
221 *SdbQueryAppCompatDataSize
= sizeof(*result
);
224 hsdb
= SdbInitDatabase(HID_DOS_PATHS
| SDB_DATABASE_MAIN_SHIM
, NULL
);
228 DWORD MatchingExeFlags
= 0;
230 if (dwFlags
& APPHELP_IGNORE_ENVIRONMENT
)
231 MatchingExeFlags
|= SDBGMEF_IGNORE_ENVIRONMENT
;
233 FoundMatch
= SdbGetMatchingExe(hsdb
, ApplicationName
, NULL
, Environment
, MatchingExeFlags
, result
);
234 if (FileHandle
!= INVALID_HANDLE_VALUE
)
236 dwFlags
|= APPHELP_VALID_RESULT
;
237 dwFlags
|= (FoundMatch
? APPHELP_RESULT_FOUND
: APPHELP_RESULT_NOTFOUND
);
240 SdbReleaseDatabase(hsdb
);
243 if (Reason
&& !(dwFlags
& APPHELP_DONTWRITE_REASON
))
247 /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */
253 * @name SdbRegisterDatabaseEx
254 * Register an application compatibility database
256 * @param pszDatabasePath The database. Required
257 * @param dwDatabaseType The database type. SDB_DATABASE_*
258 * @param pTimeStamp The timestamp. When this argument is not provided, the system time is used.
259 * @return TRUE on success, or FALSE on failure.
261 BOOL WINAPI
SdbRegisterDatabaseEx(
262 _In_ LPCWSTR pszDatabasePath
,
263 _In_ DWORD dwDatabaseType
,
264 _In_opt_
const PULONGLONG pTimeStamp
)
267 DB_INFORMATION Information
;
268 WCHAR GuidBuffer
[MAX_GUID_STRING_LEN
];
269 UNICODE_STRING KeyName
;
270 ACCESS_MASK KeyAccess
;
271 OBJECT_ATTRIBUTES ObjectKey
= RTL_INIT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
273 HANDLE InstalledSDBKey
;
275 pdb
= SdbOpenDatabase(pszDatabasePath
, DOS_PATH
);
278 SHIM_ERR("Unable to open DB %S\n", pszDatabasePath
);
282 if (!SdbGetDatabaseInformation(pdb
, &Information
) ||
283 !(Information
.dwFlags
& DB_INFO_FLAGS_VALID_GUID
))
285 SHIM_ERR("Unable to retrieve DB info\n");
286 SdbCloseDatabase(pdb
);
290 if (!SdbGUIDToString(&Information
.Id
, GuidBuffer
, RTL_NUMBER_OF(GuidBuffer
)))
292 SHIM_ERR("Unable to Convert GUID to string\n");
293 SdbFreeDatabaseInformation(&Information
);
294 SdbCloseDatabase(pdb
);
298 KeyName
= InstalledSDBKeyName
;
299 KeyAccess
= Wow64QueryFlag() | KEY_WRITE
| KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
;
300 Status
= NtCreateKey(&InstalledSDBKey
, KeyAccess
, &ObjectKey
, 0, NULL
, 0, NULL
);
301 if (NT_SUCCESS(Status
))
305 RtlInitUnicodeString(&KeyName
, GuidBuffer
);
306 ObjectKey
.RootDirectory
= InstalledSDBKey
;
308 Status
= NtCreateKey(&DbKey
, KeyAccess
, &ObjectKey
, 0, NULL
, 0, NULL
);
309 if (NT_SUCCESS(Status
))
311 UNICODE_STRING DatabasePathKey
= RTL_CONSTANT_STRING(L
"DatabasePath");
312 UNICODE_STRING DatabaseInstallTimeStampKey
= RTL_CONSTANT_STRING(L
"DatabaseInstallTimeStamp");
313 UNICODE_STRING DatabaseTypeKey
= RTL_CONSTANT_STRING(L
"DatabaseType");
314 UNICODE_STRING DatabaseDescriptionKey
= RTL_CONSTANT_STRING(L
"DatabaseDescription");
316 Status
= NtSetValueKey(DbKey
, &DatabasePathKey
, 0, REG_SZ
,
317 (PVOID
)pszDatabasePath
, ((ULONG
)wcslen(pszDatabasePath
) + 1) * sizeof(WCHAR
));
318 if (!NT_SUCCESS(Status
))
319 SHIM_ERR("Unable to write %wZ\n", &DatabasePathKey
);
321 if (NT_SUCCESS(Status
))
323 ULARGE_INTEGER ulTimeStamp
;
326 ulTimeStamp
.QuadPart
= *pTimeStamp
;
331 GetSystemTimeAsFileTime(&fi
);
332 ulTimeStamp
.LowPart
= fi
.dwLowDateTime
;
333 ulTimeStamp
.HighPart
= fi
.dwHighDateTime
;
335 Status
= NtSetValueKey(DbKey
, &DatabaseInstallTimeStampKey
, 0, REG_QWORD
,
336 &ulTimeStamp
.QuadPart
, sizeof(ulTimeStamp
));
337 if (!NT_SUCCESS(Status
))
338 SHIM_ERR("Unable to write %wZ\n", &DatabaseInstallTimeStampKey
);
341 if (NT_SUCCESS(Status
))
343 Status
= NtSetValueKey(DbKey
, &DatabaseTypeKey
, 0, REG_DWORD
,
344 &dwDatabaseType
, sizeof(dwDatabaseType
));
345 if (!NT_SUCCESS(Status
))
346 SHIM_ERR("Unable to write %wZ\n", &DatabaseTypeKey
);
349 if (NT_SUCCESS(Status
) && Information
.Description
)
351 Status
= NtSetValueKey(DbKey
, &DatabaseDescriptionKey
, 0, REG_SZ
,
352 (PVOID
)Information
.Description
, ((ULONG
)wcslen(Information
.Description
) + 1) * sizeof(WCHAR
));
353 if (!NT_SUCCESS(Status
))
354 SHIM_ERR("Unable to write %wZ\n", &DatabaseDescriptionKey
);
359 if (NT_SUCCESS(Status
))
361 SHIM_INFO("Installed %wS as %wZ\n", pszDatabasePath
, &KeyName
);
366 SHIM_ERR("Unable to create key %wZ\n", &KeyName
);
369 NtClose(InstalledSDBKey
);
373 SHIM_ERR("Unable to create key %wZ\n", &KeyName
);
376 SdbFreeDatabaseInformation(&Information
);
377 SdbCloseDatabase(pdb
);
379 return NT_SUCCESS(Status
);
384 * @name SdbRegisterDatabase
385 * Register an application compatibility database
387 * @param pszDatabasePath The database. Required
388 * @param dwDatabaseType The database type. SDB_DATABASE_*
389 * @return TRUE on success, or FALSE on failure.
391 BOOL WINAPI
SdbRegisterDatabase(
392 _In_ LPCWSTR pszDatabasePath
,
393 _In_ DWORD dwDatabaseType
)
395 return SdbRegisterDatabaseEx(pszDatabasePath
, dwDatabaseType
, NULL
);
400 * @name SdbUnregisterDatabase
406 BOOL WINAPI
SdbUnregisterDatabase(_In_
const GUID
*pguidDB
)
408 WCHAR KeyBuffer
[MAX_PATH
], GuidBuffer
[50];
409 UNICODE_STRING KeyName
;
410 ACCESS_MASK KeyAccess
;
411 OBJECT_ATTRIBUTES ObjectKey
= RTL_INIT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
415 if (!SdbGUIDToString(pguidDB
, GuidBuffer
, RTL_NUMBER_OF(GuidBuffer
)))
417 SHIM_ERR("Unable to Convert GUID to string\n");
421 RtlInitEmptyUnicodeString(&KeyName
, KeyBuffer
, sizeof(KeyBuffer
));
422 RtlAppendUnicodeStringToString(&KeyName
, &InstalledSDBKeyName
);
423 RtlAppendUnicodeToString(&KeyName
, L
"\\");
424 RtlAppendUnicodeToString(&KeyName
, GuidBuffer
);
426 KeyAccess
= Wow64QueryFlag() | DELETE
;
427 Status
= NtCreateKey(&DbKey
, KeyAccess
, &ObjectKey
, 0, NULL
, 0, NULL
);
428 if (!NT_SUCCESS(Status
))
430 SHIM_ERR("Unable to open %wZ\n", &KeyName
);
434 Status
= NtDeleteKey(DbKey
);
435 if (!NT_SUCCESS(Status
))
436 SHIM_ERR("Unable to delete %wZ\n", &KeyName
);
439 return NT_SUCCESS(Status
);
444 BOOL WINAPI
BaseDumpAppcompatCache(VOID
);
445 BOOL WINAPI
BaseFlushAppcompatCache(VOID
);
449 * @name ShimDumpCache
450 * Dump contents of the shim cache.
452 * @param hwnd Unused, pass 0
453 * @param hInstance Unused, pass 0
454 * @param lpszCmdLine Unused, pass 0
455 * @param nCmdShow Unused, pass 0
458 BOOL WINAPI
ShimDumpCache(HWND hwnd
, HINSTANCE hInstance
, LPCSTR lpszCmdLine
, int nCmdShow
)
460 return BaseDumpAppcompatCache();
464 * @name ShimFlushCache
465 * Flush the shim cache. Call this after installing a new shim database
467 * @param hwnd Unused, pass 0
468 * @param hInstance Unused, pass 0
469 * @param lpszCmdLine Unused, pass 0
470 * @param nCmdShow Unused, pass 0
473 BOOL WINAPI
ShimFlushCache(HWND hwnd
, HINSTANCE hInstance
, LPCSTR lpszCmdLine
, int nCmdShow
)
475 return BaseFlushAppcompatCache();