xref: /reactos/base/applications/sdbinst/sdbinst.c (revision 9393fc32)
1 /*
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)
6  */
7 
8 #include <tchar.h>
9 
10 #include <windef.h>
11 #include <winbase.h>
12 #include <winreg.h>
13 #include <strsafe.h>
14 #include <objbase.h>
15 #include <apphelp.h>
16 #include <shlwapi.h>
17 
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))
26 
27 BOOL SdbUninstallByGuid(_In_ LPWSTR guidSdbStr);
28 
29 HRESULT
RegisterSdbEntry(_In_ PWCHAR sdbEntryName,_In_ LPCWSTR dbGuid,_In_ ULONGLONG time,_In_ BOOL isInstall,_In_ BOOL isExe)30 RegisterSdbEntry(
31     _In_ PWCHAR sdbEntryName,
32     _In_ LPCWSTR dbGuid,
33     _In_ ULONGLONG time,
34     _In_ BOOL isInstall,
35     _In_ BOOL isExe)
36 {
37     WCHAR regName[MAX_PATH];
38     HKEY hKey = NULL;
39     LSTATUS status;
40     HRESULT hres;
41 
42     hres = StringCchPrintfW(regName, MAX_PATH, L"%ls\\%ls",
43                             isExe ? APPCOMPAT_CUSTOM_REG_PATH : APPCOMPAT_LAYERS_REG_PATH,
44                             sdbEntryName);
45     if (FAILED(hres))
46     {
47         wprintf(L"StringCchPrintfW error: 0x%08X\n", hres);
48         goto end;
49     }
50 
51     // Remove key
52     if (!isInstall)
53     {
54         status = RegDeleteKeyW(HKEY_LOCAL_MACHINE, regName);
55         if (status == ERROR_FILE_NOT_FOUND)
56         {
57             status = ERROR_SUCCESS;
58         }
59 
60         return HRESULT_FROM_WIN32(status);
61     }
62 
63     // Create main key
64     status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
65                             regName,
66                             0,
67                             NULL,
68                             REG_OPTION_NON_VOLATILE,
69                             KEY_ALL_ACCESS,
70                             NULL,
71                             &hKey,
72                             NULL);
73     if (status != ERROR_SUCCESS)
74     {
75         wprintf(L"RegKeyCreateEx error: 0x%08X", status);
76         hres = HRESULT_FROM_WIN32(status);
77         goto end;
78     }
79 
80     // Set installed time
81     status = RegSetValueExW(hKey,
82                            dbGuid,
83                            0,
84                            REG_QWORD,
85                            (PBYTE)&time,
86                            sizeof(time));
87     if (status != ERROR_SUCCESS)
88     {
89         wprintf(L"RegSetValueExW error: 0x%08X", status);
90         hres = HRESULT_FROM_WIN32(status);
91         goto end;
92     }
93 
94 end:
95     if (hKey)
96     {
97         RegCloseKey(hKey);
98     }
99 
100     return hres;
101 }
102 
103 HRESULT
AddUninstallKey(_In_ LPCWSTR dbName,_In_ LPCWSTR sdbInstalledPath,_In_ LPCWSTR guidDbStr)104 AddUninstallKey(
105     _In_ LPCWSTR dbName,
106     _In_ LPCWSTR sdbInstalledPath,
107     _In_ LPCWSTR guidDbStr)
108 {
109     WCHAR sdbinstPath[MAX_PATH];
110     WCHAR regName[MAX_PATH];
111     WCHAR uninstString[MAX_PATH];
112     HKEY hKey = NULL;
113     HRESULT hres;
114 
115     UINT count = GetSystemWindowsDirectory(sdbinstPath, MAX_PATH);
116     if (sdbinstPath[count - 1] != L'\\')
117     {
118         hres = StringCchCatW(sdbinstPath, MAX_PATH, L"\\");
119         if (FAILED(hres))
120         {
121             wprintf(L"StringCchCatW error: 0x%08X", hres);
122             goto end;
123         }
124     }
125 
126     // Full path to sdbinst.exe
127     hres = StringCchCatW(sdbinstPath, MAX_PATH, L"System32\\sdbinst.exe");
128     if (FAILED(hres))
129     {
130         wprintf(L"StringCchCatW error: 0x%08X", hres);
131         goto end;
132     }
133 
134     // Sdb GUID registry key
135     hres = StringCchPrintfW(regName, MAX_PATH, L"%ls\\%ls", UNINSTALL_REG_PATH, guidDbStr);
136     if (FAILED(hres))
137     {
138         wprintf(L"StringCchPrintfW error: 0x%08X", hres);
139         goto end;
140     }
141 
142     // Create main key
143     LSTATUS status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
144                                     regName,
145                                     0,
146                                     NULL,
147                                     REG_OPTION_NON_VOLATILE,
148                                     KEY_ALL_ACCESS,
149                                     NULL,
150                                     &hKey,
151                                     NULL);
152 
153     if (status != ERROR_SUCCESS)
154     {
155         wprintf(L"RegKeyCreateEx error: 0x%08X", status);
156         hres = HRESULT_FROM_WIN32(status);
157         goto end;
158     }
159 
160     // Set Display name
161     DWORD length = wcslen(dbName) * sizeof(WCHAR);
162     status = RegSetValueExW(hKey,
163                            L"DisplayName",
164                            0,
165                            REG_SZ,
166                            (PBYTE)dbName,
167                            length + sizeof(WCHAR));
168     if (status != ERROR_SUCCESS)
169     {
170         wprintf(L"RegSetValueExW error: 0x%08X", status);
171         hres = HRESULT_FROM_WIN32(status);
172         goto end;
173     }
174 
175     // Uninstall full string
176     hres = StringCchPrintfW(uninstString, MAX_PATH, L"%ls -u \"%ls\"", sdbinstPath, sdbInstalledPath);
177     if (FAILED(hres))
178     {
179         wprintf(L"StringCchPrintfW error: 0x%08X", hres);
180         goto end;
181     }
182 
183     // Set uninstall string
184     length = wcslen(uninstString) * sizeof(WCHAR);
185     status = RegSetValueExW(hKey,
186                            L"UninstallString",
187                            0,
188                            REG_SZ,
189                            (PBYTE)uninstString,
190                            length + sizeof(WCHAR));
191     if (status != ERROR_SUCCESS)
192     {
193         wprintf(L"RegSetValueExW error: 0x%08X", status);
194         hres = HRESULT_FROM_WIN32(status);
195         goto end;
196     }
197 
198 end:
199     if (hKey)
200     {
201         RegCloseKey(hKey);
202     }
203 
204     return hres;
205 }
206 
207 //
208 // Get database GUID id
209 //
210 BOOL
GetSdbGuid(_In_ PDB pdb,_In_ TAGID tagDb,_Out_ GUID * guid)211 GetSdbGuid(
212     _In_ PDB pdb,
213     _In_ TAGID tagDb,
214     _Out_ GUID* guid)
215 {
216     TAGID tagDbId;
217 
218     tagDbId = SdbFindFirstTag(pdb, tagDb, TAG_DATABASE_ID);
219     if (!tagDbId)
220     {
221         wprintf(L"Can't find database id tag");
222         return FALSE;
223     }
224 
225     if (!SdbReadBinaryTag(pdb, tagDbId, (PBYTE)guid, sizeof(GUID)))
226     {
227         wprintf(L"Can't read database id");
228         return FALSE;
229     }
230 
231     return TRUE;
232 }
233 
234 HRESULT
ProcessLayers(_In_ PDB pdb,_In_ TAGID tagDb,_In_opt_ LPCWSTR guidDbStr,_In_opt_ ULONGLONG time,_In_ BOOL isInstall)235 ProcessLayers(
236     _In_ PDB pdb,
237     _In_ TAGID tagDb,
238     _In_opt_ LPCWSTR guidDbStr,
239     _In_opt_ ULONGLONG time,
240     _In_ BOOL isInstall)
241 {
242     HRESULT res = ERROR_SUCCESS;
243     TAGID tagLayerName;
244     TAGID prevTagLayer = 0;
245 
246     TAGID tagLayer = SdbFindFirstTag(pdb, tagDb, TAG_LAYER);
247 
248     // Add all layers to registry (AppCompatFlags)
249     while (tagLayer && (tagLayer != prevTagLayer))
250     {
251         tagLayerName = SdbFindFirstTag(pdb, tagLayer, TAG_NAME);
252         if (!tagLayerName)
253         {
254             res = ERROR_NOT_FOUND;
255             break;
256         }
257 
258         LPWSTR name = SdbGetStringTagPtr(pdb, tagLayerName);
259 
260         res = RegisterSdbEntry(name, guidDbStr, time, isInstall, FALSE);
261         if (FAILED(res))
262         {
263             wprintf(L"Can't register layer\n");
264             break;
265         }
266 
267         prevTagLayer = tagLayer;
268         tagLayer = SdbFindNextTag(pdb, tagDb, tagLayer);
269     }
270 
271     return res;
272 }
273 
274 HRESULT
ProcessExe(_In_ PDB pdb,_In_ TAGID tagDb,_In_opt_ LPCWSTR guidDbStr,_In_opt_ ULONGLONG time,_In_ BOOL isInstall)275 ProcessExe(
276     _In_ PDB pdb,
277     _In_ TAGID tagDb,
278     _In_opt_ LPCWSTR guidDbStr,
279     _In_opt_ ULONGLONG time,
280     _In_ BOOL isInstall)
281 {
282     HRESULT res = ERROR_SUCCESS;
283     TAGID tagExeName;
284     TAGID tagExePrev = 0;
285 
286     TAGID tagExe = SdbFindFirstTag(pdb, tagDb, TAG_EXE);
287 
288     // Add all exe to registry (AppCompatFlags)
289     while (tagExe != 0 && (tagExe != tagExePrev))
290     {
291         tagExeName = SdbFindFirstTag(pdb, tagExe, TAG_NAME);
292         if (!tagExeName)
293         {
294             wprintf(L"Can't find exe tag\n");
295             res = ERROR_NOT_FOUND;
296             break;
297         }
298 
299         LPWSTR name = SdbGetStringTagPtr(pdb, tagExeName);
300 
301         res = RegisterSdbEntry(name, guidDbStr, time, isInstall, TRUE);
302         if (FAILED(res))
303         {
304             wprintf(L"Can't register exe 0x%08X\n", res);
305             break;
306         }
307 
308         tagExePrev = tagExe;
309         tagExe = SdbFindNextTag(pdb, tagDb, tagExe);
310     }
311 
312     return res;
313 }
314 
315 HRESULT
CopySdbToAppPatch(_In_ LPCWSTR sourceSdbPath,_In_ LPCWSTR destSdbPath)316 CopySdbToAppPatch(
317     _In_ LPCWSTR sourceSdbPath,
318     _In_ LPCWSTR destSdbPath)
319 {
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));
324 
325     if (sysdirPath == NULL)
326     {
327         error = ERROR_NOT_ENOUGH_MEMORY;
328         goto end;
329     }
330 
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)
335     {
336         wprintf(L"Can't find directory separator\n");
337         goto end;
338     }
339     else
340     {
341         *pTmpSysdir = UNICODE_NULL;
342     }
343 
344     // Create directory if need
345     if (!CreateDirectory(sysdirPath, NULL))
346     {
347         error = GetLastError();
348         if (error != ERROR_ALREADY_EXISTS)
349         {
350             wprintf(L"Can't create folder %ls\n Error: 0x%08X\n", sysdirPath, error);
351             goto end;
352         }
353         error = ERROR_SUCCESS;
354     }
355 
356     // Copy file
357     if (!CopyFile(sourceSdbPath, destSdbPath, TRUE))
358     {
359         error = GetLastError();
360         wprintf(L"Can't copy sdb file");
361     }
362 
363 end:
364     if (sysdirPath)
365     {
366         HeapFree(GetProcessHeap(), 0, sysdirPath);
367     }
368 
369     return HRESULT_FROM_WIN32(error);
370 }
371 
372 HRESULT
BuildPathToSdb(_In_ PWCHAR buffer,_In_ SIZE_T bufLen,_In_ LPCWSTR guidDbStr)373 BuildPathToSdb(
374     _In_ PWCHAR buffer,
375     _In_ SIZE_T bufLen,
376     _In_ LPCWSTR guidDbStr)
377 {
378     ZeroMemory(buffer, bufLen * sizeof(WCHAR));
379 
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'\\')
383     {
384         buffer[count] = L'\\';
385     }
386 
387     HRESULT res = StringCchCatW(buffer, bufLen, L"AppPatch\\Custom\\");
388     if (FAILED(res))
389     {
390         goto end;
391     }
392 
393     res = StringCchCatW(buffer, bufLen, guidDbStr);
394 
395 end:
396     return res;
397 }
398 
399 BOOL
SdbInstall(_In_ LPCWSTR sdbPath)400 SdbInstall(
401     _In_ LPCWSTR sdbPath)
402 {
403     BOOL res = FALSE;
404     PDB pdb = NULL;
405     TAGID tagDb;
406     TAGID tagDbName;
407     GUID dbGuid = {0};
408     FILETIME systemTime = {0};
409     ULARGE_INTEGER currentTime = {0};
410     WCHAR sysdirPatchPath[MAX_PATH];
411     WCHAR guidDbStr[GUID_SBD_NAME_LENGTH];
412 
413     GetSystemTimeAsFileTime(&systemTime);
414     currentTime.LowPart  = systemTime.dwLowDateTime;
415     currentTime.HighPart = systemTime.dwHighDateTime;
416 
417     // Open database
418     pdb = SdbOpenDatabase(sdbPath, DOS_PATH);
419     if (pdb == NULL)
420     {
421         wprintf(L"Can't open database %ls\n", sdbPath);
422         goto end;
423     }
424 
425     tagDb = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
426     if (!tagDb)
427     {
428         wprintf(L"Can't find database tag\n");
429         goto end;
430     }
431 
432     // Get database GUID
433     if (!GetSdbGuid(pdb, tagDb, &dbGuid))
434     {
435         wprintf(L"GetSdbGuid error\n");
436         goto end;
437     }
438 
439     StringFromGUID2(&dbGuid, guidDbStr, GUID_SBD_NAME_LENGTH);
440     HRESULT hres = StringCchCatW(guidDbStr, GUID_SBD_NAME_LENGTH, SDB_EXT);
441     if (FAILED(hres))
442     {
443         wprintf(L"StringCchCatW error 0x%08X\n", hres);
444         goto end;
445     }
446 
447     wprintf(L"Database guid %ls\n", guidDbStr);
448 
449     tagDbName = SdbFindFirstTag(pdb, tagDb, TAG_NAME);
450     if (!tagDbName)
451     {
452         wprintf(L"Can't get tag name\n");
453         goto end;
454     }
455 
456     LPWSTR dbName = SdbGetStringTagPtr(pdb, tagDbName);
457     wprintf(L"Database name %ls\n", dbName);
458 
459     // Process exe tags
460     hres = ProcessExe(pdb, tagDb, guidDbStr, currentTime.QuadPart, TRUE);
461     if (FAILED(hres))
462     {
463         wprintf(L"Process exe failed. Status: 0x%08X", res);
464         goto end;
465     }
466 
467     // Proess layer tags
468     hres = ProcessLayers(pdb, tagDb, guidDbStr, currentTime.QuadPart, TRUE);
469     if (FAILED(hres))
470     {
471         wprintf(L"Process layers failed. Status: 0x%08X", res);
472         goto end;
473     }
474 
475     // Create full path to sdb in system folder
476     hres = BuildPathToSdb(sysdirPatchPath, ARRAYSIZE(sysdirPatchPath), guidDbStr);
477     if (FAILED(hres))
478     {
479         wprintf(L"Build path error\n");
480         goto end;
481     }
482 
483     wprintf(L"file path %ls\n", sysdirPatchPath);
484 
485     res = CopySdbToAppPatch(sdbPath, sysdirPatchPath);
486     if (FAILED(res))
487     {
488         wprintf(L"Copy sdb error. Status: 0x%08X\n", res);
489         goto end;
490     }
491 
492     AddUninstallKey(dbName, sysdirPatchPath, guidDbStr);
493 
494     // Registration
495     if (!SdbRegisterDatabaseEx(sysdirPatchPath, SDB_DATABASE_SHIM, &currentTime.QuadPart))
496     {
497         wprintf(L"SdbRegisterDatabaseEx failed");
498         goto end;
499     }
500 
501     res = TRUE;
502 
503 end:
504     if (pdb)
505     {
506         SdbCloseDatabase(pdb);
507     }
508 
509     return res;
510 }
511 
512 HRESULT
DeleteUninstallKey(_In_ LPCWSTR keyName)513 DeleteUninstallKey(
514     _In_ LPCWSTR keyName)
515 {
516     HKEY hKey = NULL;
517     HRESULT hres = HRESULT_FROM_WIN32(ERROR_SUCCESS);
518 
519     LSTATUS status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
520                                     UNINSTALL_REG_PATH,
521                                     0,
522                                     NULL,
523                                     REG_OPTION_NON_VOLATILE,
524                                     KEY_ALL_ACCESS,
525                                     NULL,
526                                     &hKey,
527                                     NULL);
528 
529     if (status != ERROR_SUCCESS)
530     {
531         hres = HRESULT_FROM_WIN32(status);
532         goto end;
533     }
534 
535     status = RegDeleteKeyW(hKey, keyName);
536     if (status != ERROR_SUCCESS)
537     {
538         hres = HRESULT_FROM_WIN32(status);
539     }
540 
541 end:
542     if (hKey)
543     {
544         RegCloseKey(hKey);
545     }
546 
547     return hres;
548 }
549 
550 BOOL
SdbUninstall(_In_ LPWSTR sdbPath)551 SdbUninstall(
552     _In_ LPWSTR sdbPath)
553 {
554     BOOL res = FALSE;
555     PWCHAR sdbName = NULL;
556     PDB pdb;
557     TAGID tagDb;
558     GUID dbGuid = {0};
559     WCHAR guidDbStr[GUID_SBD_NAME_LENGTH];
560 
561     SIZE_T sdbPathLen = wcslen(sdbPath);
562     sdbName = sdbPath + sdbPathLen;
563 
564     wprintf(L"uninstall path %ls\n", sdbPath);
565     sdbName = StrRChrW(sdbPath, sdbPath + sdbPathLen, L'\\');
566     if (sdbName == NULL)
567     {
568         sdbName = sdbPath;
569     }
570     else
571     {
572         sdbName++;
573     }
574 
575     wprintf(L"uninstall name %ls\n", sdbName);
576 
577     // open sdb
578     pdb = SdbOpenDatabase(sdbPath, DOS_PATH);
579     if (pdb == NULL)
580     {
581         wprintf(L"Can't open database %ls\n", sdbPath);
582         return FALSE;
583     }
584 
585     tagDb = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
586     if (!tagDb)
587     {
588         wprintf(L"Can't find database tag\n");
589         goto end;
590     }
591 
592     if (!GetSdbGuid(pdb, tagDb, &dbGuid))
593     {
594         wprintf(L"GetSdbGuid error\n");
595         goto end;
596     }
597 
598     // Database name must be GUID string
599     if (wcslen(sdbName) + 1 != GUID_SBD_NAME_LENGTH)
600     {
601         StringFromGUID2(&dbGuid, guidDbStr, GUID_SBD_NAME_LENGTH);
602         SdbCloseDatabase(pdb);
603         return SdbUninstallByGuid(guidDbStr);
604     }
605 
606     //remove regkey in appatch/custom
607     HRESULT hres = ProcessExe(pdb, tagDb, NULL, 0, FALSE);
608     if (FAILED(hres))
609     {
610         wprintf(L"Process exe fail\n");
611         goto end;
612     }
613 
614     hres = ProcessLayers(pdb, tagDb, NULL, 0, FALSE);
615     if (FAILED(hres))
616     {
617         wprintf(L"Process layers fail\n");
618         goto end;
619     }
620 
621     SdbCloseDatabase(pdb);
622     pdb = NULL;
623 
624     hres = DeleteUninstallKey(sdbName);
625     if (FAILED(hres))
626     {
627         wprintf(L"Remove uninstall key fail\n");
628     }
629 
630     if (!SdbUnregisterDatabase(&dbGuid))
631     {
632         wprintf(L"SdbUnregisterDatabase\n");
633         return FALSE;
634     }
635 
636     SetFileAttributesW(sdbPath, FILE_ATTRIBUTE_NORMAL);
637     if (!DeleteFileW(sdbPath))
638     {
639         wprintf(L"Remove file fail 0x%08X\n", GetLastError());
640         return FALSE;
641     }
642 
643     res = TRUE;
644 
645 end:
646     if (pdb)
647         SdbCloseDatabase(pdb);
648     return res;
649 }
650 
651 BOOL
ValidateGuidString(_In_ PWCHAR guidStr)652 ValidateGuidString(
653     _In_ PWCHAR guidStr)
654 {
655     ULONG length = wcslen(guidStr);
656 
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'-' &&
663         guidStr[24] == L'-')
664     {
665         return TRUE;
666     }
667 
668     return FALSE;
669 }
670 
671 BOOL
SdbUninstallByGuid(_In_ LPWSTR guidSdbStr)672 SdbUninstallByGuid(
673     _In_ LPWSTR guidSdbStr)
674 {
675     BOOL res = FALSE;
676     HKEY hKey = NULL;
677     HKEY guidKey = NULL;
678     LSTATUS status;
679     WCHAR dbPath[MAX_PATH];
680     DWORD keyValSize = sizeof(dbPath);
681 
682     if (!ValidateGuidString(guidSdbStr))
683     {
684         wprintf(L"Invalid GUID: %ls\n", guidSdbStr);
685         return res;
686     }
687 
688     status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, APPCOMPAT_INSTALLEDSDB_REG_PATH, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
689 
690     if (status != ERROR_SUCCESS)
691     {
692         wprintf(L"RegOpenKeyW error: 0x%08X", status);
693         goto end;
694     }
695 
696     status = RegOpenKeyExW(hKey, guidSdbStr, 0, KEY_READ | KEY_QUERY_VALUE, &guidKey);
697 
698     if (status != ERROR_SUCCESS)
699     {
700         wprintf(L"Cant open key: 0x%08X %ls\n", status, guidSdbStr);
701         goto end;
702     }
703 
704     status = RegQueryValueExW(guidKey, DBPATH_KEY_NAME, NULL, NULL, (LPBYTE)dbPath, &keyValSize);
705     if (status != ERROR_SUCCESS)
706     {
707         wprintf(L"RegQueryValueExW: 0x%08X\n", status);
708         goto end;
709     }
710 
711     res = SdbUninstall(dbPath);
712 
713 end:
714     if (hKey)
715     {
716         RegCloseKey(hKey);
717     }
718 
719     if (guidKey)
720     {
721         RegCloseKey(guidKey);
722     }
723 
724     return res;
725 }
726 
727 BOOL
SdbUninstallByName(_In_ LPWSTR nameSdbStr)728 SdbUninstallByName(
729     _In_ LPWSTR nameSdbStr)
730 {
731     BOOL res = FALSE;
732     LSTATUS status;
733     HKEY hKey = NULL;
734     HKEY subKey = NULL;
735     DWORD index = 0;
736     WCHAR keyName[MAX_PATH];
737     DWORD keyNameLen = ARRAYSIZE(keyName);
738     DWORD keyValSize;
739     WCHAR dbDescript[MAX_PATH];
740     WCHAR dbPath[MAX_PATH];
741 
742     status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, APPCOMPAT_INSTALLEDSDB_REG_PATH, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
743 
744     if (status != ERROR_SUCCESS)
745     {
746         wprintf(L"RegOpenKeyW error: 0x%08X", status);
747         goto end;
748     }
749 
750     status = RegEnumKeyEx(hKey, index, keyName, &keyNameLen, NULL, NULL, NULL, NULL);
751     wprintf(L"0x%08X %d %ls \n", status, keyNameLen, keyName);
752 
753     // Search database GUID by name
754     while (status == ERROR_SUCCESS)
755     {
756         status = RegOpenKeyExW(hKey, keyName, 0, KEY_READ | KEY_QUERY_VALUE, &subKey);
757         if (status != ERROR_SUCCESS)
758         {
759             break;
760         }
761 
762         keyValSize = sizeof(dbDescript);
763         status = RegQueryValueExW(subKey, L"DatabaseDescription", NULL, NULL, (LPBYTE)dbDescript, &keyValSize);
764         if (status != ERROR_SUCCESS)
765         {
766             break;
767         }
768 
769         wprintf(L"dbdescript: %ls \n", dbDescript);
770 
771         if (_wcsnicmp(dbDescript, nameSdbStr, keyNameLen) == 0)
772         {
773             // Take db full path
774             keyValSize = sizeof(dbPath);
775             status = RegQueryValueExW(subKey, DBPATH_KEY_NAME, NULL, NULL, (LPBYTE)dbPath, &keyValSize);
776             if (status != ERROR_SUCCESS)
777             {
778                 dbPath[0] = UNICODE_NULL;
779                 break;
780             }
781 
782             wprintf(L"dbpath: 0x%08X %ls \n", status, dbPath);
783             RegCloseKey(subKey);
784             break;
785         }
786 
787         RegCloseKey(subKey);
788 
789         keyName[0] = UNICODE_NULL;
790 
791         ++index;
792         keyNameLen = ARRAYSIZE(keyName);
793         status = RegEnumKeyExW(hKey, index, keyName, &keyNameLen, NULL, NULL, NULL, NULL);
794     }
795 
796     RegCloseKey(hKey);
797 
798     if (dbPath[0] != UNICODE_NULL)
799     {
800         res = SdbUninstall(dbPath);
801     }
802 
803 end:
804     return res;
805 }
806 
807 
808 void
ShowHelp()809 ShowHelp()
810 {
811     /* FIXME: to be localized */
812     wprintf(L"Using: sdbinst [-?][-q][-u][-g][-n] foo.sdb | {guid} | \"name\" \n"
813             L"-? - show help\n"
814             L"-u - uninstall\n"
815             L"-g - {guid} file GUID (only uninstall)\n"
816             L"-n - \"name\" - file name (only uninstall)\n");
817 }
818 
_tmain(int argc,LPWSTR argv[])819 int _tmain(int argc, LPWSTR argv[])
820 {
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;
828 
829     if (argc < 2)
830     {
831         ShowHelp();
832     }
833 
834     for (int i = 1; i < argc; ++i)
835     {
836         if (argv[i][0] != L'-')
837         {
838             sdbPath = argv[i];
839             break;
840         }
841 
842         switch (argv[i][1])
843         {
844             case L'?':
845                 ShowHelp();
846                 return ERROR_SUCCESS;
847             break;
848 
849             case L'u':
850                 isInstall = FALSE;
851             break;
852 
853             case L'g':
854                 ++i;
855 
856                 if (i >= argc)
857                 {
858                     return ERROR_INVALID_PARAMETER;
859                 }
860 
861                 guidSdbStr = argv[i];
862                 wprintf(L"guidSdbStr %ls\n", guidSdbStr);
863 
864                 isUninstByGuid = TRUE;
865                 isInstall = FALSE;
866             break;
867 
868             case L'n':
869                 ++i;
870 
871                 if (i >= argc)
872                 {
873                     return ERROR_INVALID_PARAMETER;
874                 }
875 
876                 nameSdbStr = argv[i];
877                 wprintf(L"nameSdbStr %ls\n", nameSdbStr);
878 
879                 isUninstByName = TRUE;
880                 isInstall = FALSE;
881             break;
882         }
883     }
884 
885     if (isInstall)
886     {
887         wprintf(L"install\n");
888         success = SdbInstall(sdbPath);
889     }
890     else if (isUninstByGuid)
891     {
892         wprintf(L"uninstall by GUID\n");
893         success = SdbUninstallByGuid(guidSdbStr);
894     }
895     else if (isUninstByName)
896     {
897         wprintf(L"uninstall by name\n");
898         success = SdbUninstallByName(nameSdbStr);
899     }
900     else
901     {
902         wprintf(L"uninstall\n");
903         success = SdbUninstall(sdbPath);
904     }
905 
906     if (!success)
907     {
908         wprintf(isInstall ? L"Sdb install failed\n" : L"Sdb uninstall failed\n");
909         return -1;
910     }
911 
912     return ERROR_SUCCESS;
913 }
914