[APPHELP] Fix warnings
[reactos.git] / dll / appcompat / apphelp / apphelp.c
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 sizeof("{12345678-1234-1234-0123-456789abcdef}")
30
31 #ifndef NT_SUCCESS
32 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0)
33 #endif
34
35 ULONG g_ShimDebugLevel = ~0;
36 HMODULE g_hInstance;
37
38 void ApphelppInitDebugLevel(void)
39 {
40 static const UNICODE_STRING DebugKey = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
41 UNICODE_STRING DebugValue;
42 NTSTATUS Status;
43 ULONG NewLevel = SHIM_ERR;
44 WCHAR Buffer[40];
45
46 RtlInitEmptyUnicodeString(&DebugValue, Buffer, sizeof(Buffer));
47
48 Status = RtlQueryEnvironmentVariable_U(NULL, &DebugKey, &DebugValue);
49
50 if (NT_SUCCESS(Status))
51 {
52 if (!NT_SUCCESS(RtlUnicodeStringToInteger(&DebugValue, 10, &NewLevel)))
53 NewLevel = SHIM_ERR;
54 }
55 g_ShimDebugLevel = NewLevel;
56 }
57
58
59 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
60 {
61 switch (reason)
62 {
63 case DLL_PROCESS_ATTACH:
64 g_hInstance = hinst;
65 DisableThreadLibraryCalls(hinst);
66 SdbpHeapInit();
67 break;
68 case DLL_PROCESS_DETACH:
69 SdbpHeapDeinit();
70 break;
71 }
72 return TRUE;
73 }
74
75 BOOL WINAPI ApphelpCheckInstallShieldPackage(void* ptr, LPCWSTR path)
76 {
77 SHIM_WARN("stub: ptr=%p, path='%S'\n", ptr, path);
78 return TRUE;
79 }
80
81
82 BOOL WINAPI ApphelpCheckShellObject(REFCLSID ObjectCLSID, BOOL bShimIfNecessary, ULONGLONG *pullFlags)
83 {
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);
88
89 if (pullFlags)
90 *pullFlags = 0;
91
92 return TRUE;
93 }
94
95 /**
96 * Outputs diagnostic info.
97 *
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.
103 *
104 * @return Success: TRUE Failure: FALSE.
105 */
106 BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...)
107 {
108 char Buffer[512];
109 va_list ArgList;
110 char* Current = Buffer;
111 const char* LevelStr;
112 size_t Length = sizeof(Buffer);
113
114 if (g_ShimDebugLevel == ~0)
115 ApphelppInitDebugLevel();
116
117 if (Level > g_ShimDebugLevel)
118 return FALSE;
119
120 switch (Level)
121 {
122 case SHIM_ERR:
123 LevelStr = "Err ";
124 Level = DPFLTR_MASK | (1 << DPFLTR_ERROR_LEVEL);
125 break;
126 case SHIM_WARN:
127 LevelStr = "Warn";
128 Level = DPFLTR_MASK | (1 << DPFLTR_WARNING_LEVEL);
129 break;
130 case SHIM_INFO:
131 LevelStr = "Info";
132 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
133 break;
134 default:
135 LevelStr = "User";
136 Level = DPFLTR_MASK | (1 << DPFLTR_INFO_LEVEL);
137 break;
138 }
139 StringCchPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, "[%s][%-20s] ", LevelStr, FunctionName);
140
141 va_start(ArgList, Format);
142 StringCchVPrintfExA(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
143 va_end(ArgList);
144
145 #if defined(APPCOMPAT_USE_DBGPRINTEX) && APPCOMPAT_USE_DBGPRINTEX
146 return NT_SUCCESS(DbgPrintEx(DPFLTR_APPCOMPAT_ID, Level, "%s", Buffer));
147 #else
148 DbgPrint("%s", Buffer);
149 return TRUE;
150 #endif
151 }
152
153
154 #define APPHELP_DONTWRITE_REASON 2
155 #define APPHELP_CLEARBITS 0x100 /* TODO: Investigate */
156 #define APPHELP_IGNORE_ENVIRONMENT 0x400
157
158 #define APPHELP_VALID_RESULT 0x10000
159 #define APPHELP_RESULT_NOTFOUND 0x20000
160 #define APPHELP_RESULT_FOUND 0x40000
161
162 /**
163 * Lookup Shims / Fixes for the specified application
164 *
165 * @param [in] FileHandle Handle to the file to check.
166 * @param [in] Unk1
167 * @param [in] Unk2
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
179 *
180 * @return TRUE if the application is allowed to run.
181 */
182 BOOL
183 WINAPI
184 ApphelpCheckRunAppEx(
185 _In_ HANDLE FileHandle,
186 _In_opt_ PVOID Unk1,
187 _In_opt_ PVOID Unk2,
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)
199 {
200 SDBQUERYRESULT* result = NULL;
201 HSDB hsdb = NULL;
202 DWORD dwFlags = 0;
203
204 if (SxsData)
205 *SxsData = NULL;
206 if (SxsDataSize)
207 *SxsDataSize = 0;
208 if (FusionFlags)
209 *FusionFlags = 0;
210 if (SomeFlag1)
211 *SomeFlag1 = 0;
212 if (SomeFlag2)
213 *SomeFlag2 = 0;
214 if (Reason)
215 dwFlags = *Reason;
216
217 dwFlags &= ~APPHELP_CLEARBITS;
218
219 *SdbQueryAppCompatData = result = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDBQUERYRESULT));
220 if (SdbQueryAppCompatDataSize)
221 *SdbQueryAppCompatDataSize = sizeof(*result);
222
223
224 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
225 if (hsdb)
226 {
227 BOOL FoundMatch;
228 DWORD MatchingExeFlags = 0;
229
230 if (dwFlags & APPHELP_IGNORE_ENVIRONMENT)
231 MatchingExeFlags |= SDBGMEF_IGNORE_ENVIRONMENT;
232
233 FoundMatch = SdbGetMatchingExe(hsdb, ApplicationName, NULL, Environment, MatchingExeFlags, result);
234 if (FileHandle != INVALID_HANDLE_VALUE)
235 {
236 dwFlags |= APPHELP_VALID_RESULT;
237 dwFlags |= (FoundMatch ? APPHELP_RESULT_FOUND : APPHELP_RESULT_NOTFOUND);
238 }
239
240 SdbReleaseDatabase(hsdb);
241 }
242
243 if (Reason && !(dwFlags & APPHELP_DONTWRITE_REASON))
244 *Reason = dwFlags;
245
246
247 /* We should _ALWAYS_ return TRUE here, unless we want to block an application from starting! */
248 return TRUE;
249 }
250
251
252 /**
253 * @name SdbRegisterDatabaseEx
254 * Register an application compatibility database
255 *
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.
260 */
261 BOOL WINAPI SdbRegisterDatabaseEx(
262 _In_ LPCWSTR pszDatabasePath,
263 _In_ DWORD dwDatabaseType,
264 _In_opt_ const PULONGLONG pTimeStamp)
265 {
266 PDB pdb;
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);
272 NTSTATUS Status;
273 HANDLE InstalledSDBKey;
274
275 pdb = SdbOpenDatabase(pszDatabasePath, DOS_PATH);
276 if (!pdb)
277 {
278 SHIM_ERR("Unable to open DB %S\n", pszDatabasePath);
279 return FALSE;
280 }
281
282 if (!SdbGetDatabaseInformation(pdb, &Information) ||
283 !(Information.dwFlags & DB_INFO_FLAGS_VALID_GUID))
284 {
285 SHIM_ERR("Unable to retrieve DB info\n");
286 SdbCloseDatabase(pdb);
287 return FALSE;
288 }
289
290 if (!SdbGUIDToString(&Information.Id, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
291 {
292 SHIM_ERR("Unable to Convert GUID to string\n");
293 SdbFreeDatabaseInformation(&Information);
294 SdbCloseDatabase(pdb);
295 return FALSE;
296 }
297
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))
302 {
303 HANDLE DbKey;
304
305 RtlInitUnicodeString(&KeyName, GuidBuffer);
306 ObjectKey.RootDirectory = InstalledSDBKey;
307
308 Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
309 if (NT_SUCCESS(Status))
310 {
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");
315
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);
320
321 if (NT_SUCCESS(Status))
322 {
323 ULARGE_INTEGER ulTimeStamp;
324 if (pTimeStamp)
325 {
326 ulTimeStamp.QuadPart = *pTimeStamp;
327 }
328 else
329 {
330 FILETIME fi;
331 GetSystemTimeAsFileTime(&fi);
332 ulTimeStamp.LowPart = fi.dwLowDateTime;
333 ulTimeStamp.HighPart = fi.dwHighDateTime;
334 }
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);
339 }
340
341 if (NT_SUCCESS(Status))
342 {
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);
347 }
348
349 if (NT_SUCCESS(Status) && Information.Description)
350 {
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);
355 }
356
357 NtClose(DbKey);
358
359 if (NT_SUCCESS(Status))
360 {
361 SHIM_INFO("Installed %wS as %wZ\n", pszDatabasePath, &KeyName);
362 }
363 }
364 else
365 {
366 SHIM_ERR("Unable to create key %wZ\n", &KeyName);
367 }
368
369 NtClose(InstalledSDBKey);
370 }
371 else
372 {
373 SHIM_ERR("Unable to create key %wZ\n", &KeyName);
374 }
375
376 SdbFreeDatabaseInformation(&Information);
377 SdbCloseDatabase(pdb);
378
379 return NT_SUCCESS(Status);
380 }
381
382
383 /**
384 * @name SdbRegisterDatabase
385 * Register an application compatibility database
386 *
387 * @param pszDatabasePath The database. Required
388 * @param dwDatabaseType The database type. SDB_DATABASE_*
389 * @return TRUE on success, or FALSE on failure.
390 */
391 BOOL WINAPI SdbRegisterDatabase(
392 _In_ LPCWSTR pszDatabasePath,
393 _In_ DWORD dwDatabaseType)
394 {
395 return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL);
396 }
397
398
399 /**
400 * @name SdbUnregisterDatabase
401 *
402 *
403 * @param pguidDB
404 * @return
405 */
406 BOOL WINAPI SdbUnregisterDatabase(_In_ const GUID *pguidDB)
407 {
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);
412 NTSTATUS Status;
413 HANDLE DbKey;
414
415 if (!SdbGUIDToString(pguidDB, GuidBuffer, RTL_NUMBER_OF(GuidBuffer)))
416 {
417 SHIM_ERR("Unable to Convert GUID to string\n");
418 return FALSE;
419 }
420
421 RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
422 RtlAppendUnicodeStringToString(&KeyName, &InstalledSDBKeyName);
423 RtlAppendUnicodeToString(&KeyName, L"\\");
424 RtlAppendUnicodeToString(&KeyName, GuidBuffer);
425
426 KeyAccess = Wow64QueryFlag() | DELETE;
427 Status = NtCreateKey(&DbKey, KeyAccess, &ObjectKey, 0, NULL, 0, NULL);
428 if (!NT_SUCCESS(Status))
429 {
430 SHIM_ERR("Unable to open %wZ\n", &KeyName);
431 return FALSE;
432 }
433
434 Status = NtDeleteKey(DbKey);
435 if (!NT_SUCCESS(Status))
436 SHIM_ERR("Unable to delete %wZ\n", &KeyName);
437
438 NtClose(DbKey);
439 return NT_SUCCESS(Status);
440 }
441
442
443 /* kernel32.dll */
444 BOOL WINAPI BaseDumpAppcompatCache(VOID);
445 BOOL WINAPI BaseFlushAppcompatCache(VOID);
446
447
448 /**
449 * @name ShimDumpCache
450 * Dump contents of the shim cache.
451 *
452 * @param hwnd Unused, pass 0
453 * @param hInstance Unused, pass 0
454 * @param lpszCmdLine Unused, pass 0
455 * @param nCmdShow Unused, pass 0
456 * @return
457 */
458 BOOL WINAPI ShimDumpCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
459 {
460 return BaseDumpAppcompatCache();
461 }
462
463 /**
464 * @name ShimFlushCache
465 * Flush the shim cache. Call this after installing a new shim database
466 *
467 * @param hwnd Unused, pass 0
468 * @param hInstance Unused, pass 0
469 * @param lpszCmdLine Unused, pass 0
470 * @param nCmdShow Unused, pass 0
471 * @return
472 */
473 BOOL WINAPI ShimFlushCache(HWND hwnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, int nCmdShow)
474 {
475 return BaseFlushAppcompatCache();
476 }