2020-10-25 01:03:34 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS sdbinst
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: Application compatibility database installer
|
|
|
|
* COPYRIGHT: Copyright 2020 Max Korostil (mrmks04@yandex.ru)
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-10-21 20:11:35 +00:00
|
|
|
#include <windef.h>
|
|
|
|
#include <winbase.h>
|
|
|
|
#include <tchar.h>
|
|
|
|
#include <winreg.h>
|
|
|
|
#include <strsafe.h>
|
|
|
|
#include <objbase.h>
|
|
|
|
#include <apphelp.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
#define APPCOMPAT_CUSTOM_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom"
|
|
|
|
#define APPCOMPAT_LAYERS_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"
|
2020-10-25 01:03:34 +00:00
|
|
|
#define APPCOMPAT_INSTALLEDSDB_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\InstalledSDB"
|
2020-10-21 20:11:35 +00:00
|
|
|
#define UNINSTALL_REG_PATH L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
2020-10-25 01:03:34 +00:00
|
|
|
#define DBPATH_KEY_NAME L"DatabasePath"
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
HRESULT
|
|
|
|
RegisterSdbEntry(
|
|
|
|
_In_ PWCHAR sdbEntryName,
|
|
|
|
_In_ LPCWSTR dbGuid,
|
2020-10-22 19:45:20 +00:00
|
|
|
_In_ ULONGLONG time,
|
|
|
|
_In_ BOOL isInstall,
|
|
|
|
_In_ BOOL isExe)
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
2020-10-25 01:03:34 +00:00
|
|
|
PWCHAR regName;
|
|
|
|
HKEY hKey = NULL;
|
2020-10-22 19:45:20 +00:00
|
|
|
LSTATUS status;
|
2020-10-25 01:03:34 +00:00
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
regName = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (!regName)
|
|
|
|
{
|
|
|
|
hres = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
ZeroMemory(regName, MAX_PATH * sizeof(WCHAR));
|
2020-10-21 20:11:35 +00:00
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
hres = StringCchPrintf(regName, MAX_PATH, L"%ls\\%ls",
|
2020-10-22 19:45:20 +00:00
|
|
|
isExe ? APPCOMPAT_CUSTOM_REG_PATH : APPCOMPAT_LAYERS_REG_PATH, sdbEntryName);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
2020-10-22 19:45:20 +00:00
|
|
|
wprintf(L"StringCchPrintfW error: 0x%08X\n", hres);
|
2020-10-21 20:11:35 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Remove key
|
|
|
|
if (!isInstall)
|
|
|
|
{
|
|
|
|
status = RegDeleteKey(HKEY_LOCAL_MACHINE, regName);
|
|
|
|
return HRESULT_FROM_WIN32(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create main key
|
|
|
|
status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
|
|
regName,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS,
|
|
|
|
NULL,
|
|
|
|
&hKey,
|
|
|
|
NULL);
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegKeyCreateEx error: 0x%08X", status);
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Set instlled time
|
2020-10-21 20:11:35 +00:00
|
|
|
status = RegSetValueEx(hKey,
|
|
|
|
dbGuid,
|
|
|
|
0,
|
|
|
|
REG_QWORD,
|
|
|
|
(PBYTE)&time,
|
|
|
|
sizeof(time));
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegSetValueEx error: 0x%08X", status);
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
2020-10-25 01:03:34 +00:00
|
|
|
if (hKey)
|
|
|
|
{
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (regName)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, regName);
|
|
|
|
}
|
|
|
|
|
2020-10-21 20:11:35 +00:00
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
AddUninstallKey(
|
|
|
|
_In_ LPCWSTR dbName,
|
|
|
|
_In_ LPCWSTR sdbInstalledPath,
|
|
|
|
_In_ LPCWSTR guidDbStr)
|
|
|
|
{
|
2020-10-25 01:03:34 +00:00
|
|
|
PWCHAR sdbinstPath;
|
|
|
|
PWCHAR regName;
|
|
|
|
PWCHAR uninstString;
|
|
|
|
HKEY hKey = NULL;
|
2020-10-21 20:11:35 +00:00
|
|
|
HRESULT hres;
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
sdbinstPath = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
|
|
|
|
regName = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
|
|
|
|
uninstString = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
|
|
|
|
|
|
|
|
if (!sdbinstPath || !regName || !uninstString)
|
|
|
|
{
|
|
|
|
hres = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZeroMemory(sdbinstPath, MAX_PATH * sizeof(WCHAR));
|
|
|
|
ZeroMemory(regName, MAX_PATH * sizeof(WCHAR));
|
|
|
|
ZeroMemory(uninstString, MAX_PATH * sizeof(WCHAR));
|
|
|
|
|
|
|
|
UINT count = GetSystemWindowsDirectory(sdbinstPath, MAX_PATH);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (sdbinstPath[count - 1] != L'\\')
|
|
|
|
{
|
|
|
|
sdbinstPath[count] = L'\\';
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Full path to sdbinst.exe
|
2020-10-25 01:03:34 +00:00
|
|
|
hres = StringCchCat(sdbinstPath, MAX_PATH, L"System32\\sdbinst.exe");
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"StringCchCat error: 0x%08X", hres);
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Sdb guid reg key
|
2020-10-25 01:03:34 +00:00
|
|
|
hres = StringCchPrintf(regName, MAX_PATH, L"%ls\\%ls", UNINSTALL_REG_PATH, guidDbStr);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"StringCchPrintfW error: 0x%08X", hres);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
wprintf(L"%ls\n", sdbinstPath);
|
|
|
|
wprintf(L"%ls\n", regName);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Create main key
|
2020-10-21 20:11:35 +00:00
|
|
|
LSTATUS status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
|
|
regName,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS,
|
|
|
|
NULL,
|
|
|
|
&hKey,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegKeyCreateEx error: 0x%08X", status);
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Set Display name
|
2020-10-21 20:11:35 +00:00
|
|
|
DWORD length = wcslen(dbName) * sizeof(WCHAR);
|
|
|
|
status = RegSetValueEx(hKey,
|
|
|
|
L"DisplayName",
|
|
|
|
0,
|
|
|
|
REG_SZ,
|
|
|
|
(PBYTE)dbName,
|
|
|
|
length + sizeof(WCHAR));
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegSetValueEx error: 0x%08X", status);
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Uninstall full string
|
2020-10-25 01:03:34 +00:00
|
|
|
hres = StringCchPrintf(uninstString, MAX_PATH, L"%ls -u \"%ls\"", sdbinstPath, sdbInstalledPath);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"StringCchPrintfW error: 0x%08X", hres);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Set uninstall string
|
2020-10-21 20:11:35 +00:00
|
|
|
length = wcslen(uninstString) * sizeof(WCHAR);
|
|
|
|
status = RegSetValueEx(hKey,
|
|
|
|
L"UninstallString",
|
|
|
|
0,
|
|
|
|
REG_SZ,
|
|
|
|
(PBYTE)uninstString,
|
|
|
|
length + sizeof(WCHAR));
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegSetValueEx error: 0x%08X", status);
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
2020-10-25 01:03:34 +00:00
|
|
|
if (hKey)
|
|
|
|
{
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sdbinstPath)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, sdbinstPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (regName)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, regName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uninstString)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, uninstString);
|
|
|
|
}
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get database guid id
|
|
|
|
//
|
|
|
|
BOOL
|
|
|
|
GetSdbGuid(
|
|
|
|
_In_ PDB pdb,
|
|
|
|
_In_ TAGID tagDb,
|
|
|
|
_Out_ GUID* guid)
|
|
|
|
{
|
|
|
|
TAGID tagDbId;
|
|
|
|
|
|
|
|
tagDbId = SdbFindFirstTag(pdb, tagDb, TAG_DATABASE_ID);
|
|
|
|
|
|
|
|
if (!tagDbId)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't find database id tag");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SdbReadBinaryTag(pdb, tagDbId, (PBYTE)guid, sizeof(GUID)))
|
|
|
|
{
|
|
|
|
wprintf(L"Can't read database id");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
ProcessLayers(
|
|
|
|
_In_ PDB pdb,
|
|
|
|
_In_ TAGID tagDb,
|
2020-10-22 19:45:20 +00:00
|
|
|
_In_opt_ LPCWSTR guidDbStr,
|
|
|
|
_In_opt_ ULONGLONG time,
|
|
|
|
_In_ BOOL isInstall)
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
|
|
|
HRESULT res = ERROR_SUCCESS;
|
|
|
|
TAGID tagLayerName;
|
2020-10-22 19:45:20 +00:00
|
|
|
TAGID prevTagLayer = 0;
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
TAGID tagLayer = SdbFindFirstTag(pdb, tagDb, TAG_LAYER);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Add all exe to registry (AppCompatFlags)
|
|
|
|
while (tagLayer && (tagLayer != prevTagLayer))
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
|
|
|
tagLayerName = SdbFindFirstTag(pdb, tagLayer, TAG_NAME);
|
|
|
|
if (!tagLayerName)
|
|
|
|
{
|
|
|
|
res = ERROR_NOT_FOUND;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPWSTR name = SdbGetStringTagPtr(pdb, tagLayerName);
|
|
|
|
wprintf(L"Layer name %ls", name);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
res = RegisterSdbEntry(name, guidDbStr, time, isInstall, FALSE);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(res))
|
|
|
|
{
|
|
|
|
wprintf(L"Can't register layer\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
prevTagLayer = tagLayer;
|
|
|
|
tagLayer = SdbFindNextTag(pdb, tagDb, tagLayer);
|
2020-10-21 20:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
ProcessExe(
|
|
|
|
_In_ PDB pdb,
|
|
|
|
_In_ TAGID tagDb,
|
2020-10-22 19:45:20 +00:00
|
|
|
_In_opt_ LPCWSTR guidDbStr,
|
|
|
|
_In_opt_ ULONGLONG time,
|
|
|
|
_In_ BOOL isInstall)
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
|
|
|
HRESULT res = ERROR_SUCCESS;
|
|
|
|
TAGID tagExeName;
|
2020-10-22 19:45:20 +00:00
|
|
|
TAGID tagExePrev = 0;
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
TAGID tagExe = SdbFindFirstTag(pdb, tagDb, TAG_EXE);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Add all exe to registry (AppCompatFlags)
|
|
|
|
while (tagExe != 0 && (tagExe != tagExePrev))
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
|
|
|
tagExeName = SdbFindFirstTag(pdb, tagExe, TAG_NAME);
|
|
|
|
if (!tagExeName)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't find exe tag\n");
|
|
|
|
res = ERROR_NOT_FOUND;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPWSTR name = SdbGetStringTagPtr(pdb, tagExeName);
|
|
|
|
wprintf(L"Exe name %ls\n", name);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
res = RegisterSdbEntry(name, guidDbStr, time, isInstall, TRUE);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(res))
|
|
|
|
{
|
2020-10-22 19:45:20 +00:00
|
|
|
wprintf(L"Can't register exe 0x%08X\n", res);
|
2020-10-21 20:11:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
tagExePrev = tagExe;
|
|
|
|
tagExe = SdbFindNextTag(pdb, tagDb, tagExe);
|
2020-10-21 20:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
CopySdbToAppPatch(
|
|
|
|
_In_ LPCWSTR sourceSdbPath,
|
|
|
|
_In_ LPCWSTR destSdbPath)
|
|
|
|
{
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
|
|
PWCHAR pTmpSysdir = NULL;
|
|
|
|
SIZE_T destLen = wcslen(destSdbPath);
|
|
|
|
PWCHAR sysdirPath = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, destLen * sizeof(WCHAR));
|
|
|
|
|
|
|
|
if (sysdirPath == NULL)
|
|
|
|
{
|
|
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Get parent folder fo sdb file
|
2020-10-21 20:11:35 +00:00
|
|
|
CopyMemory(sysdirPath, destSdbPath, destLen * sizeof(WCHAR));
|
|
|
|
pTmpSysdir = sysdirPath + destLen;
|
|
|
|
|
|
|
|
while (*pTmpSysdir != L'\\')
|
|
|
|
{
|
|
|
|
*pTmpSysdir = L'\0';
|
|
|
|
--pTmpSysdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
wprintf(L"%ls\n", sysdirPath);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Create directory if need
|
2020-10-21 20:11:35 +00:00
|
|
|
if (!CreateDirectory(sysdirPath, NULL))
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
|
|
|
if (error != ERROR_ALREADY_EXISTS)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't create folder %ls\n Error: 0x%08\n", sysdirPath, error);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
error = ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Copy file
|
2020-10-21 20:11:35 +00:00
|
|
|
if (!CopyFile(sourceSdbPath, destSdbPath, TRUE))
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
|
|
|
wprintf(L"Can't copy sdb file");
|
|
|
|
}
|
|
|
|
|
|
|
|
HeapFree(GetProcessHeap(), 0, sysdirPath);
|
|
|
|
|
|
|
|
end:
|
|
|
|
return HRESULT_FROM_WIN32(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
BuildPathToSdb(
|
|
|
|
_In_ PWCHAR* buffer,
|
|
|
|
_In_ SIZE_T bufLen,
|
|
|
|
_In_ LPCWSTR guidDbStr)
|
|
|
|
{
|
|
|
|
PWCHAR pBuffer = *buffer;
|
|
|
|
ZeroMemory(pBuffer, bufLen * sizeof(WCHAR));
|
|
|
|
|
|
|
|
UINT count = GetSystemWindowsDirectory(pBuffer, bufLen);
|
|
|
|
if (pBuffer[count - 1] != L'\\')
|
|
|
|
{
|
|
|
|
pBuffer[count] = L'\\';
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT res = StringCchCatW(pBuffer, bufLen, L"AppPatch\\Custom\\");
|
|
|
|
if (FAILED(res))
|
|
|
|
{
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = StringCchCatW(pBuffer, bufLen, guidDbStr);
|
|
|
|
|
|
|
|
end:
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
SdbInstall(
|
|
|
|
_In_ LPCWSTR sdbPath)
|
|
|
|
{
|
|
|
|
BOOL res = FALSE;
|
|
|
|
PDB pdb = NULL;
|
|
|
|
TAGID tagDb;
|
|
|
|
TAGID tagDbName;
|
|
|
|
GUID dbGuid = {0};
|
|
|
|
FILETIME systemTime = {0};
|
|
|
|
ULARGE_INTEGER currentTime = {0};
|
|
|
|
PWCHAR sysdirPatchPath = NULL;
|
2020-10-25 01:03:34 +00:00
|
|
|
PWCHAR guidDbStr;
|
|
|
|
|
|
|
|
guidDbStr = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (!guidDbStr)
|
|
|
|
{
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
ZeroMemory(guidDbStr, MAX_PATH * sizeof(WCHAR));
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
GetSystemTimeAsFileTime(&systemTime);
|
|
|
|
currentTime.LowPart = systemTime.dwLowDateTime;
|
|
|
|
currentTime.HighPart = systemTime.dwHighDateTime;
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Open db
|
2020-10-21 20:11:35 +00:00
|
|
|
pdb = SdbOpenDatabase(sdbPath, DOS_PATH);
|
|
|
|
if (pdb == NULL)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't open database %ls\n", sdbPath);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
tagDb = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
|
|
|
|
if (!tagDb)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't find database tag\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Get db guid
|
2020-10-21 20:11:35 +00:00
|
|
|
if (!GetSdbGuid(pdb, tagDb, &dbGuid))
|
|
|
|
{
|
|
|
|
wprintf(L"GetSdbGuid error\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringFromGUID2(&dbGuid, guidDbStr, MAX_PATH);
|
2020-10-25 01:03:34 +00:00
|
|
|
HRESULT hres = StringCchCatW(guidDbStr, MAX_PATH, L".sdb");
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"StringCchCatW error 0x%08X\n", hres);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
wprintf(L"Database guid %ls\n", guidDbStr);
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
tagDbName = SdbFindFirstTag(pdb, tagDb, TAG_NAME);
|
|
|
|
if (!tagDbName)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't get tag name\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
LPWSTR dbName = SdbGetStringTagPtr(pdb, tagDbName);
|
|
|
|
wprintf(L"Database name %ls\n", dbName);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Process exe tags
|
|
|
|
hres = ProcessExe(pdb, tagDb, guidDbStr, currentTime.QuadPart, TRUE);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"Process exe failed. Status: 0x%08X", res);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Proess layer tags
|
|
|
|
hres = ProcessLayers(pdb, tagDb, guidDbStr, currentTime.QuadPart, TRUE);
|
2020-10-21 20:11:35 +00:00
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"Process layers failed. Status: 0x%08X", res);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SIZE_T bufLen = MAX_PATH * 2;
|
|
|
|
sysdirPatchPath = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, bufLen * sizeof(WCHAR));
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Create full path tos db in system folder
|
2020-10-21 20:11:35 +00:00
|
|
|
hres = BuildPathToSdb(&sysdirPatchPath, bufLen, guidDbStr);
|
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"Build path error\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
wprintf(L"file path %ls\n", sysdirPatchPath);
|
|
|
|
|
|
|
|
res = CopySdbToAppPatch(sdbPath, sysdirPatchPath);
|
|
|
|
if (FAILED(res))
|
|
|
|
{
|
|
|
|
wprintf(L"Copy sdb error. Status: 0x%08X\n", res);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddUninstallKey(dbName, sysdirPatchPath, guidDbStr);
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
// Registration
|
2020-10-21 20:11:35 +00:00
|
|
|
if (!SdbRegisterDatabaseEx(sysdirPatchPath, SDB_DATABASE_SHIM, ¤tTime.QuadPart))
|
|
|
|
{
|
|
|
|
wprintf(L"SdbRegisterDatabaseEx UNSUCCESS");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = TRUE;
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (pdb)
|
|
|
|
{
|
|
|
|
SdbCloseDatabase(pdb);
|
|
|
|
}
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
if (sysdirPatchPath)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, sysdirPatchPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (guidDbStr)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, guidDbStr);
|
|
|
|
}
|
|
|
|
|
2020-10-21 20:11:35 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
HRESULT
|
|
|
|
DeleteUninstallKey(
|
|
|
|
_In_ LPCWSTR keyName)
|
|
|
|
{
|
2020-10-25 01:03:34 +00:00
|
|
|
HKEY hKey = NULL;
|
2020-10-22 19:45:20 +00:00
|
|
|
HRESULT hres = HRESULT_FROM_WIN32(ERROR_SUCCESS);
|
|
|
|
|
|
|
|
LSTATUS status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
|
|
UNINSTALL_REG_PATH,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS,
|
|
|
|
NULL,
|
|
|
|
&hKey,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = RegDeleteKey(hKey, keyName);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
hres = HRESULT_FROM_WIN32(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
2020-10-25 01:03:34 +00:00
|
|
|
if (hKey)
|
|
|
|
{
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
SdbUninstall(
|
|
|
|
_In_ LPWSTR sdbPath)
|
|
|
|
{
|
|
|
|
BOOL res = FALSE;
|
|
|
|
PWCHAR sdbName = NULL;
|
|
|
|
PDB pdb;
|
|
|
|
TAGID tagDb;
|
2020-10-25 01:03:34 +00:00
|
|
|
GUID dbGuid = {0};
|
2020-10-22 19:45:20 +00:00
|
|
|
|
|
|
|
SIZE_T sdbPathLen = wcslen(sdbPath);
|
|
|
|
sdbName = sdbPath + sdbPathLen;
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
wprintf(L"uninstall name %ls\n", sdbPath);
|
|
|
|
while (*sdbName != L'\\' && sdbPathLen > 0)
|
2020-10-22 19:45:20 +00:00
|
|
|
{
|
|
|
|
--sdbName;
|
2020-10-25 01:03:34 +00:00
|
|
|
--sdbPathLen;
|
2020-10-22 19:45:20 +00:00
|
|
|
}
|
|
|
|
sdbName++;
|
|
|
|
|
|
|
|
wprintf(L"uninstall name %ls\n", sdbName);
|
|
|
|
|
|
|
|
// open sdb
|
|
|
|
pdb = SdbOpenDatabase(sdbPath, DOS_PATH);
|
|
|
|
if (pdb == NULL)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't open database %ls\n", sdbPath);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
tagDb = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
|
|
|
|
if (!tagDb)
|
|
|
|
{
|
|
|
|
wprintf(L"Can't find database tag\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
if (!GetSdbGuid(pdb, tagDb, &dbGuid))
|
|
|
|
{
|
|
|
|
wprintf(L"GetSdbGuid error\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
//remove regkey in appatch/custom
|
|
|
|
HRESULT hres = ProcessExe(pdb, tagDb, NULL, 0, FALSE);
|
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"Process exe fail\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SdbCloseDatabase(pdb);
|
|
|
|
|
|
|
|
hres = DeleteUninstallKey(sdbName);
|
|
|
|
if (FAILED(hres))
|
|
|
|
{
|
|
|
|
wprintf(L"Remove key fail\n");
|
2020-10-25 01:03:34 +00:00
|
|
|
//goto end;
|
2020-10-22 19:45:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!SdbUnregisterDatabase(&dbGuid))
|
|
|
|
{
|
|
|
|
wprintf(L"SdbUnregisterDatabase\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!DeleteFile(sdbPath))
|
|
|
|
{
|
|
|
|
wprintf(L"Remove file fail 0x%08X\n", GetLastError());
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
res = TRUE;
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
end:
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
#define BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(WCHAR))
|
|
|
|
#define STRING_LEN (MAX_PATH + 1)
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
SdbUninstallByGuid(
|
|
|
|
_In_ LPWSTR guidSdbStr)
|
|
|
|
{
|
|
|
|
BOOL res = FALSE;
|
|
|
|
HKEY hKey = NULL;
|
|
|
|
HKEY guidKey = NULL;
|
|
|
|
LSTATUS status;
|
|
|
|
DWORD keyValSize = BUFFER_SIZE;
|
|
|
|
PWCHAR dbPath = NULL;
|
|
|
|
|
|
|
|
dbPath = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
|
|
|
|
if (!dbPath)
|
|
|
|
{
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, APPCOMPAT_INSTALLEDSDB_REG_PATH, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
|
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegOpenKey error: 0x%08X", status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = RegOpenKeyEx(hKey, guidSdbStr, 0, KEY_READ | KEY_QUERY_VALUE, &guidKey);
|
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"Cant open key: 0x%08X %ls\n", status, guidSdbStr);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = RegQueryValueEx(guidKey, DBPATH_KEY_NAME, NULL, NULL, (LPBYTE)dbPath, &keyValSize);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegQueryValueEx: 0x%08X\n", status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = SdbUninstall(dbPath);
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (dbPath)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, dbPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hKey)
|
|
|
|
{
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (guidKey)
|
|
|
|
{
|
|
|
|
RegCloseKey(guidKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
SdbUninstallByName(
|
|
|
|
_In_ LPWSTR nameSdbStr)
|
|
|
|
{
|
|
|
|
BOOL res = FALSE;
|
|
|
|
LSTATUS status;
|
|
|
|
HKEY hKey = NULL;
|
|
|
|
HKEY subKey = NULL;
|
|
|
|
DWORD index = 0;
|
|
|
|
DWORD keyNameLen = STRING_LEN;
|
|
|
|
PWCHAR keyName = NULL;
|
|
|
|
DWORD keyValSize;
|
|
|
|
PWCHAR dbDescript = NULL;
|
|
|
|
PWCHAR dbPath = NULL;
|
|
|
|
|
|
|
|
keyName = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
|
|
|
|
dbDescript = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
|
|
|
|
dbPath = (PWCHAR)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
|
|
|
|
|
|
|
|
if (!keyName || !dbDescript || !dbPath)
|
|
|
|
{
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZeroMemory(keyName, BUFFER_SIZE);
|
|
|
|
ZeroMemory(dbDescript, BUFFER_SIZE);
|
|
|
|
ZeroMemory(dbPath, BUFFER_SIZE);
|
|
|
|
|
|
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, APPCOMPAT_INSTALLEDSDB_REG_PATH, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
|
|
|
|
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wprintf(L"RegOpenKey error: 0x%08X", status);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = RegEnumKeyEx(hKey, index, keyName, &keyNameLen, NULL, NULL, NULL, NULL);
|
|
|
|
wprintf(L"0x%08X %d %ls \n", status, keyNameLen, keyName);
|
|
|
|
|
|
|
|
// Search db guid by name
|
|
|
|
while (status == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
status = RegOpenKeyEx(hKey, keyName, 0, KEY_READ | KEY_QUERY_VALUE, &subKey);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
keyValSize = BUFFER_SIZE;
|
|
|
|
status = RegQueryValueEx(subKey, L"DatabaseDescription", NULL, NULL, (LPBYTE)dbDescript, &keyValSize);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wprintf(L"dbdescript: %ls \n", dbDescript);
|
|
|
|
|
|
|
|
if (_wcsnicmp(dbDescript, nameSdbStr, keyNameLen) == 0)
|
|
|
|
{
|
|
|
|
// Take db full path
|
|
|
|
keyValSize = BUFFER_SIZE;
|
|
|
|
status = RegQueryValueEx(subKey, DBPATH_KEY_NAME, NULL, NULL, (LPBYTE)dbPath, &keyValSize);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
dbPath[0] = UNICODE_NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wprintf(L"dbpath: 0x%08X %ls \n", status, dbPath);
|
|
|
|
RegCloseKey(subKey);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RegCloseKey(subKey);
|
|
|
|
|
|
|
|
keyName[0] = UNICODE_NULL;
|
|
|
|
|
|
|
|
++index;
|
|
|
|
keyNameLen = STRING_LEN;
|
|
|
|
status = RegEnumKeyEx(hKey, index, keyName, &keyNameLen, NULL, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
|
|
|
|
if (dbPath[0] != UNICODE_NULL)
|
|
|
|
{
|
|
|
|
res = SdbUninstall(dbPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (dbPath)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, dbPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dbDescript)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, dbDescript);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyName)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, keyName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2020-10-22 19:45:20 +00:00
|
|
|
|
|
|
|
|
2020-10-21 20:11:35 +00:00
|
|
|
void
|
|
|
|
ShowHelp()
|
|
|
|
{
|
|
|
|
wprintf(L"Using: sdbinst [-?][-q][-u][-g][-n] foo.sdb | {guid} | \"name\" \n"
|
|
|
|
L"-? - show help\n"
|
2020-10-25 01:03:34 +00:00
|
|
|
//L"-q - silence mode\n"
|
2020-10-21 20:11:35 +00:00
|
|
|
L"-u - uninstall\n"
|
|
|
|
L"-g - {guid} file guid (only uninstall)\n"
|
|
|
|
L"-n - \"name\" - file name (only uninstall)\n");
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
int _tmain(int argc, LPWSTR argv[])
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
2020-10-22 19:45:20 +00:00
|
|
|
LPWSTR sdbPath = NULL;
|
|
|
|
BOOL isInstall = TRUE;
|
2020-10-25 01:03:34 +00:00
|
|
|
BOOL isUninstByGuid = FALSE;
|
|
|
|
BOOL isUninstByName = FALSE;
|
|
|
|
BOOL success = FALSE;
|
|
|
|
LPWSTR guidSdbStr = NULL;
|
|
|
|
LPWSTR nameSdbStr = NULL;
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
{
|
|
|
|
ShowHelp();
|
|
|
|
}
|
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
for (int i = 1; i < argc; ++i)
|
2020-10-21 20:11:35 +00:00
|
|
|
{
|
2020-10-22 19:45:20 +00:00
|
|
|
if (argv[i][0] != L'-')
|
|
|
|
{
|
|
|
|
sdbPath = argv[i];
|
|
|
|
break;
|
|
|
|
}
|
2020-10-21 20:11:35 +00:00
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
switch (argv[i][1])
|
|
|
|
{
|
|
|
|
case L'?':
|
|
|
|
ShowHelp();
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L'u':
|
|
|
|
isInstall = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L'g':
|
|
|
|
++i;
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
if (i >= argc)
|
2020-10-22 19:45:20 +00:00
|
|
|
{
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2020-10-25 01:03:34 +00:00
|
|
|
guidSdbStr = argv[i];
|
|
|
|
wprintf(L"guidSdbStr %ls\n", guidSdbStr);
|
|
|
|
|
|
|
|
isUninstByGuid = TRUE;
|
|
|
|
isInstall = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L'n':
|
|
|
|
++i;
|
|
|
|
|
|
|
|
if (i >= argc)
|
|
|
|
{
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
nameSdbStr = argv[i];
|
|
|
|
wprintf(L"guidSdbStr %ls\n", nameSdbStr);
|
|
|
|
|
|
|
|
isUninstByName = TRUE;
|
|
|
|
isInstall = FALSE;
|
2020-10-22 19:45:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-10-21 20:11:35 +00:00
|
|
|
|
2020-10-22 19:45:20 +00:00
|
|
|
if (isInstall)
|
|
|
|
{
|
2020-10-25 01:03:34 +00:00
|
|
|
wprintf(L"install\n");
|
|
|
|
success = SdbInstall(sdbPath);
|
|
|
|
}
|
|
|
|
else if (isUninstByGuid)
|
|
|
|
{
|
|
|
|
wprintf(L"uninstall by guid\n");
|
|
|
|
success = SdbUninstallByGuid(guidSdbStr);
|
|
|
|
}
|
|
|
|
else if (isUninstByName)
|
|
|
|
{
|
|
|
|
wprintf(L"uninstall by name\n");
|
|
|
|
success = SdbUninstallByName(nameSdbStr);
|
2020-10-22 19:45:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-25 01:03:34 +00:00
|
|
|
wprintf(L"uninstall\n");
|
|
|
|
success = SdbUninstall(sdbPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
wprintf(L"Sdb install failed\n");
|
|
|
|
return -1;
|
2020-10-22 19:45:20 +00:00
|
|
|
}
|
2020-10-21 20:11:35 +00:00
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
2020-10-22 19:45:20 +00:00
|
|
|
}
|