2 * PROJECT: ReactOS sdbinst
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Application compatibility database installer
5 * COPYRIGHT: Copyright 2020 Max Korostil (mrmks04@yandex.ru)
18 #define APPCOMPAT_CUSTOM_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom"
19 #define APPCOMPAT_LAYERS_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom\\Layers"
20 #define APPCOMPAT_INSTALLEDSDB_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB"
21 #define UNINSTALL_REG_PATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
22 #define DBPATH_KEY_NAME L"DatabasePath"
23 #define SDB_EXT L".sdb"
24 #define GUID_STR_LENGTH 38
25 #define GUID_SBD_NAME_LENGTH (GUID_STR_LENGTH + ARRAYSIZE(SDB_EXT))
27 BOOL
SdbUninstallByGuid(_In_ LPWSTR guidSdbStr
);
31 _In_ PWCHAR sdbEntryName
,
37 WCHAR regName
[MAX_PATH
];
42 hres
= StringCchPrintfW(regName
, MAX_PATH
, L
"%ls\\%ls",
43 isExe
? APPCOMPAT_CUSTOM_REG_PATH
: APPCOMPAT_LAYERS_REG_PATH
,
47 wprintf(L
"StringCchPrintfW error: 0x%08X\n", hres
);
54 status
= RegDeleteKeyW(HKEY_LOCAL_MACHINE
, regName
);
55 if (status
== ERROR_FILE_NOT_FOUND
)
57 status
= ERROR_SUCCESS
;
60 return HRESULT_FROM_WIN32(status
);
64 status
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
,
68 REG_OPTION_NON_VOLATILE
,
73 if (status
!= ERROR_SUCCESS
)
75 wprintf(L
"RegKeyCreateEx error: 0x%08X", status
);
76 hres
= HRESULT_FROM_WIN32(status
);
81 status
= RegSetValueExW(hKey
,
87 if (status
!= ERROR_SUCCESS
)
89 wprintf(L
"RegSetValueExW error: 0x%08X", status
);
90 hres
= HRESULT_FROM_WIN32(status
);
106 _In_ LPCWSTR sdbInstalledPath
,
107 _In_ LPCWSTR guidDbStr
)
109 WCHAR sdbinstPath
[MAX_PATH
];
110 WCHAR regName
[MAX_PATH
];
111 WCHAR uninstString
[MAX_PATH
];
115 UINT count
= GetSystemWindowsDirectory(sdbinstPath
, MAX_PATH
);
116 if (sdbinstPath
[count
- 1] != L
'\\')
118 hres
= StringCchCatW(sdbinstPath
, MAX_PATH
, L
"\\");
121 wprintf(L
"StringCchCatW error: 0x%08X", hres
);
126 // Full path to sdbinst.exe
127 hres
= StringCchCatW(sdbinstPath
, MAX_PATH
, L
"System32\\sdbinst.exe");
130 wprintf(L
"StringCchCatW error: 0x%08X", hres
);
134 // Sdb GUID registry key
135 hres
= StringCchPrintfW(regName
, MAX_PATH
, L
"%ls\\%ls", UNINSTALL_REG_PATH
, guidDbStr
);
138 wprintf(L
"StringCchPrintfW error: 0x%08X", hres
);
143 LSTATUS status
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
,
147 REG_OPTION_NON_VOLATILE
,
153 if (status
!= ERROR_SUCCESS
)
155 wprintf(L
"RegKeyCreateEx error: 0x%08X", status
);
156 hres
= HRESULT_FROM_WIN32(status
);
161 DWORD length
= wcslen(dbName
) * sizeof(WCHAR
);
162 status
= RegSetValueExW(hKey
,
167 length
+ sizeof(WCHAR
));
168 if (status
!= ERROR_SUCCESS
)
170 wprintf(L
"RegSetValueExW error: 0x%08X", status
);
171 hres
= HRESULT_FROM_WIN32(status
);
175 // Uninstall full string
176 hres
= StringCchPrintfW(uninstString
, MAX_PATH
, L
"%ls -u \"%ls\"", sdbinstPath
, sdbInstalledPath
);
179 wprintf(L
"StringCchPrintfW error: 0x%08X", hres
);
183 // Set uninstall string
184 length
= wcslen(uninstString
) * sizeof(WCHAR
);
185 status
= RegSetValueExW(hKey
,
190 length
+ sizeof(WCHAR
));
191 if (status
!= ERROR_SUCCESS
)
193 wprintf(L
"RegSetValueExW error: 0x%08X", status
);
194 hres
= HRESULT_FROM_WIN32(status
);
208 // Get database GUID id
218 tagDbId
= SdbFindFirstTag(pdb
, tagDb
, TAG_DATABASE_ID
);
221 wprintf(L
"Can't find database id tag");
225 if (!SdbReadBinaryTag(pdb
, tagDbId
, (PBYTE
)guid
, sizeof(GUID
)))
227 wprintf(L
"Can't read database id");
238 _In_opt_ LPCWSTR guidDbStr
,
239 _In_opt_ ULONGLONG time
,
242 HRESULT res
= ERROR_SUCCESS
;
244 TAGID prevTagLayer
= 0;
246 TAGID tagLayer
= SdbFindFirstTag(pdb
, tagDb
, TAG_LAYER
);
248 // Add all layers to registry (AppCompatFlags)
249 while (tagLayer
&& (tagLayer
!= prevTagLayer
))
251 tagLayerName
= SdbFindFirstTag(pdb
, tagLayer
, TAG_NAME
);
254 res
= ERROR_NOT_FOUND
;
258 LPWSTR name
= SdbGetStringTagPtr(pdb
, tagLayerName
);
260 res
= RegisterSdbEntry(name
, guidDbStr
, time
, isInstall
, FALSE
);
263 wprintf(L
"Can't register layer\n");
267 prevTagLayer
= tagLayer
;
268 tagLayer
= SdbFindNextTag(pdb
, tagDb
, tagLayer
);
278 _In_opt_ LPCWSTR guidDbStr
,
279 _In_opt_ ULONGLONG time
,
282 HRESULT res
= ERROR_SUCCESS
;
284 TAGID tagExePrev
= 0;
286 TAGID tagExe
= SdbFindFirstTag(pdb
, tagDb
, TAG_EXE
);
288 // Add all exe to registry (AppCompatFlags)
289 while (tagExe
!= 0 && (tagExe
!= tagExePrev
))
291 tagExeName
= SdbFindFirstTag(pdb
, tagExe
, TAG_NAME
);
294 wprintf(L
"Can't find exe tag\n");
295 res
= ERROR_NOT_FOUND
;
299 LPWSTR name
= SdbGetStringTagPtr(pdb
, tagExeName
);
301 res
= RegisterSdbEntry(name
, guidDbStr
, time
, isInstall
, TRUE
);
304 wprintf(L
"Can't register exe 0x%08X\n", res
);
309 tagExe
= SdbFindNextTag(pdb
, tagDb
, tagExe
);
317 _In_ LPCWSTR sourceSdbPath
,
318 _In_ LPCWSTR destSdbPath
)
320 DWORD error
= ERROR_SUCCESS
;
321 PWCHAR pTmpSysdir
= NULL
;
322 SIZE_T destLen
= wcslen(destSdbPath
);
323 PWCHAR sysdirPath
= (PWCHAR
)HeapAlloc(GetProcessHeap(), 0, destLen
* sizeof(WCHAR
));
325 if (sysdirPath
== NULL
)
327 error
= ERROR_NOT_ENOUGH_MEMORY
;
331 // Get parent folder fo sdb file
332 CopyMemory(sysdirPath
, destSdbPath
, destLen
* sizeof(WCHAR
));
333 pTmpSysdir
= StrRChrW(sysdirPath
, sysdirPath
+ destLen
, L
'\\');
334 if (pTmpSysdir
== NULL
)
336 wprintf(L
"Can't find directory separator\n");
341 *pTmpSysdir
= UNICODE_NULL
;
344 // Create directory if need
345 if (!CreateDirectory(sysdirPath
, NULL
))
347 error
= GetLastError();
348 if (error
!= ERROR_ALREADY_EXISTS
)
350 wprintf(L
"Can't create folder %ls\n Error: 0x%08X\n", sysdirPath
, error
);
353 error
= ERROR_SUCCESS
;
357 if (!CopyFile(sourceSdbPath
, destSdbPath
, TRUE
))
359 error
= GetLastError();
360 wprintf(L
"Can't copy sdb file");
366 HeapFree(GetProcessHeap(), 0, sysdirPath
);
369 return HRESULT_FROM_WIN32(error
);
376 _In_ LPCWSTR guidDbStr
)
378 ZeroMemory(buffer
, bufLen
* sizeof(WCHAR
));
380 // Can't use here SdbGetAppPatchDir, because Windows XP haven't this function
381 UINT count
= GetSystemWindowsDirectory(buffer
, bufLen
);
382 if (buffer
[count
- 1] != L
'\\')
384 buffer
[count
] = L
'\\';
387 HRESULT res
= StringCchCatW(buffer
, bufLen
, L
"AppPatch\\Custom\\");
393 res
= StringCchCatW(buffer
, bufLen
, guidDbStr
);
401 _In_ LPCWSTR sdbPath
)
408 FILETIME systemTime
= {0};
409 ULARGE_INTEGER currentTime
= {0};
410 WCHAR sysdirPatchPath
[MAX_PATH
];
411 WCHAR guidDbStr
[GUID_SBD_NAME_LENGTH
];
413 GetSystemTimeAsFileTime(&systemTime
);
414 currentTime
.LowPart
= systemTime
.dwLowDateTime
;
415 currentTime
.HighPart
= systemTime
.dwHighDateTime
;
418 pdb
= SdbOpenDatabase(sdbPath
, DOS_PATH
);
421 wprintf(L
"Can't open database %ls\n", sdbPath
);
425 tagDb
= SdbFindFirstTag(pdb
, TAGID_ROOT
, TAG_DATABASE
);
428 wprintf(L
"Can't find database tag\n");
433 if (!GetSdbGuid(pdb
, tagDb
, &dbGuid
))
435 wprintf(L
"GetSdbGuid error\n");
439 StringFromGUID2(&dbGuid
, guidDbStr
, GUID_SBD_NAME_LENGTH
);
440 HRESULT hres
= StringCchCatW(guidDbStr
, GUID_SBD_NAME_LENGTH
, SDB_EXT
);
443 wprintf(L
"StringCchCatW error 0x%08X\n", hres
);
447 wprintf(L
"Database guid %ls\n", guidDbStr
);
449 tagDbName
= SdbFindFirstTag(pdb
, tagDb
, TAG_NAME
);
452 wprintf(L
"Can't get tag name\n");
456 LPWSTR dbName
= SdbGetStringTagPtr(pdb
, tagDbName
);
457 wprintf(L
"Database name %ls\n", dbName
);
460 hres
= ProcessExe(pdb
, tagDb
, guidDbStr
, currentTime
.QuadPart
, TRUE
);
463 wprintf(L
"Process exe failed. Status: 0x%08X", res
);
468 hres
= ProcessLayers(pdb
, tagDb
, guidDbStr
, currentTime
.QuadPart
, TRUE
);
471 wprintf(L
"Process layers failed. Status: 0x%08X", res
);
475 // Create full path to sdb in system folder
476 hres
= BuildPathToSdb(sysdirPatchPath
, ARRAYSIZE(sysdirPatchPath
), guidDbStr
);
479 wprintf(L
"Build path error\n");
483 wprintf(L
"file path %ls\n", sysdirPatchPath
);
485 res
= CopySdbToAppPatch(sdbPath
, sysdirPatchPath
);
488 wprintf(L
"Copy sdb error. Status: 0x%08X\n", res
);
492 AddUninstallKey(dbName
, sysdirPatchPath
, guidDbStr
);
495 if (!SdbRegisterDatabaseEx(sysdirPatchPath
, SDB_DATABASE_SHIM
, ¤tTime
.QuadPart
))
497 wprintf(L
"SdbRegisterDatabaseEx failed");
506 SdbCloseDatabase(pdb
);
514 _In_ LPCWSTR keyName
)
517 HRESULT hres
= HRESULT_FROM_WIN32(ERROR_SUCCESS
);
519 LSTATUS status
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
,
523 REG_OPTION_NON_VOLATILE
,
529 if (status
!= ERROR_SUCCESS
)
531 hres
= HRESULT_FROM_WIN32(status
);
535 status
= RegDeleteKeyW(hKey
, keyName
);
536 if (status
!= ERROR_SUCCESS
)
538 hres
= HRESULT_FROM_WIN32(status
);
555 PWCHAR sdbName
= NULL
;
559 WCHAR guidDbStr
[GUID_SBD_NAME_LENGTH
];
561 SIZE_T sdbPathLen
= wcslen(sdbPath
);
562 sdbName
= sdbPath
+ sdbPathLen
;
564 wprintf(L
"uninstall path %ls\n", sdbPath
);
565 sdbName
= StrRChrW(sdbPath
, sdbPath
+ sdbPathLen
, L
'\\');
575 wprintf(L
"uninstall name %ls\n", sdbName
);
578 pdb
= SdbOpenDatabase(sdbPath
, DOS_PATH
);
581 wprintf(L
"Can't open database %ls\n", sdbPath
);
585 tagDb
= SdbFindFirstTag(pdb
, TAGID_ROOT
, TAG_DATABASE
);
588 wprintf(L
"Can't find database tag\n");
592 if (!GetSdbGuid(pdb
, tagDb
, &dbGuid
))
594 wprintf(L
"GetSdbGuid error\n");
598 // Database name must be GUID string
599 if (wcslen(sdbName
) + 1 != GUID_SBD_NAME_LENGTH
)
601 StringFromGUID2(&dbGuid
, guidDbStr
, GUID_SBD_NAME_LENGTH
);
602 SdbCloseDatabase(pdb
);
603 return SdbUninstallByGuid(guidDbStr
);
606 //remove regkey in appatch/custom
607 HRESULT hres
= ProcessExe(pdb
, tagDb
, NULL
, 0, FALSE
);
610 wprintf(L
"Process exe fail\n");
614 hres
= ProcessLayers(pdb
, tagDb
, NULL
, 0, FALSE
);
617 wprintf(L
"Process layers fail\n");
621 SdbCloseDatabase(pdb
);
624 hres
= DeleteUninstallKey(sdbName
);
627 wprintf(L
"Remove uninstall key fail\n");
630 if (!SdbUnregisterDatabase(&dbGuid
))
632 wprintf(L
"SdbUnregisterDatabase\n");
636 SetFileAttributesW(sdbPath
, FILE_ATTRIBUTE_NORMAL
);
637 if (!DeleteFileW(sdbPath
))
639 wprintf(L
"Remove file fail 0x%08X\n", GetLastError());
647 SdbCloseDatabase(pdb
);
655 ULONG length
= wcslen(guidStr
);
657 if (length
== GUID_STR_LENGTH
&&
658 guidStr
[0] == L
'{' &&
659 guidStr
[GUID_STR_LENGTH
- 1] == L
'}' &&
660 guidStr
[9] == L
'-' &&
661 guidStr
[14] == L
'-' &&
662 guidStr
[19] == L
'-' &&
673 _In_ LPWSTR guidSdbStr
)
679 WCHAR dbPath
[MAX_PATH
];
680 DWORD keyValSize
= sizeof(dbPath
);
682 if (!ValidateGuidString(guidSdbStr
))
684 wprintf(L
"Invalid GUID: %ls\n", guidSdbStr
);
688 status
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, APPCOMPAT_INSTALLEDSDB_REG_PATH
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
690 if (status
!= ERROR_SUCCESS
)
692 wprintf(L
"RegOpenKeyW error: 0x%08X", status
);
696 status
= RegOpenKeyExW(hKey
, guidSdbStr
, 0, KEY_READ
| KEY_QUERY_VALUE
, &guidKey
);
698 if (status
!= ERROR_SUCCESS
)
700 wprintf(L
"Cant open key: 0x%08X %ls\n", status
, guidSdbStr
);
704 status
= RegQueryValueExW(guidKey
, DBPATH_KEY_NAME
, NULL
, NULL
, (LPBYTE
)dbPath
, &keyValSize
);
705 if (status
!= ERROR_SUCCESS
)
707 wprintf(L
"RegQueryValueExW: 0x%08X\n", status
);
711 res
= SdbUninstall(dbPath
);
721 RegCloseKey(guidKey
);
729 _In_ LPWSTR nameSdbStr
)
736 WCHAR keyName
[MAX_PATH
];
737 DWORD keyNameLen
= ARRAYSIZE(keyName
);
739 WCHAR dbDescript
[MAX_PATH
];
740 WCHAR dbPath
[MAX_PATH
];
742 status
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, APPCOMPAT_INSTALLEDSDB_REG_PATH
, 0, KEY_READ
| KEY_QUERY_VALUE
, &hKey
);
744 if (status
!= ERROR_SUCCESS
)
746 wprintf(L
"RegOpenKeyW error: 0x%08X", status
);
750 status
= RegEnumKeyEx(hKey
, index
, keyName
, &keyNameLen
, NULL
, NULL
, NULL
, NULL
);
751 wprintf(L
"0x%08X %d %ls \n", status
, keyNameLen
, keyName
);
753 // Search database GUID by name
754 while (status
== ERROR_SUCCESS
)
756 status
= RegOpenKeyExW(hKey
, keyName
, 0, KEY_READ
| KEY_QUERY_VALUE
, &subKey
);
757 if (status
!= ERROR_SUCCESS
)
762 keyValSize
= sizeof(dbDescript
);
763 status
= RegQueryValueExW(subKey
, L
"DatabaseDescription", NULL
, NULL
, (LPBYTE
)dbDescript
, &keyValSize
);
764 if (status
!= ERROR_SUCCESS
)
769 wprintf(L
"dbdescript: %ls \n", dbDescript
);
771 if (_wcsnicmp(dbDescript
, nameSdbStr
, keyNameLen
) == 0)
774 keyValSize
= sizeof(dbPath
);
775 status
= RegQueryValueExW(subKey
, DBPATH_KEY_NAME
, NULL
, NULL
, (LPBYTE
)dbPath
, &keyValSize
);
776 if (status
!= ERROR_SUCCESS
)
778 dbPath
[0] = UNICODE_NULL
;
782 wprintf(L
"dbpath: 0x%08X %ls \n", status
, dbPath
);
789 keyName
[0] = UNICODE_NULL
;
792 keyNameLen
= ARRAYSIZE(keyName
);
793 status
= RegEnumKeyExW(hKey
, index
, keyName
, &keyNameLen
, NULL
, NULL
, NULL
, NULL
);
798 if (dbPath
[0] != UNICODE_NULL
)
800 res
= SdbUninstall(dbPath
);
811 /* FIXME: to be localized */
812 wprintf(L
"Using: sdbinst [-?][-q][-u][-g][-n] foo.sdb | {guid} | \"name\" \n"
815 L
"-g - {guid} file GUID (only uninstall)\n"
816 L
"-n - \"name\" - file name (only uninstall)\n");
819 int _tmain(int argc
, LPWSTR argv
[])
821 LPWSTR sdbPath
= NULL
;
822 BOOL isInstall
= TRUE
;
823 BOOL isUninstByGuid
= FALSE
;
824 BOOL isUninstByName
= FALSE
;
825 BOOL success
= FALSE
;
826 LPWSTR guidSdbStr
= NULL
;
827 LPWSTR nameSdbStr
= NULL
;
834 for (int i
= 1; i
< argc
; ++i
)
836 if (argv
[i
][0] != L
'-')
846 return ERROR_SUCCESS
;
858 return ERROR_INVALID_PARAMETER
;
861 guidSdbStr
= argv
[i
];
862 wprintf(L
"guidSdbStr %ls\n", guidSdbStr
);
864 isUninstByGuid
= TRUE
;
873 return ERROR_INVALID_PARAMETER
;
876 nameSdbStr
= argv
[i
];
877 wprintf(L
"nameSdbStr %ls\n", nameSdbStr
);
879 isUninstByName
= TRUE
;
887 wprintf(L
"install\n");
888 success
= SdbInstall(sdbPath
);
890 else if (isUninstByGuid
)
892 wprintf(L
"uninstall by GUID\n");
893 success
= SdbUninstallByGuid(guidSdbStr
);
895 else if (isUninstByName
)
897 wprintf(L
"uninstall by name\n");
898 success
= SdbUninstallByName(nameSdbStr
);
902 wprintf(L
"uninstall\n");
903 success
= SdbUninstall(sdbPath
);
908 wprintf(isInstall
? L
"Sdb install failed\n" : L
"Sdb uninstall failed\n");
912 return ERROR_SUCCESS
;