xref: /reactos/dll/appcompat/apphelp/apphelp.c (revision b3094aa0)
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 "winreg.h"
14 #include "strsafe.h"
15 #include "apphelp.h"
16 #include <ndk/rtlfuncs.h>
17 #include <ndk/cmfuncs.h>
18 #include <ndk/obfuncs.h>
19 #include <ndk/kdtypes.h>
20 
21 
22 ACCESS_MASK Wow64QueryFlag(void);
23 
24 const UNICODE_STRING InstalledSDBKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB");
25 
26 /* from dpfilter.h */
27 #define DPFLTR_APPCOMPAT_ID 123
28 
29 #define MAX_GUID_STRING_LEN   RTL_NUMBER_OF("{12345678-1234-1234-0123-456789abcdef}")
30 C_ASSERT(MAX_GUID_STRING_LEN == 39); // See psdk/cfgmgr32.h
31 
32 #ifndef NT_SUCCESS
33 #define NT_SUCCESS(StatCode)  ((NTSTATUS)(StatCode) >= 0)
34 #endif
35 
36 ULONG g_ShimDebugLevel = ~0;
37 HMODULE g_hInstance;
38 
ApphelppInitDebugLevel(void)39 void ApphelppInitDebugLevel(void)
40 {
41     static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
42     UNICODE_STRING DebugValue;
43     NTSTATUS Status;
44     ULONG NewLevel = SHIM_ERR;
45     WCHAR Buffer[40];
46 
47     RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
48 
49     Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
50 
51     if (NT_SUCCESS(Status))
52     {
53         if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
54             NewLevel = SHIM_ERR;
55     }
56     g_ShimDebugLevel = NewLevel;
57 }
58 
59 
DllMain(HINSTANCE hinst,DWORD reason,LPVOID reserved)60 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
61 {
62     switch (reason)
63     {
64     case DLL_PROCESS_ATTACH:
65         g_hInstance = hinst;
66         DisableThreadLibraryCalls(hinst);
67         SdbpHeapInit();
68         break;
69     case DLL_PROCESS_DETACH:
70         SdbpHeapDeinit();
71         break;
72     }
73     return TRUE;
74 }
75 
ApphelpCheckInstallShieldPackage(void * ptr,LPCWSTR path)76 BOOL WINAPI ApphelpCheckInstallShieldPackage(void* ptr, LPCWSTR path)
77 {
78     SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr, path);
79     return TRUE;
80 }
81 
82 
ApphelpCheckShellObject(REFCLSID ObjectCLSID,BOOL bShimIfNecessary,ULONGLONG * pullFlags)83 BOOL WINAPI ApphelpCheckShellObject(REFCLSID ObjectCLSID, BOOL bShimIfNecessary, ULONGLONG *pullFlags)
84 {
85     WCHAR GuidString[MAX_GUID_STRING_LEN];
86     if (!ObjectCLSID || !SdbGUIDToString(ObjectCLSID, GuidString, RTL_NUMBER_OF(GuidString)))
87         GuidString[0] = L'\0';
88     SHIM_WARN("stub: ObjectCLSID='%S', bShimIfNecessary=%d, pullFlags=%p)\n", GuidString, bShimIfNecessary, pullFlags);
89 
90     if (pullFlags)
91         *pullFlags = 0;
92 
93     return TRUE;
94 }
95 
96 /**
97  * Outputs diagnostic info.
98  *
99  * @param [in]  Level           The level to log this message with, choose any of [SHIM_ERR,
100  *                              SHIM_WARN, SHIM_INFO].
101  * @param [in]  FunctionName    The function this log should be attributed to.
102  * @param [in]  Format          The format string.
103  * @param   ...                 Variable arguments providing additional information.
104  *
105  * @return  Success: TRUE Failure: FALSE.
106  */
ShimDbgPrint(SHIM_LOG_LEVEL Level,PCSTR FunctionName,PCSTR Format,...)107 BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...)
108 {
109     char Buffer[512];
110     va_list ArgList;
111     char* Current = Buffer;
112     const char* LevelStr;
113     size_t Length = sizeof(Buffer);
114 
115     if (g_ShimDebugLevel == ~0)
116         ApphelppInitDebugLevel();
117 
118     if (Level > g_ShimDebugLevel)
119         return FALSE;
120 
121     switch (Level)
122     {
123     case SHIM_ERR:
124         LevelStr = "Err ";
125         Level = DPFLTR_MASK | (1 << DPFLTR_ERROR_LEVEL);
126         break;
127     case SHIM_WARN:
128         LevelStr = "Warn";
129         Level = DPFLTR_MASK | (1 << DPFLTR_WARNING_LEVEL);
130         break;
131     case SHIM_INFO:
132         LevelStr = "Info";
133         Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
134         break;
135     default:
136         LevelStr = "User";
137         Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
138         break;
139     }
140     StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName);
141 
142     va_start(ArgList, Format);
143     StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
144     va_end(ArgList);
145 
146 #if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX
147     return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer));
148 #else
149     DbgPrint("%s", Buffer);
150     return TRUE;
151 #endif
152 }
153 
154 
155 #define APPHELP_DONTWRITE_REASON    2
156 #define APPHELP_CLEARBITS           0x100   /* TODO: Investigate */
157 #define APPHELP_IGNORE_ENVIRONMENT  0x400
158 
159 #define APPHELP_VALID_RESULT        0x10000
160 #define APPHELP_RESULT_NOTFOUND     0x20000
161 #define APPHELP_RESULT_FOUND        0x40000
162 
163 /**
164  * Lookup Shims / Fixes for the specified application
165  *
166  * @param [in]  FileHandle                  Handle to the file to check.
167  * @param [in]  Unk1
168  * @param [in]  Unk2
169  * @param [in]  ApplicationName             Exe to check
170  * @param [in]  Environment                 The environment variables to use, or NULL to use the current environment.
171  * @param [in]  ExeType                     Exe type (MACHINE_TYPE_XXXX)
172  * @param [in,out]  Reason                  Input/output flags
173  * @param [in]  SdbQueryAppCompatData       The resulting data.
174  * @param [in]  SdbQueryAppCompatDataSize   The resulting data size.
175  * @param [in]  SxsData                     TODO
176  * @param [in]  SxsDataSize                 TODO
177  * @param [in]  FusionFlags                 TODO
178  * @param [in]  SomeFlag1                   TODO
179  * @param [in]  SomeFlag2                   TODO
180  *
181  * @return  TRUE if the application is allowed to run.
182  */
183 BOOL
184 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)185 ApphelpCheckRunAppEx(
186     _In_ HANDLE FileHandle,
187     _In_opt_ PVOID Unk1,
188     _In_opt_ PVOID Unk2,
189     _In_opt_z_ PCWSTR ApplicationName,
190     _In_opt_ PVOID Environment,
191     _In_opt_ USHORT ExeType,
192     _Inout_opt_ PULONG Reason,
193     _Out_opt_ PVOID* SdbQueryAppCompatData,
194     _Out_opt_ PULONG SdbQueryAppCompatDataSize,
195     _Out_opt_ PVOID* SxsData,
196     _Out_opt_ PULONG SxsDataSize,
197     _Out_opt_ PULONG FusionFlags,
198     _Out_opt_ PULONG64 SomeFlag1,
199     _Out_opt_ PULONG SomeFlag2)
200 {
201     SDBQUERYRESULT* result = NULL;
202     HSDB hsdb = NULL;
203     DWORD dwFlags = 0;
204 
205     if (SxsData)
206         *SxsData = NULL;
207     if (SxsDataSize)
208         *SxsDataSize = 0;
209     if (FusionFlags)
210         *FusionFlags = 0;
211     if (SomeFlag1)
212         *SomeFlag1 = 0;
213     if (SomeFlag2)
214         *SomeFlag2 = 0;
215     if (Reason)
216         dwFlags = *Reason;
217 
218     dwFlags &= ~APPHELP_CLEARBITS;
219 
220     *SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT));
221     if (SdbQueryAppCompatDataSize)
222         *SdbQueryAppCompatDataSize = sizeof(*result);
223 
224 
225     hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
226     if (hsdb)
227     {
228         BOOL FoundMatch;
229         DWORD MatchingExeFlags = 0;
230 
231         if (dwFlags & APPHELP_IGNORE_ENVIRONMENT)
232             MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT;
233 
234         FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result);
235         if (FileHandle != INVALID_HANDLE_VALUE)
236         {
237             dwFlags |= APPHELP_VALID_RESULT;
238             dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND);
239         }
240 
241         SdbReleaseDatabase(hsdb);
242     }
243 
244     if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON))
245         *Reason = dwFlags;
246 
247 
248     /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */
249     return TRUE;
250 }
251 
252 
253 /**
254  * @name SdbRegisterDatabaseEx
255  * Register an application compatibility database
256  *
257  * @param pszDatabasePath   The database. Required
258  * @param dwDatabaseType    The database type. SDB_DATABASE_*
259  * @param pTimeStamp        The timestamp. When this argument is not provided, the system time is used.
260  * @return                  TRUE on success, or FALSE on failure.
261  */
SdbRegisterDatabaseEx(_In_ LPCWSTR pszDatabasePath,_In_ DWORD dwDatabaseType,_In_opt_ const PULONGLONG pTimeStamp)262 BOOL WINAPI SdbRegisterDatabaseEx(
263     _In_ LPCWSTR pszDatabasePath,
264     _In_ DWORD dwDatabaseType,
265     _In_opt_ const PULONGLONG pTimeStamp)
266 {
267     PDB pdb;
268     DB_INFORMATION Information;
269     WCHAR GuidBuffer[MAX_GUID_STRING_LEN];
270     UNICODE_STRING KeyName;
271     ACCESS_MASK KeyAccess;
272     OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
273     NTSTATUS Status;
274     HANDLE InstalledSDBKey;
275 
276     pdb = SdbOpenDatabase(pszDatabasePath, DOS_PATH);
277     if (!pdb)
278     {
279         SHIM_ERR("Unable to open DB %S\n", pszDatabasePath);
280         return FALSE;
281     }
282 
283     if (!SdbGetDatabaseInformation(pdb, &Information) ||
284         !(Information.dwFlags & DB_INFO_FLAGS_VALID_GUID))
285     {
286         SHIM_ERR("Unable to retrieve DB info\n");
287         SdbCloseDatabase(pdb);
288         return FALSE;
289     }
290 
291     if (!SdbGUIDToString(&Information.Id, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
292     {
293         SHIM_ERR("Unable to Convert GUID to string\n");
294         SdbFreeDatabaseInformation(&Information);
295         SdbCloseDatabase(pdb);
296         return FALSE;
297     }
298 
299     KeyName = InstalledSDBKeyName;
300     KeyAccess = Wow64QueryFlag() | KEY_WRITE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
301     Status = NtCreateKey(&InstalledSDBKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
302     if (NT_SUCCESS(Status))
303     {
304         HANDLE DbKey;
305 
306         RtlInitUnicodeString(&KeyName, GuidBuffer);
307         ObjectKey.RootDirectory = InstalledSDBKey;
308 
309         Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
310         if (NT_SUCCESS(Status))
311         {
312             UNICODE_STRING DatabasePathKey = RTL_CONSTANT_STRING(L"DatabasePath");
313             UNICODE_STRING DatabaseInstallTimeStampKey = RTL_CONSTANT_STRING(L"DatabaseInstallTimeStamp");
314             UNICODE_STRING DatabaseTypeKey = RTL_CONSTANT_STRING(L"DatabaseType");
315             UNICODE_STRING DatabaseDescriptionKey = RTL_CONSTANT_STRING(L"DatabaseDescription");
316 
317             Status = NtSetValueKey(DbKey, &DatabasePathKey, 0, REG_SZ,
318                                    (PVOID)pszDatabasePath, ((ULONG)wcslen(pszDatabasePath) + 1) * sizeof(WCHAR));
319             if (!NT_SUCCESS(Status))
320                 SHIM_ERR("Unable to write %wZ\n", &DatabasePathKey);
321 
322             if (NT_SUCCESS(Status))
323             {
324                 ULARGE_INTEGER ulTimeStamp;
325                 if (pTimeStamp)
326                 {
327                     ulTimeStamp.QuadPart = *pTimeStamp;
328                 }
329                 else
330                 {
331                     FILETIME fi;
332                     GetSystemTimeAsFileTime(&fi);
333                     ulTimeStamp.LowPart = fi.dwLowDateTime;
334                     ulTimeStamp.HighPart = fi.dwHighDateTime;
335                 }
336                 Status = NtSetValueKey(DbKey, &DatabaseInstallTimeStampKey, 0, REG_QWORD,
337                                        &ulTimeStamp.QuadPart, sizeof(ulTimeStamp));
338                 if (!NT_SUCCESS(Status))
339                     SHIM_ERR("Unable to write %wZ\n", &DatabaseInstallTimeStampKey);
340             }
341 
342             if (NT_SUCCESS(Status))
343             {
344                 Status = NtSetValueKey(DbKey, &DatabaseTypeKey, 0, REG_DWORD,
345                                        &dwDatabaseType, sizeof(dwDatabaseType));
346                 if (!NT_SUCCESS(Status))
347                     SHIM_ERR("Unable to write %wZ\n", &DatabaseTypeKey);
348             }
349 
350             if (NT_SUCCESS(Status) && Information.Description)
351             {
352                 Status = NtSetValueKey(DbKey, &DatabaseDescriptionKey, 0, REG_SZ,
353                                        (PVOID)Information.Description, ((ULONG)wcslen(Information.Description) + 1) * sizeof(WCHAR));
354                 if (!NT_SUCCESS(Status))
355                     SHIM_ERR("Unable to write %wZ\n", &DatabaseDescriptionKey);
356             }
357 
358             NtClose(DbKey);
359 
360             if (NT_SUCCESS(Status))
361             {
362                 SHIM_INFO("Installed %wS as %wZ\n", pszDatabasePath, &KeyName);
363             }
364         }
365         else
366         {
367             SHIM_ERR("Unable to create key %wZ\n", &KeyName);
368         }
369 
370         NtClose(InstalledSDBKey);
371     }
372     else
373     {
374         SHIM_ERR("Unable to create key %wZ\n", &KeyName);
375     }
376 
377     SdbFreeDatabaseInformation(&Information);
378     SdbCloseDatabase(pdb);
379 
380     return NT_SUCCESS(Status);
381 }
382 
383 
384 /**
385  * @name SdbRegisterDatabase
386  * Register an application compatibility database
387  *
388  * @param pszDatabasePath   The database. Required
389  * @param dwDatabaseType    The database type. SDB_DATABASE_*
390  * @return                  TRUE on success, or FALSE on failure.
391  */
SdbRegisterDatabase(_In_ LPCWSTR pszDatabasePath,_In_ DWORD dwDatabaseType)392 BOOL WINAPI SdbRegisterDatabase(
393     _In_ LPCWSTR pszDatabasePath,
394     _In_ DWORD dwDatabaseType)
395 {
396     return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL);
397 }
398 
399 
400 /**
401  * @name SdbUnregisterDatabase
402  *
403  *
404  * @param pguidDB
405  * @return
406  */
SdbUnregisterDatabase(_In_ const GUID * pguidDB)407 BOOL WINAPI SdbUnregisterDatabase(_In_ const GUID *pguidDB)
408 {
409     WCHAR KeyBuffer[MAX_PATH], GuidBuffer[MAX_GUID_STRING_LEN];
410     UNICODE_STRING KeyName;
411     ACCESS_MASK KeyAccess;
412     OBJECT_ATTRIBUTES ObjectKey = RTL_INIT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
413     NTSTATUS Status;
414     HANDLE DbKey;
415 
416     if (!SdbGUIDToString(pguidDB, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
417     {
418         SHIM_ERR("Unable to Convert GUID to string\n");
419         return FALSE;
420     }
421 
422     RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
423     RtlAppendUnicodeStringToString(&KeyName, &InstalledSDBKeyName);
424     RtlAppendUnicodeToString(&KeyName, L"\\");
425     RtlAppendUnicodeToString(&KeyName, GuidBuffer);
426 
427     KeyAccess = Wow64QueryFlag() | DELETE;
428     Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
429     if (!NT_SUCCESS(Status))
430     {
431         SHIM_ERR("Unable to open %wZ\n", &KeyName);
432         return FALSE;
433     }
434 
435     Status = NtDeleteKey(DbKey);
436     if (!NT_SUCCESS(Status))
437         SHIM_ERR("Unable to delete %wZ\n", &KeyName);
438 
439     NtClose(DbKey);
440     return NT_SUCCESS(Status);
441 }
442 
443 
444 /* kernel32.dll */
445 BOOL WINAPI BaseDumpAppcompatCache(VOID);
446 BOOL WINAPI BaseFlushAppcompatCache(VOID);
447 
448 
449 /**
450  * @name ShimDumpCache
451  * Dump contents of the shim cache.
452  *
453  * @param hwnd          Unused, pass 0
454  * @param hInstance     Unused, pass 0
455  * @param lpszCmdLine   Unused, pass 0
456  * @param nCmdShow      Unused, pass 0
457  * @return
458  */
ShimDumpCache(HWND hwnd,HINSTANCE hInstance,LPCSTR lpszCmdLine,int nCmdShow)459 BOOL WINAPI ShimDumpCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
460 {
461     return BaseDumpAppcompatCache();
462 }
463 
464 /**
465 * @name ShimFlushCache
466 * Flush the shim cache. Call this after installing a new shim database
467 *
468 * @param hwnd          Unused, pass 0
469 * @param hInstance     Unused, pass 0
470 * @param lpszCmdLine   Unused, pass 0
471 * @param nCmdShow      Unused, pass 0
472 * @return
473 */
ShimFlushCache(HWND hwnd,HINSTANCE hInstance,LPCSTR lpszCmdLine,int nCmdShow)474 BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
475 {
476     return BaseFlushAppcompatCache();
477 }
478