mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 02:25:40 +00:00
[RAPPS] Automatically generate installer/uninstaller for downloaded zip/cab files (#6652)
With a single database line added to applications distributed as zip/cab allows rapps.exe to act as an installer that automatically extracts the files and creates a startmenu shortcut. It can also uninstall the extracted files (and optionally other files and registry entries created by the application).
This commit is contained in:
parent
ad8392602e
commit
57b775ef6e
43 changed files with 1638 additions and 137 deletions
|
@ -2,6 +2,9 @@ project(rapps)
|
||||||
|
|
||||||
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/cryptlib)
|
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/cryptlib)
|
||||||
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
|
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
|
||||||
|
include_directories(
|
||||||
|
${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
|
||||||
|
${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/contrib)
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
|
||||||
list(APPEND SOURCE
|
list(APPEND SOURCE
|
||||||
|
@ -11,6 +14,7 @@ list(APPEND SOURCE
|
||||||
asyncinet.cpp
|
asyncinet.cpp
|
||||||
cabinet.cpp
|
cabinet.cpp
|
||||||
configparser.cpp
|
configparser.cpp
|
||||||
|
geninst.cpp
|
||||||
gui.cpp
|
gui.cpp
|
||||||
integrity.cpp
|
integrity.cpp
|
||||||
loaddlg.cpp
|
loaddlg.cpp
|
||||||
|
@ -35,6 +39,7 @@ list(APPEND SOURCE
|
||||||
include/settings.h
|
include/settings.h
|
||||||
include/unattended.h
|
include/unattended.h
|
||||||
include/winmain.h
|
include/winmain.h
|
||||||
|
${ZLIB_SOURCE}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
|
@ -44,7 +49,7 @@ file(GLOB_RECURSE rapps_rc_deps res/*.*)
|
||||||
add_rc_deps(rapps.rc ${rapps_rc_deps})
|
add_rc_deps(rapps.rc ${rapps_rc_deps})
|
||||||
add_executable(rapps ${SOURCE} rapps.rc)
|
add_executable(rapps ${SOURCE} rapps.rc)
|
||||||
set_module_type(rapps win32gui UNICODE)
|
set_module_type(rapps win32gui UNICODE)
|
||||||
target_link_libraries(rapps conutils ${PSEH_LIB} uuid wine cpprt atl_classes)
|
target_link_libraries(rapps conutils ${PSEH_LIB} uuid wine cpprt atl_classes minizip zlib)
|
||||||
add_importlibs(rapps advapi32 comctl32 gdi32 wininet user32 shell32 shlwapi ole32 setupapi gdiplus msvcrt kernel32 ntdll)
|
add_importlibs(rapps advapi32 comctl32 gdi32 wininet user32 shell32 shlwapi ole32 setupapi gdiplus msvcrt kernel32 ntdll)
|
||||||
add_pch(rapps include/rapps.h SOURCE)
|
add_pch(rapps include/rapps.h SOURCE)
|
||||||
add_dependencies(rapps rappsmsg)
|
add_dependencies(rapps rappsmsg)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
|
|
||||||
static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
|
static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
|
||||||
static REGSAM g_RegSamEnum[3] = {KEY_WOW64_32KEY, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
|
static REGSAM g_RegSamEnum[3] = {0, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
|
||||||
#define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
#define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
||||||
|
|
||||||
static VOID
|
static VOID
|
||||||
|
@ -34,8 +34,8 @@ CAppDB::CAppDB(const CStringW &path) : m_BasePath(path)
|
||||||
m_BasePath.Canonicalize();
|
m_BasePath.Canonicalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
CAppInfo *
|
CAvailableApplicationInfo *
|
||||||
CAppDB::FindByPackageName(const CStringW &name)
|
CAppDB::FindAvailableByPackageName(const CStringW &name)
|
||||||
{
|
{
|
||||||
POSITION CurrentListPosition = m_Available.GetHeadPosition();
|
POSITION CurrentListPosition = m_Available.GetHeadPosition();
|
||||||
while (CurrentListPosition)
|
while (CurrentListPosition)
|
||||||
|
@ -43,7 +43,7 @@ CAppDB::FindByPackageName(const CStringW &name)
|
||||||
CAppInfo *Info = m_Available.GetNext(CurrentListPosition);
|
CAppInfo *Info = m_Available.GetNext(CurrentListPosition);
|
||||||
if (Info->szIdentifier == name)
|
if (Info->szIdentifier == name)
|
||||||
{
|
{
|
||||||
return Info;
|
return static_cast<CAvailableApplicationInfo *>(Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -97,7 +97,7 @@ CAppDB::EnumerateFiles()
|
||||||
{
|
{
|
||||||
CConfigParser *Parser = new CConfigParser(CPathW(AppsPath) += FindFileData.cFileName);
|
CConfigParser *Parser = new CConfigParser(CPathW(AppsPath) += FindFileData.cFileName);
|
||||||
int Cat;
|
int Cat;
|
||||||
if (!Parser->GetInt(L"Category", Cat))
|
if (!Parser->GetInt(DB_CATEGORY, Cat))
|
||||||
Cat = ENUM_INVALID;
|
Cat = ENUM_INVALID;
|
||||||
|
|
||||||
Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
|
Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
|
||||||
|
@ -141,74 +141,123 @@ CAppDB::UpdateAvailable()
|
||||||
EnumerateFiles();
|
EnumerateFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline HKEY
|
||||||
|
GetRootKeyInfo(UINT Index, REGSAM &RegSam)
|
||||||
|
{
|
||||||
|
C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum));
|
||||||
|
if (Index < _countof(g_RootKeyEnum))
|
||||||
|
{
|
||||||
|
RegSam = g_RegSamEnum[Index];
|
||||||
|
return g_RootKeyEnum[Index];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HKEY
|
||||||
|
CAppDB::EnumInstalledRootKey(UINT Index, REGSAM &RegSam)
|
||||||
|
{
|
||||||
|
// Loop for through all combinations.
|
||||||
|
// Note that HKEY_CURRENT_USER\Software does not have a redirect
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64
|
||||||
|
if (Index < (IsSystem64Bit() ? 3 : 2))
|
||||||
|
return GetRootKeyInfo(Index, RegSam);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CInstalledApplicationInfo *
|
||||||
|
CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex)
|
||||||
|
{
|
||||||
|
CRegKey hSubKey;
|
||||||
|
if (hSubKey.Open(hKeyParent, KeyName, KEY_READ) != ERROR_SUCCESS)
|
||||||
|
return NULL;
|
||||||
|
DWORD value, size;
|
||||||
|
|
||||||
|
size = sizeof(DWORD);
|
||||||
|
if (!RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&value, &size) && value == 1)
|
||||||
|
{
|
||||||
|
// Ignore system components
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
BOOL bIsUpdate = !RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &size);
|
||||||
|
|
||||||
|
AppsCategories cat = bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS;
|
||||||
|
CInstalledApplicationInfo *pInfo;
|
||||||
|
pInfo = new CInstalledApplicationInfo(hSubKey.Detach(), KeyName, cat, KeyIndex);
|
||||||
|
if (pInfo && pInfo->Valid())
|
||||||
|
{
|
||||||
|
return pInfo;
|
||||||
|
}
|
||||||
|
delete pInfo;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CInstalledApplicationInfo *
|
||||||
|
CAppDB::EnumerateRegistry(CAtlList<CAppInfo *> *List, LPCWSTR SearchOnly)
|
||||||
|
{
|
||||||
|
ATLASSERT(List || SearchOnly);
|
||||||
|
REGSAM wowsam;
|
||||||
|
HKEY hRootKey;
|
||||||
|
for (UINT rki = 0; (hRootKey = EnumInstalledRootKey(rki, wowsam)); ++rki)
|
||||||
|
{
|
||||||
|
CRegKey hKey;
|
||||||
|
if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | wowsam) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (DWORD Index = 0;; ++Index)
|
||||||
|
{
|
||||||
|
WCHAR szKeyName[MAX_PATH];
|
||||||
|
DWORD dwSize = _countof(szKeyName);
|
||||||
|
if (hKey.EnumKey(Index, szKeyName, &dwSize) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (List || !StrCmpIW(SearchOnly, szKeyName))
|
||||||
|
{
|
||||||
|
CInstalledApplicationInfo *Info;
|
||||||
|
Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, rki);
|
||||||
|
if (Info)
|
||||||
|
{
|
||||||
|
if (List)
|
||||||
|
List->AddTail(Info);
|
||||||
|
else
|
||||||
|
return Info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
CAppDB::UpdateInstalled()
|
CAppDB::UpdateInstalled()
|
||||||
{
|
{
|
||||||
// Remove all old entries
|
// Remove all old entries
|
||||||
ClearList(m_Installed);
|
ClearList(m_Installed);
|
||||||
|
|
||||||
int LoopKeys = 2;
|
EnumerateRegistry(&m_Installed, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsSystem64Bit())
|
CInstalledApplicationInfo *
|
||||||
{
|
CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR Name)
|
||||||
// loop for all 3 combination.
|
{
|
||||||
// note that HKEY_CURRENT_USER\Software don't have a redirect
|
return EnumerateRegistry(NULL, Name);
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64
|
}
|
||||||
LoopKeys = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int keyIndex = 0; keyIndex < LoopKeys; keyIndex++)
|
|
||||||
{
|
|
||||||
LONG ItemIndex = 0;
|
|
||||||
WCHAR szKeyName[MAX_PATH];
|
|
||||||
|
|
||||||
|
CInstalledApplicationInfo *
|
||||||
|
CAppDB::CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam)
|
||||||
|
{
|
||||||
|
HKEY hRootKey = User ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
||||||
|
UINT KeyIndex = User ? (0) : ((WowSam & KEY_WOW64_64KEY) ? 2 : 1);
|
||||||
CRegKey hKey;
|
CRegKey hKey;
|
||||||
if (hKey.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | g_RegSamEnum[keyIndex]) != ERROR_SUCCESS)
|
if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | WowSam) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
continue;
|
return CreateInstalledAppByRegistryKey(KeyName, hKey, KeyIndex);
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
DWORD dwSize = _countof(szKeyName);
|
|
||||||
if (hKey.EnumKey(ItemIndex, szKeyName, &dwSize) != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemIndex++;
|
|
||||||
|
|
||||||
CRegKey hSubKey;
|
|
||||||
if (hSubKey.Open(hKey, szKeyName, KEY_READ) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
DWORD dwValue = 0;
|
|
||||||
|
|
||||||
dwSize = sizeof(DWORD);
|
|
||||||
if (RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&dwValue, &dwSize) ==
|
|
||||||
ERROR_SUCCESS &&
|
|
||||||
dwValue == 1)
|
|
||||||
{
|
|
||||||
// Ignore system components
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL bIsUpdate =
|
|
||||||
(RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &dwSize) == ERROR_SUCCESS);
|
|
||||||
|
|
||||||
CInstalledApplicationInfo *Info = new CInstalledApplicationInfo(
|
|
||||||
hSubKey.Detach(), szKeyName, bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS, keyIndex);
|
|
||||||
|
|
||||||
if (Info->Valid())
|
|
||||||
{
|
|
||||||
m_Installed.AddTail(Info);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete Info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -262,30 +311,28 @@ CAppDB::RemoveCached()
|
||||||
RemoveDirectoryW(m_BasePath);
|
RemoveDirectoryW(m_BasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
DWORD
|
||||||
CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info)
|
CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info)
|
||||||
{
|
{
|
||||||
// Validate that this is actually an installed app / update
|
// Validate that this is actually an installed app / update
|
||||||
ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES);
|
ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES);
|
||||||
if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES)
|
if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES)
|
||||||
return FALSE;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Grab the index in the registry keys
|
|
||||||
const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info);
|
const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info);
|
||||||
ATLASSERT(InstalledInfo->iKeyIndex >= 0 && InstalledInfo->iKeyIndex < (int)_countof(g_RootKeyEnum));
|
|
||||||
if (InstalledInfo->iKeyIndex < 0 && InstalledInfo->iKeyIndex >= (int)_countof(g_RootKeyEnum))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
int keyIndex = InstalledInfo->iKeyIndex;
|
|
||||||
|
|
||||||
// Grab the registry key name
|
|
||||||
CStringW Name = InstalledInfo->szIdentifier;
|
CStringW Name = InstalledInfo->szIdentifier;
|
||||||
|
REGSAM wowsam;
|
||||||
|
HKEY hRoot = GetRootKeyInfo(InstalledInfo->m_KeyInfo, wowsam);
|
||||||
|
ATLASSERT(hRoot);
|
||||||
|
if (!hRoot)
|
||||||
|
return ERROR_OPEN_FAILED;
|
||||||
|
|
||||||
// Recursively delete this key
|
|
||||||
CRegKey Uninstall;
|
CRegKey Uninstall;
|
||||||
if (Uninstall.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | g_RegSamEnum[keyIndex]) !=
|
LSTATUS err = Uninstall.Open(hRoot, UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | wowsam);
|
||||||
ERROR_SUCCESS)
|
if (err == ERROR_SUCCESS)
|
||||||
return FALSE;
|
{
|
||||||
|
err = Uninstall.RecurseDeleteKey(Name);
|
||||||
return Uninstall.RecurseDeleteKey(Name) == ERROR_SUCCESS;
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ VOID
|
||||||
CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
|
CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
|
||||||
{
|
{
|
||||||
CStringW szRegName;
|
CStringW szRegName;
|
||||||
m_Parser->GetString(L"RegName", szRegName);
|
m_Parser->GetString(DB_REGNAME, szRegName);
|
||||||
|
|
||||||
BOOL bIsInstalled = ::GetInstalledVersion(NULL, szRegName) || ::GetInstalledVersion(NULL, szDisplayName);
|
BOOL bIsInstalled = ::GetInstalledVersion(NULL, szRegName) || ::GetInstalledVersion(NULL, szDisplayName);
|
||||||
if (bIsInstalled)
|
if (bIsInstalled)
|
||||||
|
@ -131,10 +131,8 @@ CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
|
||||||
{
|
{
|
||||||
BOOL bHasUpdate = CompareVersion(szInstalledVersion, szDisplayVersion) < 0;
|
BOOL bHasUpdate = CompareVersion(szInstalledVersion, szDisplayVersion) < 0;
|
||||||
if (bHasUpdate)
|
if (bHasUpdate)
|
||||||
|
{
|
||||||
RichEdit->LoadAndInsertText(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
|
RichEdit->LoadAndInsertText(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
|
||||||
else
|
|
||||||
RichEdit->LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC);
|
|
||||||
|
|
||||||
RichEdit->LoadAndInsertText(IDS_AINFO_VERSION, szInstalledVersion, 0);
|
RichEdit->LoadAndInsertText(IDS_AINFO_VERSION, szInstalledVersion, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -143,6 +141,11 @@ CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
RichEdit->LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
RichEdit->LoadAndInsertText(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
|
RichEdit->LoadAndInsertText(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
|
||||||
}
|
}
|
||||||
|
@ -364,8 +367,19 @@ CAvailableApplicationInfo::GetDisplayInfo(CStringW &License, CStringW &Size, CSt
|
||||||
UrlDownload = m_szUrlDownload;
|
UrlDownload = m_szUrlDownload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstallerType
|
||||||
|
CAvailableApplicationInfo::GetInstallerType() const
|
||||||
|
{
|
||||||
|
CStringW str;
|
||||||
|
m_Parser->GetString(DB_INSTALLER, str);
|
||||||
|
if (str.CompareNoCase(DB_GENINSTSECTION) == 0)
|
||||||
|
return INSTALLER_GENERATE;
|
||||||
|
else
|
||||||
|
return INSTALLER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
CAvailableApplicationInfo::UninstallApplication(BOOL bModify)
|
CAvailableApplicationInfo::UninstallApplication(UninstallCommandFlags Flags)
|
||||||
{
|
{
|
||||||
ATLASSERT(FALSE && "Should not be called");
|
ATLASSERT(FALSE && "Should not be called");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -374,9 +388,8 @@ CAvailableApplicationInfo::UninstallApplication(BOOL bModify)
|
||||||
CInstalledApplicationInfo::CInstalledApplicationInfo(
|
CInstalledApplicationInfo::CInstalledApplicationInfo(
|
||||||
HKEY Key,
|
HKEY Key,
|
||||||
const CStringW &KeyName,
|
const CStringW &KeyName,
|
||||||
AppsCategories Category,
|
AppsCategories Category, UINT KeyInfo)
|
||||||
int KeyIndex)
|
: CAppInfo(KeyName, Category), m_hKey(Key), m_KeyInfo(KeyInfo)
|
||||||
: CAppInfo(KeyName, Category), m_hKey(Key), iKeyIndex(KeyIndex)
|
|
||||||
{
|
{
|
||||||
if (GetApplicationRegString(L"DisplayName", szDisplayName))
|
if (GetApplicationRegString(L"DisplayName", szDisplayName))
|
||||||
{
|
{
|
||||||
|
@ -561,15 +574,51 @@ CInstalledApplicationInfo::GetDisplayInfo(CStringW &License, CStringW &Size, CSt
|
||||||
ATLASSERT(FALSE && "Should not be called");
|
ATLASSERT(FALSE && "Should not be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
InstallerType
|
||||||
CInstalledApplicationInfo::UninstallApplication(BOOL bModify)
|
CInstalledApplicationInfo::GetInstallerType() const
|
||||||
{
|
{
|
||||||
|
CRegKey reg;
|
||||||
|
if (reg.Open(m_hKey, GENERATE_ARPSUBKEY, KEY_READ) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return INSTALLER_GENERATE;
|
||||||
|
}
|
||||||
|
return INSTALLER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
CInstalledApplicationInfo::UninstallApplication(UninstallCommandFlags Flags)
|
||||||
|
{
|
||||||
|
if (GetInstallerType() == INSTALLER_GENERATE)
|
||||||
|
{
|
||||||
|
return UninstallGenerated(*this, Flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL bModify = Flags & UCF_MODIFY;
|
||||||
if (m_szUninstallString.IsEmpty())
|
if (m_szUninstallString.IsEmpty())
|
||||||
{
|
{
|
||||||
RetrieveUninstallStrings();
|
RetrieveUninstallStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL bSuccess = StartProcess(bModify ? m_szModifyString : m_szUninstallString, TRUE);
|
CStringW cmd = bModify ? m_szModifyString : m_szUninstallString;
|
||||||
|
if ((Flags & (UCF_MODIFY | UCF_SILENT)) == UCF_SILENT)
|
||||||
|
{
|
||||||
|
DWORD msi = 0;
|
||||||
|
msi = GetApplicationRegDword(L"WindowsInstaller", &msi) && msi;
|
||||||
|
if (msi)
|
||||||
|
{
|
||||||
|
cmd += L" /qn";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CStringW silentcmd;
|
||||||
|
if (GetApplicationRegString(L"QuietUninstallString", silentcmd) && !silentcmd.IsEmpty())
|
||||||
|
{
|
||||||
|
cmd = silentcmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL bSuccess = StartProcess(cmd, TRUE);
|
||||||
|
|
||||||
if (bSuccess && !bModify)
|
if (bSuccess && !bModify)
|
||||||
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szDisplayName);
|
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szDisplayName);
|
||||||
|
|
|
@ -53,6 +53,13 @@ MultiByteToWide(const CStringA &szSource, CStringW &szDest, UINT Codepage)
|
||||||
return sz != 0;
|
return sz != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NotifyData
|
||||||
|
{
|
||||||
|
EXTRACTCALLBACK Callback;
|
||||||
|
void *CallerCookie;
|
||||||
|
LPCSTR OutputDir;
|
||||||
|
};
|
||||||
|
|
||||||
/* FDICreate callbacks */
|
/* FDICreate callbacks */
|
||||||
|
|
||||||
FNALLOC(fnMemAlloc)
|
FNALLOC(fnMemAlloc)
|
||||||
|
@ -143,6 +150,7 @@ FNSEEK(fnFileSeek)
|
||||||
FNFDINOTIFY(fnNotify)
|
FNFDINOTIFY(fnNotify)
|
||||||
{
|
{
|
||||||
INT_PTR iResult = 0;
|
INT_PTR iResult = 0;
|
||||||
|
NotifyData *pND = (NotifyData *)pfdin->pv;
|
||||||
|
|
||||||
switch (fdint)
|
switch (fdint)
|
||||||
{
|
{
|
||||||
|
@ -151,9 +159,15 @@ FNFDINOTIFY(fnNotify)
|
||||||
CStringW szExtractDir, szCabFileName;
|
CStringW szExtractDir, szCabFileName;
|
||||||
|
|
||||||
// Append the destination directory to the file name.
|
// Append the destination directory to the file name.
|
||||||
MultiByteToWide((LPCSTR)pfdin->pv, szExtractDir, CP_UTF8);
|
MultiByteToWide(pND->OutputDir, szExtractDir, CP_UTF8);
|
||||||
MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP);
|
MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP);
|
||||||
|
|
||||||
|
if (!NotifyFileExtractCallback(szCabFileName, pfdin->cb, pfdin->attribs,
|
||||||
|
pND->Callback, pND->CallerCookie))
|
||||||
|
{
|
||||||
|
break; // Skip file
|
||||||
|
}
|
||||||
|
|
||||||
if (szCabFileName.Find('\\') >= 0)
|
if (szCabFileName.Find('\\') >= 0)
|
||||||
{
|
{
|
||||||
CStringW szNewDirName = szExtractDir;
|
CStringW szNewDirName = szExtractDir;
|
||||||
|
@ -230,10 +244,10 @@ typedef BOOL (*fnFDIDestroy)(HFDI);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extraction function
|
* Extraction function
|
||||||
* TODO: require only a full path to the cab as an argument
|
|
||||||
*/
|
*/
|
||||||
BOOL
|
BOOL
|
||||||
ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir)
|
ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
{
|
{
|
||||||
HINSTANCE hCabinetDll;
|
HINSTANCE hCabinetDll;
|
||||||
HFDI ExtractHandler;
|
HFDI ExtractHandler;
|
||||||
|
@ -243,6 +257,7 @@ ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const C
|
||||||
fnFDICopy pfnFDICopy;
|
fnFDICopy pfnFDICopy;
|
||||||
fnFDIDestroy pfnFDIDestroy;
|
fnFDIDestroy pfnFDIDestroy;
|
||||||
BOOL bResult;
|
BOOL bResult;
|
||||||
|
NotifyData nd = { Callback, Cookie };
|
||||||
|
|
||||||
// Load cabinet.dll and extract needed functions
|
// Load cabinet.dll and extract needed functions
|
||||||
hCabinetDll = LoadLibraryW(L"cabinet.dll");
|
hCabinetDll = LoadLibraryW(L"cabinet.dll");
|
||||||
|
@ -290,12 +305,21 @@ ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const C
|
||||||
// Add a slash to cab name as required by the api
|
// Add a slash to cab name as required by the api
|
||||||
szCabNameUTF8 = "\\" + szCabNameUTF8;
|
szCabNameUTF8 = "\\" + szCabNameUTF8;
|
||||||
|
|
||||||
|
nd.OutputDir = szOutputDirUTF8.GetString();
|
||||||
bResult = pfnFDICopy(
|
bResult = pfnFDICopy(
|
||||||
ExtractHandler, (LPSTR)szCabNameUTF8.GetString(), (LPSTR)szCabDirUTF8.GetString(), 0, fnNotify, NULL,
|
ExtractHandler, (LPSTR)szCabNameUTF8.GetString(), (LPSTR)szCabDirUTF8.GetString(), 0, fnNotify, NULL,
|
||||||
(void FAR *)szOutputDirUTF8.GetString());
|
(void FAR *)&nd);
|
||||||
}
|
}
|
||||||
|
|
||||||
pfnFDIDestroy(ExtractHandler);
|
pfnFDIDestroy(ExtractHandler);
|
||||||
FreeLibrary(hCabinetDll);
|
FreeLibrary(hCabinetDll);
|
||||||
return bResult;
|
return bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
ExtractFilesFromCab(LPCWSTR FullCabPath, const CStringW &szOutputDir,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
|
{
|
||||||
|
CStringW dir, file = SplitFileAndDirectory(FullCabPath, &dir);
|
||||||
|
return ExtractFilesFromCab(file, dir, szOutputDir, Callback, Cookie);
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,20 @@ struct CSectionNames
|
||||||
};
|
};
|
||||||
static CSectionNames g_Names;
|
static CSectionNames g_Names;
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
ReadIniValue(LPCWSTR File, LPCWSTR Section, LPCWSTR Name, CStringW &Output)
|
||||||
|
{
|
||||||
|
for (DWORD len = 256, ret;; len *= 2)
|
||||||
|
{
|
||||||
|
ret = GetPrivateProfileString(Section, Name, L"\n", Output.GetBuffer(len), len, File);
|
||||||
|
if (ret + 1 != len)
|
||||||
|
{
|
||||||
|
Output.ReleaseBuffer(ret);
|
||||||
|
return ret && Output[0] != L'\n' ? ret : HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CConfigParser::CConfigParser(const CStringW &FilePath) : szConfigPath(FilePath)
|
CConfigParser::CConfigParser(const CStringW &FilePath) : szConfigPath(FilePath)
|
||||||
{
|
{
|
||||||
CacheINI();
|
CacheINI();
|
||||||
|
@ -166,3 +180,42 @@ CConfigParser::GetInt(const CStringW &KeyName, INT &iResult)
|
||||||
// we only care about values > 0
|
// we only care about values > 0
|
||||||
return (iResult > 0);
|
return (iResult > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT
|
||||||
|
CConfigParser::GetSectionString(LPCWSTR Section, LPCWSTR Name, CStringW &Result)
|
||||||
|
{
|
||||||
|
HRESULT hr; // Return value; length of ini string or 0 on failure.
|
||||||
|
CStringW SecBuf;
|
||||||
|
WCHAR FullLoc[5], *NeutralLoc = FullLoc + 2;
|
||||||
|
GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, FullLoc, _countof(FullLoc));
|
||||||
|
|
||||||
|
SecBuf.Format(L"%s.%s.%s", Section, FullLoc, CurrentArchitecture);
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (*NeutralLoc)
|
||||||
|
{
|
||||||
|
SecBuf.Format(L"%s.%s.%s", Section, NeutralLoc, CurrentArchitecture);
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecBuf.Format(L"%s.%s", Section, CurrentArchitecture);
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
SecBuf.Format(L"%s.%s", Section, FullLoc);
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (*NeutralLoc)
|
||||||
|
{
|
||||||
|
SecBuf.Format(L"%s.%s", Section, NeutralLoc);
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hr = ReadIniValue(szConfigPath, Section, Name, Result)) > 0)
|
||||||
|
return hr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
817
base/applications/rapps/geninst.cpp
Normal file
817
base/applications/rapps/geninst.cpp
Normal file
|
@ -0,0 +1,817 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS Applications Manager
|
||||||
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||||
|
* PURPOSE: Act as installer and uninstaller for applications distributed in archives
|
||||||
|
* COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks@proton.me>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <setupapi.h>
|
||||||
|
#include <commctrl.h>
|
||||||
|
#include "resource.h"
|
||||||
|
#include "appdb.h"
|
||||||
|
#include "appinfo.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "configparser.h"
|
||||||
|
#include "unattended.h"
|
||||||
|
|
||||||
|
#include "minizip/ioapi.h"
|
||||||
|
#include "minizip/iowin32.h"
|
||||||
|
#include "minizip/unzip.h"
|
||||||
|
extern "C" {
|
||||||
|
#include "minizip/ioapi.c"
|
||||||
|
#include "minizip/iowin32.c"
|
||||||
|
#include "minizip/unzip.c"
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REGPATH_UNINSTALL L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
||||||
|
|
||||||
|
#define DB_GENINST_FILES L"Files"
|
||||||
|
#define DB_GENINST_DIR L"Dir"
|
||||||
|
#define DB_GENINST_ICON L"Icon"
|
||||||
|
#define DB_GENINST_LNK L"Lnk"
|
||||||
|
#define DB_GENINST_DELFILE L"DelFile" // Delete files generated by the application
|
||||||
|
#define DB_GENINST_DELDIR L"DelDir"
|
||||||
|
#define DB_GENINST_DELDIREMPTY L"DelDirEmpty"
|
||||||
|
#define DB_GENINST_DELREG L"DelReg"
|
||||||
|
#define DB_GENINST_DELREGEMPTY L"DelRegEmpty"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UNOP_FILE = 'F',
|
||||||
|
UNOP_DIR = 'D',
|
||||||
|
UNOP_EMPTYDIR = 'd',
|
||||||
|
UNOP_REGKEY = 'K',
|
||||||
|
UNOP_EMPTYREGKEY = 'k',
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
ExtractFilesFromZip(LPCWSTR Archive, const CStringW &OutputDir,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
|
{
|
||||||
|
const UINT pkzefsutf8 = 1 << 11; // APPNOTE; APPENDIX D
|
||||||
|
zlib_filefunc64_def zff;
|
||||||
|
fill_win32_filefunc64W(&zff);
|
||||||
|
unzFile hzf = unzOpen2_64(Archive, &zff);
|
||||||
|
if (!hzf)
|
||||||
|
return UNZ_BADZIPFILE;
|
||||||
|
CStringA narrow;
|
||||||
|
CStringW path, dir;
|
||||||
|
int zerr = unzGoToFirstFile(hzf);
|
||||||
|
for (; zerr == UNZ_OK; zerr = unzGoToNextFile(hzf))
|
||||||
|
{
|
||||||
|
unz_file_info64 fi;
|
||||||
|
zerr = unzGetCurrentFileInfo64(hzf, &fi, NULL, 0, NULL, 0, NULL, 0);
|
||||||
|
if (zerr != UNZ_OK)
|
||||||
|
break;
|
||||||
|
LPSTR file = narrow.GetBuffer(fi.size_filename);
|
||||||
|
zerr = unzGetCurrentFileInfo64(hzf, &fi, file, narrow.GetAllocLength(), NULL, 0, NULL, 0);
|
||||||
|
if (zerr != UNZ_OK)
|
||||||
|
break;
|
||||||
|
narrow.ReleaseBuffer(fi.size_filename);
|
||||||
|
narrow.Replace('/', '\\');
|
||||||
|
while (narrow[0] == '\\')
|
||||||
|
narrow = narrow.Mid(1);
|
||||||
|
UINT codepage = (fi.flag & pkzefsutf8) ? CP_UTF8 : 1252;
|
||||||
|
UINT cch = MultiByteToWideChar(codepage, 0, narrow, -1, NULL, 0);
|
||||||
|
cch = MultiByteToWideChar(codepage, 0, narrow, -1, path.GetBuffer(cch - 1), cch);
|
||||||
|
if (!cch)
|
||||||
|
break;
|
||||||
|
path.ReleaseBuffer(cch - 1);
|
||||||
|
DWORD fileatt = FILE_ATTRIBUTE_NORMAL, err;
|
||||||
|
BYTE attsys = HIBYTE(fi.version), dos = 0, ntfs = 10, vfat = 14;
|
||||||
|
if ((attsys == dos || attsys == ntfs || attsys == vfat) && LOBYTE(fi.external_fa))
|
||||||
|
fileatt = LOBYTE(fi.external_fa);
|
||||||
|
|
||||||
|
if (!NotifyFileExtractCallback(path, fi.uncompressed_size, fileatt,
|
||||||
|
Callback, Cookie))
|
||||||
|
continue; // Skip file
|
||||||
|
|
||||||
|
path = BuildPath(OutputDir, path);
|
||||||
|
SplitFileAndDirectory(path, &dir);
|
||||||
|
if (!dir.IsEmpty() && (err = CreateDirectoryTree(dir)))
|
||||||
|
{
|
||||||
|
zerr = UNZ_ERRNO;
|
||||||
|
SetLastError(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerr = unzOpenCurrentFile(hzf)) != UNZ_OK)
|
||||||
|
break;
|
||||||
|
zerr = UNZ_ERRNO;
|
||||||
|
HANDLE hFile = CreateFileW(path, GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||||
|
NULL, CREATE_ALWAYS, fileatt, NULL);
|
||||||
|
if (hFile != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
DWORD len = 1024 * 4, cb;
|
||||||
|
LPSTR buf = narrow.GetBuffer(len);
|
||||||
|
for (zerr = UNZ_OK; zerr == UNZ_OK;)
|
||||||
|
{
|
||||||
|
len = zerr = unzReadCurrentFile(hzf, buf, len);
|
||||||
|
if (zerr <= 0)
|
||||||
|
break;
|
||||||
|
zerr = WriteFile(hFile, buf, len, &cb, NULL) && cb == len ? UNZ_OK : UNZ_ERRNO;
|
||||||
|
}
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
unzCloseCurrentFile(hzf);
|
||||||
|
}
|
||||||
|
unzClose(hzf);
|
||||||
|
return zerr == UNZ_END_OF_LIST_OF_FILE ? UNZ_OK : zerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT
|
||||||
|
ExtractZip(LPCWSTR Archive, const CStringW &OutputDir,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
|
{
|
||||||
|
int zerr = ExtractFilesFromZip(Archive, OutputDir, Callback, Cookie);
|
||||||
|
return zerr == UNZ_ERRNO ? GetLastError() : zerr ? ERROR_INTERNAL_ERROR : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT
|
||||||
|
ExtractCab(LPCWSTR Archive, const CStringW &OutputDir,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
|
{
|
||||||
|
if (ExtractFilesFromCab(Archive, OutputDir, Callback, Cookie))
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
UINT err = GetLastError();
|
||||||
|
return err ? err : ERROR_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { IM_STARTPROGRESS = WM_APP, IM_PROGRESS, IM_END };
|
||||||
|
|
||||||
|
static struct CommonInfo
|
||||||
|
{
|
||||||
|
LPCWSTR AppName;
|
||||||
|
HWND hDlg;
|
||||||
|
DWORD Error, Count;
|
||||||
|
BOOL Silent;
|
||||||
|
CRegKey *ArpKey, Entries;
|
||||||
|
|
||||||
|
CommonInfo(LPCWSTR DisplayName, BOOL IsSilent = FALSE)
|
||||||
|
: AppName(DisplayName), hDlg(NULL), Error(0), Count(0), Silent(IsSilent), ArpKey(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
inline HWND GetGuiOwner() const { return Silent ? NULL : hDlg; }
|
||||||
|
} *g_pInfo;
|
||||||
|
|
||||||
|
struct InstallInfo : CommonInfo
|
||||||
|
{
|
||||||
|
CConfigParser &Parser;
|
||||||
|
LPCWSTR ArchivePath, InstallDir, ShortcutFile;
|
||||||
|
CStringW UninstFile, MainApp;
|
||||||
|
UINT InstallDirLen, EntryCount;
|
||||||
|
BOOL PerUser;
|
||||||
|
|
||||||
|
InstallInfo(LPCWSTR AppName, CConfigParser &CP, LPCWSTR Archive)
|
||||||
|
: CommonInfo(AppName), Parser(CP), ArchivePath(Archive)
|
||||||
|
{
|
||||||
|
EntryCount = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static UINT
|
||||||
|
ErrorBox(UINT Error = GetLastError())
|
||||||
|
{
|
||||||
|
if (!Error)
|
||||||
|
Error = ERROR_INTERNAL_ERROR;
|
||||||
|
WCHAR buf[400];
|
||||||
|
UINT fmf = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
|
||||||
|
FormatMessageW(fmf, NULL, Error, 0, buf, _countof(buf), NULL);
|
||||||
|
MessageBoxW(g_pInfo->GetGuiOwner(), buf, 0, MB_OK | MB_ICONSTOP);
|
||||||
|
g_pInfo->Error = Error;
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LPCWSTR
|
||||||
|
GetCommonString(LPCWSTR Name, CStringW &Output, LPCWSTR Default = L"")
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
BOOL found = Info.Parser.GetString(Name, Output);
|
||||||
|
return (found && !Output.IsEmpty() ? Output : Output = Default).GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static LPCWSTR
|
||||||
|
GetGenerateString(LPCWSTR Name, CStringW &Output, LPCWSTR Default = L"")
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
UINT r = Info.Parser.GetSectionString(DB_GENINSTSECTION, Name, Output);
|
||||||
|
return (r ? Output : Output = Default).GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
WriteArpEntry(LPCWSTR Name, LPCWSTR Value, UINT Type = REG_SZ)
|
||||||
|
{
|
||||||
|
// Write a "Add/Remove programs" value if we have a valid uninstaller key
|
||||||
|
if (g_pInfo->ArpKey)
|
||||||
|
{
|
||||||
|
UINT err = g_pInfo->ArpKey->SetStringValue(Name, Value, Type);
|
||||||
|
if (err)
|
||||||
|
ErrorBox(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
AddEntry(WCHAR Type, LPCWSTR Value)
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
CStringW name;
|
||||||
|
name.Format(L"%c%u", Type, ++Info.EntryCount);
|
||||||
|
UINT err = Info.Entries.SetStringValue(name, Value);
|
||||||
|
if (err)
|
||||||
|
ErrorBox(err);
|
||||||
|
return !err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT
|
||||||
|
GetCustomIconPath(InstallInfo &Info, CStringW &Path)
|
||||||
|
{
|
||||||
|
if (*GetGenerateString(DB_GENINST_ICON, Path))
|
||||||
|
{
|
||||||
|
Path = BuildPath(Info.InstallDir, Path);
|
||||||
|
int idx = PathParseIconLocation(Path.GetBuffer());
|
||||||
|
Path.ReleaseBuffer();
|
||||||
|
HICON hIco = NULL;
|
||||||
|
if (ExtractIconExW(Path, idx, &hIco, NULL, 1))
|
||||||
|
DestroyIcon(hIco);
|
||||||
|
if (idx)
|
||||||
|
Path.AppendFormat(L",%d", idx);
|
||||||
|
return hIco ? S_OK : S_FALSE;
|
||||||
|
}
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
GetLocalizedSMFolderName(LPCWSTR WinVal, LPCWSTR RosInf, LPCWSTR RosVal, CStringW &Output)
|
||||||
|
{
|
||||||
|
CRegKey key;
|
||||||
|
if (key.Open(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", KEY_READ) == ERROR_SUCCESS &&
|
||||||
|
GetRegString(key, WinVal, Output) && !Output.IsEmpty())
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
WCHAR windir[MAX_PATH];
|
||||||
|
GetWindowsDirectoryW(windir, _countof(windir));
|
||||||
|
CStringW path = BuildPath(BuildPath(windir, L"inf"), RosInf), section;
|
||||||
|
DWORD lang = 0, lctype = LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER;
|
||||||
|
if (GetLocaleInfoW(GetUserDefaultLCID(), lctype, (LPWSTR) &lang, sizeof(lang) / sizeof(WCHAR)))
|
||||||
|
{
|
||||||
|
section.Format(L"Strings.%.4x", lang);
|
||||||
|
if (ReadIniValue(path, section, RosVal, Output) > 0)
|
||||||
|
return TRUE;
|
||||||
|
section.Format(L"Strings.%.2x", PRIMARYLANGID(lang));
|
||||||
|
if (ReadIniValue(path, section, RosVal, Output) > 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return ReadIniValue(path, L"Strings", RosVal, Output) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
CreateShortcut(const CStringW &Target)
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
UINT csidl = Info.PerUser ? CSIDL_PROGRAMS : CSIDL_COMMON_PROGRAMS;
|
||||||
|
CStringW rel = Info.ShortcutFile, path, dir, tmp;
|
||||||
|
|
||||||
|
if (FAILED(GetSpecialPath(csidl, path, Info.GetGuiOwner())))
|
||||||
|
return TRUE; // Pretend everything is OK
|
||||||
|
|
||||||
|
int cat;
|
||||||
|
if (Info.Parser.GetInt(DB_CATEGORY, cat) && cat == ENUM_CAT_GAMES)
|
||||||
|
{
|
||||||
|
// Try to find the name of the Games folder in the Start Menu
|
||||||
|
if (GetLocalizedSMFolderName(L"SM_GamesName", L"shortcuts.inf", L"Games", tmp))
|
||||||
|
{
|
||||||
|
path = BuildPath(path, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHPathPrepareForWrite will prepare the necessary directories.
|
||||||
|
// Windows and ReactOS SHPathPrepareForWrite do not support '/'.
|
||||||
|
rel.Replace('/', '\\');
|
||||||
|
path = BuildPath(path, rel.GetString());
|
||||||
|
UINT SHPPFW = SHPPFW_DIRCREATE | SHPPFW_IGNOREFILENAME;
|
||||||
|
HRESULT hr = SHPathPrepareForWriteW(Info.GetGuiOwner(), NULL, path, SHPPFW);
|
||||||
|
if ((Info.Error = ErrorFromHResult(hr)) != 0)
|
||||||
|
{
|
||||||
|
ErrorBox(Info.Error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IShellLinkW> link;
|
||||||
|
hr = link.CoCreateInstance(CLSID_ShellLink, IID_IShellLinkW);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(hr = link->SetPath(Target)))
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(GetCustomIconPath(Info, tmp)))
|
||||||
|
{
|
||||||
|
LPWSTR p = tmp.GetBuffer();
|
||||||
|
int idx = PathParseIconLocation(p);
|
||||||
|
link->SetIconLocation(p, idx);
|
||||||
|
}
|
||||||
|
CComPtr<IPersistFile> persist;
|
||||||
|
if (SUCCEEDED(hr = link->QueryInterface(IID_IPersistFile, (void**)&persist)))
|
||||||
|
{
|
||||||
|
hr = persist->Save(path, FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
if (AddEntry(UNOP_FILE, path))
|
||||||
|
{
|
||||||
|
SplitFileAndDirectory(path, &dir);
|
||||||
|
AddEntry(UNOP_EMPTYDIR, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorBox(ErrorFromHResult(hr));
|
||||||
|
}
|
||||||
|
return !Info.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
InstallFiles(const CStringW &SourceDirBase, const CStringW &Spec,
|
||||||
|
const CStringW &DestinationDir)
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
CStringW sourcedir, filespec;
|
||||||
|
filespec = SplitFileAndDirectory(Spec, &sourcedir); // Split "[OptionalDir\]*.ext"
|
||||||
|
sourcedir = BuildPath(SourceDirBase, sourcedir);
|
||||||
|
BOOL success = TRUE;
|
||||||
|
WIN32_FIND_DATAW wfd;
|
||||||
|
HANDLE hFind = FindFirstFileW(BuildPath(sourcedir, filespec), &wfd);
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_FILE_NOT_FOUND)
|
||||||
|
return TRUE;
|
||||||
|
else
|
||||||
|
return !ErrorBox(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
CStringW from = BuildPath(sourcedir, wfd.cFileName);
|
||||||
|
CStringW to = BuildPath(DestinationDir, wfd.cFileName);
|
||||||
|
CStringW uninstpath = to.Mid(Info.InstallDirLen - 1);
|
||||||
|
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
{
|
||||||
|
LPWSTR p = wfd.cFileName;
|
||||||
|
BOOL dots = p[0] == '.' && (!p[1] || (p[1] == '.' && !p[2]));
|
||||||
|
if (!dots)
|
||||||
|
{
|
||||||
|
Info.Error = CreateDirectoryTree(to);
|
||||||
|
if (Info.Error)
|
||||||
|
{
|
||||||
|
success = !ErrorBox(Info.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
success = AddEntry(UNOP_EMPTYDIR, uninstpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
success = InstallFiles(from, filespec, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
success = MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
if (Info.MainApp.IsEmpty())
|
||||||
|
{
|
||||||
|
Info.MainApp = to;
|
||||||
|
}
|
||||||
|
success = AddEntry(UNOP_FILE, uninstpath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorBox();
|
||||||
|
}
|
||||||
|
SendMessage(g_pInfo->hDlg, IM_PROGRESS, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success || !FindNextFileW(hFind, &wfd))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FindClose(hFind);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AddUninstallOperationsFromDB(LPCWSTR Name, WCHAR UnOp, CStringW PathPrefix = CStringW(L""))
|
||||||
|
{
|
||||||
|
CStringW item, tmp;
|
||||||
|
if (*GetGenerateString(Name, tmp))
|
||||||
|
{
|
||||||
|
for (int pos = 1; pos > 0;)
|
||||||
|
{
|
||||||
|
pos = tmp.Find(L'|');
|
||||||
|
item = pos <= 0 ? tmp : tmp.Left(pos);
|
||||||
|
tmp = tmp.Mid(pos + 1);
|
||||||
|
AddEntry(UnOp, PathPrefix + item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL CALLBACK
|
||||||
|
ExtractCallback(const EXTRACTCALLBACKINFO &, void *Cookie)
|
||||||
|
{
|
||||||
|
InstallInfo &Info = *(InstallInfo *) Cookie;
|
||||||
|
Info.Count += 1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD CALLBACK
|
||||||
|
ExtractAndInstallThread(LPVOID Parameter)
|
||||||
|
{
|
||||||
|
const BOOL PerUserModeDefault = TRUE;
|
||||||
|
InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo);
|
||||||
|
LPCWSTR AppName = Info.AppName, Archive = Info.ArchivePath, None = L"!";
|
||||||
|
CStringW installdir, tempdir, files, shortcut, tmp;
|
||||||
|
HRESULT hr;
|
||||||
|
CRegKey arpkey;
|
||||||
|
Info.ArpKey = &arpkey;
|
||||||
|
|
||||||
|
if (!*GetGenerateString(DB_GENINST_FILES, files, L"*.exe|*.*"))
|
||||||
|
return ErrorBox(ERROR_BAD_FORMAT);
|
||||||
|
|
||||||
|
GetCommonString(DB_SCOPE, tmp);
|
||||||
|
if (tmp.CompareNoCase(L"User") == 0)
|
||||||
|
Info.PerUser = TRUE;
|
||||||
|
else if (tmp.CompareNoCase(L"Machine") == 0)
|
||||||
|
Info.PerUser = FALSE;
|
||||||
|
else
|
||||||
|
Info.PerUser = PerUserModeDefault;
|
||||||
|
|
||||||
|
hr = GetProgramFilesPath(installdir, Info.PerUser, Info.GetGuiOwner());
|
||||||
|
if ((Info.Error = ErrorFromHResult(hr)) != 0)
|
||||||
|
return ErrorBox(Info.Error);
|
||||||
|
|
||||||
|
GetGenerateString(DB_GENINST_DIR, tmp);
|
||||||
|
if (tmp.Find('%') == 0 && ExpandEnvStrings(tmp))
|
||||||
|
installdir = tmp;
|
||||||
|
else if (tmp.Compare(None))
|
||||||
|
installdir = BuildPath(installdir, tmp.IsEmpty() ? AppName : tmp.GetString());
|
||||||
|
Info.InstallDir = installdir.GetString();
|
||||||
|
Info.InstallDirLen = installdir.GetLength() + 1;
|
||||||
|
hr = SHPathPrepareForWriteW(Info.GetGuiOwner(), NULL, installdir, SHPPFW_DIRCREATE);
|
||||||
|
if ((Info.Error = ErrorFromHResult(hr)) != 0)
|
||||||
|
return ErrorBox(Info.Error);
|
||||||
|
|
||||||
|
// Create the destination directory, and inside it, a temporary directory
|
||||||
|
// where we will extract the archive to before moving the files to their
|
||||||
|
// final location (adding uninstall entries as we go)
|
||||||
|
tempdir.Format(L"%s\\~RAM%u.tmp", installdir.GetString(), GetCurrentProcessId());
|
||||||
|
Info.Error = CreateDirectoryTree(tempdir.GetString());
|
||||||
|
if (Info.Error)
|
||||||
|
return ErrorBox(Info.Error);
|
||||||
|
|
||||||
|
if (!*GetGenerateString(DB_GENINST_LNK, shortcut))
|
||||||
|
shortcut.Format(L"%s.lnk", AppName);
|
||||||
|
Info.ShortcutFile = shortcut.Compare(None) ? shortcut.GetString() : NULL;
|
||||||
|
|
||||||
|
// Create the uninstall registration key
|
||||||
|
LPCWSTR arpkeyname = AppName;
|
||||||
|
tmp = BuildPath(REGPATH_UNINSTALL, arpkeyname);
|
||||||
|
HKEY hRoot = Info.PerUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
||||||
|
REGSAM regsam = KEY_READ | KEY_WRITE | (IsSystem64Bit() ? KEY_WOW64_64KEY : KEY_WOW64_32KEY);
|
||||||
|
Info.Error = arpkey.Create(hRoot, tmp, NULL, REG_OPTION_NON_VOLATILE, regsam);
|
||||||
|
if (!Info.Error)
|
||||||
|
{
|
||||||
|
arpkey.RecurseDeleteKey(GENERATE_ARPSUBKEY);
|
||||||
|
Info.Error = Info.Entries.Create(arpkey, GENERATE_ARPSUBKEY);
|
||||||
|
}
|
||||||
|
if (Info.Error)
|
||||||
|
ErrorBox(Info.Error);
|
||||||
|
|
||||||
|
if (!Info.Error)
|
||||||
|
{
|
||||||
|
BOOL isCab = SplitFileAndDirectory(Archive).Right(4).CompareNoCase(L".cab") == 0;
|
||||||
|
Info.Error = isCab ? ExtractCab(Archive, tempdir, ExtractCallback, &Info)
|
||||||
|
: ExtractZip(Archive, tempdir, ExtractCallback, &Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Info.Error)
|
||||||
|
{
|
||||||
|
// We now know how many files we extracted, change from marquee to normal progress bar.
|
||||||
|
SendMessage(Info.hDlg, IM_STARTPROGRESS, 0, 0);
|
||||||
|
|
||||||
|
for (int pos = 1; pos > 0 && !Info.Error;)
|
||||||
|
{
|
||||||
|
pos = files.Find(L'|');
|
||||||
|
CStringW item = pos <= 0 ? files : files.Left(pos);
|
||||||
|
files = files.Mid(pos + 1);
|
||||||
|
InstallFiles(tempdir, item, installdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Info.MainApp.IsEmpty())
|
||||||
|
{
|
||||||
|
AddUninstallOperationsFromDB(DB_GENINST_DELREG, UNOP_REGKEY);
|
||||||
|
AddUninstallOperationsFromDB(DB_GENINST_DELREGEMPTY, UNOP_EMPTYREGKEY);
|
||||||
|
AddUninstallOperationsFromDB(DB_GENINST_DELFILE, UNOP_FILE, L"\\");
|
||||||
|
AddUninstallOperationsFromDB(DB_GENINST_DELDIR, UNOP_DIR, L"\\");
|
||||||
|
AddUninstallOperationsFromDB(DB_GENINST_DELDIREMPTY, UNOP_EMPTYDIR, L"\\");
|
||||||
|
AddEntry(UNOP_EMPTYDIR, L"\\");
|
||||||
|
|
||||||
|
WriteArpEntry(L"DisplayName", AppName);
|
||||||
|
WriteArpEntry(L"InstallLocation", Info.InstallDir); // Note: This value is used by the uninstaller!
|
||||||
|
|
||||||
|
LPWSTR p = tmp.GetBuffer(1 + MAX_PATH);
|
||||||
|
p[0] = L'\"';
|
||||||
|
GetModuleFileName(NULL, p + 1, MAX_PATH);
|
||||||
|
tmp.ReleaseBuffer();
|
||||||
|
UINT cch = tmp.GetLength(), bitness = IsSystem64Bit() ? 64 : 32;
|
||||||
|
WCHAR modechar = Info.PerUser ? 'U' : 'M';
|
||||||
|
LPCWSTR unparamsfmt = L"\" /" CMD_KEY_UNINSTALL L" /K%s \"%c%d\\%s\"";
|
||||||
|
(tmp = tmp.Mid(0, cch)).AppendFormat(unparamsfmt, L"", modechar, bitness, arpkeyname);
|
||||||
|
WriteArpEntry(L"UninstallString", tmp);
|
||||||
|
(tmp = tmp.Mid(0, cch)).AppendFormat(unparamsfmt, L" /S", modechar, bitness, arpkeyname);
|
||||||
|
WriteArpEntry(L"QuietUninstallString", tmp);
|
||||||
|
|
||||||
|
if (GetCustomIconPath(Info, tmp) != S_OK)
|
||||||
|
tmp = Info.MainApp;
|
||||||
|
WriteArpEntry(L"DisplayIcon", tmp);
|
||||||
|
|
||||||
|
if (*GetCommonString(DB_VERSION, tmp))
|
||||||
|
WriteArpEntry(L"DisplayVersion", tmp);
|
||||||
|
|
||||||
|
SYSTEMTIME st;
|
||||||
|
GetSystemTime(&st);
|
||||||
|
tmp.Format(L"%.4u%.2u%.2u", st.wYear, st.wMonth, st.wDay);
|
||||||
|
WriteArpEntry(L"InstallDate", tmp);
|
||||||
|
|
||||||
|
if (*GetCommonString(DB_PUBLISHER, tmp))
|
||||||
|
WriteArpEntry(L"Publisher", tmp);
|
||||||
|
|
||||||
|
#if DBG
|
||||||
|
tmp.Format(L"sys64=%d rapps%d", IsSystem64Bit(), sizeof(void*) * 8);
|
||||||
|
WriteArpEntry(L"_DEBUG", tmp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Info.Error && Info.ShortcutFile)
|
||||||
|
{
|
||||||
|
CreateShortcut(Info.MainApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteDirectoryTree(tempdir.GetString());
|
||||||
|
RemoveDirectory(installdir.GetString()); // This is harmless even if we installed something
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD CALLBACK
|
||||||
|
WorkerThread(LPVOID Parameter)
|
||||||
|
{
|
||||||
|
((LPTHREAD_START_ROUTINE)Parameter)(NULL);
|
||||||
|
return SendMessage(g_pInfo->hDlg, IM_END, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static INT_PTR CALLBACK
|
||||||
|
UIDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
HWND hPB = GetDlgItem(hDlg, 1);
|
||||||
|
switch(uMsg)
|
||||||
|
{
|
||||||
|
case IM_STARTPROGRESS:
|
||||||
|
SetWindowLongPtr(hPB, GWL_STYLE, WS_CHILD | WS_VISIBLE);
|
||||||
|
SendMessageW(hPB, PBM_SETMARQUEE, FALSE, 0);
|
||||||
|
SendMessageW(hPB, PBM_SETRANGE32, 0, g_pInfo->Count);
|
||||||
|
SendMessageW(hPB, PBM_SETPOS, 0, 0);
|
||||||
|
break;
|
||||||
|
case IM_PROGRESS:
|
||||||
|
SendMessageW(hPB, PBM_DELTAPOS, 1, 0);
|
||||||
|
break;
|
||||||
|
case IM_END:
|
||||||
|
DestroyWindow(hDlg);
|
||||||
|
break;
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
{
|
||||||
|
SendMessageW(hPB, PBM_SETMARQUEE, TRUE, 0);
|
||||||
|
g_pInfo->hDlg = hDlg;
|
||||||
|
HICON hIco = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
|
||||||
|
SendMessageW(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIco);
|
||||||
|
SendMessageW(hDlg, WM_SETTEXT, 0, (LPARAM)g_pInfo->AppName);
|
||||||
|
if (!SHCreateThread(WorkerThread, (void*)lParam, CTF_COINIT, NULL))
|
||||||
|
{
|
||||||
|
ErrorBox();
|
||||||
|
SendMessageW(hDlg, IM_END, 0, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_CLOSE:
|
||||||
|
return TRUE;
|
||||||
|
case WM_DESTROY:
|
||||||
|
PostMessage(NULL, WM_QUIT, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
CreateUI(BOOL Silent, LPTHREAD_START_ROUTINE ThreadProc)
|
||||||
|
{
|
||||||
|
enum { DLGW = 150, DLGH = 20, PAD = 4, PADx2 = PAD * 2, CHILDCOUNT = 1 };
|
||||||
|
const UINT DlgStyle = WS_CAPTION | DS_MODALFRAME | DS_NOFAILCREATE | DS_CENTER;
|
||||||
|
static const WORD DlgTmpl[] =
|
||||||
|
{
|
||||||
|
LOWORD(DlgStyle), HIWORD(DlgStyle), 0, 0, CHILDCOUNT, 0, 0, DLGW, DLGH, 0, 0, 0,
|
||||||
|
PBS_MARQUEE, HIWORD(WS_CHILD | WS_VISIBLE), 0, 0, PAD, PAD, DLGW - PADx2, DLGH - PADx2, 1,
|
||||||
|
'm', 's', 'c', 't', 'l', 's', '_', 'p', 'r', 'o', 'g', 'r', 'e', 's', 's', '3', '2', 0, 0,
|
||||||
|
};
|
||||||
|
HWND hWnd = CreateDialogIndirectParamW(NULL, (LPCDLGTEMPLATE)DlgTmpl, NULL,
|
||||||
|
UIDlgProc, (LPARAM)ThreadProc);
|
||||||
|
if (!hWnd)
|
||||||
|
{
|
||||||
|
ErrorBox();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else if (!Silent)
|
||||||
|
{
|
||||||
|
ShowWindow(hWnd, SW_SHOW);
|
||||||
|
}
|
||||||
|
MSG Msg;
|
||||||
|
while (GetMessageW(&Msg, NULL, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(&Msg);
|
||||||
|
DispatchMessageW(&Msg);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
ExtractAndRunGeneratedInstaller(const CAvailableApplicationInfo &AppInfo, LPCWSTR Archive)
|
||||||
|
{
|
||||||
|
InstallInfo Info(AppInfo.szDisplayName, *AppInfo.GetConfigParser(), Archive);
|
||||||
|
g_pInfo = &Info;
|
||||||
|
return CreateUI(Info.Silent, ExtractAndInstallThread) ? !Info.Error : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UninstallInfo : CommonInfo
|
||||||
|
{
|
||||||
|
CInstalledApplicationInfo &AppInfo;
|
||||||
|
UninstallInfo(CInstalledApplicationInfo &Info, BOOL IsSilent)
|
||||||
|
: CommonInfo(Info.szDisplayName, IsSilent), AppInfo(Info)
|
||||||
|
{
|
||||||
|
ArpKey = &Info.GetRegKey();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UninstallStage
|
||||||
|
{
|
||||||
|
US_ITEMS,
|
||||||
|
US_CONTAINERS,
|
||||||
|
UINSTALLSTAGECOUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD CALLBACK
|
||||||
|
UninstallThread(LPVOID Parameter)
|
||||||
|
{
|
||||||
|
UninstallInfo &Info = *static_cast<UninstallInfo *>(g_pInfo);
|
||||||
|
|
||||||
|
CStringW tmp, path, installdir;
|
||||||
|
path.LoadString(IDS_INSTGEN_CONFIRMUNINST);
|
||||||
|
tmp.Format(path.GetString(), Info.AppName);
|
||||||
|
if (!Info.Silent &&
|
||||||
|
MessageBox(Info.GetGuiOwner(), tmp, Info.AppName, MB_YESNO | MB_ICONQUESTION) != IDYES)
|
||||||
|
{
|
||||||
|
Info.Error = ERROR_CANCELLED;
|
||||||
|
SendMessage(Info.hDlg, IM_END, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info.Error = Info.Entries.Open(*Info.ArpKey, GENERATE_ARPSUBKEY, KEY_READ);
|
||||||
|
if (Info.Error)
|
||||||
|
return ErrorBox(Info.Error);
|
||||||
|
|
||||||
|
RegQueryInfoKey(Info.Entries, NULL, NULL, NULL, NULL, NULL, NULL, &Info.Count, NULL, NULL, NULL, NULL);
|
||||||
|
Info.Count *= UINSTALLSTAGECOUNT;
|
||||||
|
SendMessage(Info.hDlg, IM_STARTPROGRESS, 0, 0);
|
||||||
|
|
||||||
|
if (!GetRegString(*Info.ArpKey, L"InstallLocation", installdir) || installdir.IsEmpty())
|
||||||
|
return ErrorBox(ERROR_INVALID_NAME);
|
||||||
|
|
||||||
|
for (UINT stage = 0; stage < UINSTALLSTAGECOUNT; ++stage)
|
||||||
|
{
|
||||||
|
for (UINT vi = 0;; ++vi)
|
||||||
|
{
|
||||||
|
WCHAR value[MAX_PATH], data[MAX_PATH * 2];
|
||||||
|
DWORD valsize = _countof(value), size = sizeof(data) - sizeof(WCHAR), rt;
|
||||||
|
data[_countof(data) - 1] = UNICODE_NULL;
|
||||||
|
UINT err = RegEnumValue(Info.Entries, vi, value, &valsize, NULL, &rt, (BYTE*)data, &size);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (err != ERROR_NO_MORE_ITEMS)
|
||||||
|
{
|
||||||
|
return ErrorBox(err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPCWSTR str = data;
|
||||||
|
WORD op = value[0];
|
||||||
|
switch(*data ? MAKEWORD(stage, op) : 0)
|
||||||
|
{
|
||||||
|
case MAKEWORD(US_ITEMS, UNOP_REGKEY):
|
||||||
|
case MAKEWORD(US_CONTAINERS, UNOP_EMPTYREGKEY):
|
||||||
|
{
|
||||||
|
REGSAM wowsam = 0;
|
||||||
|
HKEY hKey = NULL;
|
||||||
|
path.Format(L"%.4s", data);
|
||||||
|
if (path.CompareNoCase(L"HKCR") == 0)
|
||||||
|
hKey = HKEY_CLASSES_ROOT;
|
||||||
|
else if (path.CompareNoCase(L"HKCU") == 0)
|
||||||
|
hKey = HKEY_CURRENT_USER;
|
||||||
|
else if (path.CompareNoCase(L"HKLM") == 0)
|
||||||
|
hKey = HKEY_LOCAL_MACHINE;
|
||||||
|
|
||||||
|
if (data[4] == '6' && data[5] == '4')
|
||||||
|
wowsam = KEY_WOW64_64KEY;
|
||||||
|
else if (data[4] == '3' && data[5] == '2')
|
||||||
|
wowsam = KEY_WOW64_32KEY;
|
||||||
|
|
||||||
|
str = &data[wowsam ? 6 : 4];
|
||||||
|
if (!hKey || *str != L'\\')
|
||||||
|
break;
|
||||||
|
tmp = SplitFileAndDirectory(++str, &path);
|
||||||
|
if (!tmp.IsEmpty() && !path.IsEmpty())
|
||||||
|
{
|
||||||
|
CRegKey key;
|
||||||
|
err = key.Open(hKey, path, DELETE | wowsam);
|
||||||
|
if (err == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (op == UNOP_REGKEY)
|
||||||
|
err = key.RecurseDeleteKey(tmp);
|
||||||
|
else if (RegKeyHasValues(hKey, str, wowsam) == S_FALSE)
|
||||||
|
err = key.DeleteSubKey(tmp);
|
||||||
|
}
|
||||||
|
switch(err)
|
||||||
|
{
|
||||||
|
case ERROR_SUCCESS:
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
case ERROR_PATH_NOT_FOUND:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ErrorBox(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MAKEWORD(US_ITEMS, UNOP_FILE):
|
||||||
|
{
|
||||||
|
if (*data == L'\\')
|
||||||
|
str = (path = BuildPath(installdir, data));
|
||||||
|
|
||||||
|
if (!DeleteFile(str))
|
||||||
|
{
|
||||||
|
err = GetLastError();
|
||||||
|
if (err != ERROR_FILE_NOT_FOUND)
|
||||||
|
{
|
||||||
|
return ErrorBox(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MAKEWORD(US_CONTAINERS, UNOP_EMPTYDIR):
|
||||||
|
case MAKEWORD(US_CONTAINERS, UNOP_DIR):
|
||||||
|
{
|
||||||
|
if (*data == L'\\')
|
||||||
|
str = (path = BuildPath(installdir, data));
|
||||||
|
|
||||||
|
if (op == UNOP_DIR)
|
||||||
|
DeleteDirectoryTree(str, Info.GetGuiOwner());
|
||||||
|
else
|
||||||
|
RemoveDirectory(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SendMessage(Info.hDlg, IM_PROGRESS, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Info.Error)
|
||||||
|
{
|
||||||
|
Info.Error = CAppDB::RemoveInstalledAppFromRegistry(&Info.AppInfo);
|
||||||
|
if (Info.Error)
|
||||||
|
return ErrorBox(Info.Error);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
UninstallGenerated(CInstalledApplicationInfo &AppInfo, UninstallCommandFlags Flags)
|
||||||
|
{
|
||||||
|
UninstallInfo Info(AppInfo, Flags & UCF_SILENT);
|
||||||
|
g_pInfo = &Info;
|
||||||
|
return CreateUI(Info.Silent, UninstallThread) ? !Info.Error : FALSE;
|
||||||
|
}
|
|
@ -275,7 +275,7 @@ CMainWindow::RemoveSelectedAppFromRegistry()
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
|
if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
|
||||||
return m_Db->RemoveInstalledAppFromRegistry(InstalledApp);
|
return m_Db->RemoveInstalledAppFromRegistry(InstalledApp) == ERROR_SUCCESS;
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ CMainWindow::UninstallSelectedApp(BOOL bModify)
|
||||||
if (!InstalledApp)
|
if (!InstalledApp)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return InstalledApp->UninstallApplication(bModify);
|
return InstalledApp->UninstallApplication(bModify ? UCF_MODIFY : UCF_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
|
|
@ -15,13 +15,20 @@ class CAppDB
|
||||||
BOOL
|
BOOL
|
||||||
EnumerateFiles();
|
EnumerateFiles();
|
||||||
|
|
||||||
|
static CInstalledApplicationInfo *
|
||||||
|
EnumerateRegistry(CAtlList<CAppInfo *> *List, LPCWSTR Name);
|
||||||
|
static CInstalledApplicationInfo *
|
||||||
|
CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAppDB(const CStringW &path);
|
CAppDB(const CStringW &path);
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const;
|
GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const;
|
||||||
|
CAvailableApplicationInfo *
|
||||||
|
FindAvailableByPackageName(const CStringW &name);
|
||||||
CAppInfo *
|
CAppInfo *
|
||||||
FindByPackageName(const CStringW &name);
|
FindByPackageName(const CStringW &name) { return FindAvailableByPackageName(name); }
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
UpdateAvailable();
|
UpdateAvailable();
|
||||||
|
@ -30,9 +37,16 @@ class CAppDB
|
||||||
VOID
|
VOID
|
||||||
RemoveCached();
|
RemoveCached();
|
||||||
|
|
||||||
BOOL
|
static DWORD
|
||||||
RemoveInstalledAppFromRegistry(const CAppInfo *Info);
|
RemoveInstalledAppFromRegistry(const CAppInfo *Info);
|
||||||
|
|
||||||
|
static CInstalledApplicationInfo *
|
||||||
|
CreateInstalledAppByRegistryKey(LPCWSTR Name);
|
||||||
|
static CInstalledApplicationInfo *
|
||||||
|
CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam);
|
||||||
|
static HKEY
|
||||||
|
EnumInstalledRootKey(UINT Index, REGSAM &RegSam);
|
||||||
|
|
||||||
size_t GetAvailableCount() const
|
size_t GetAvailableCount() const
|
||||||
{
|
{
|
||||||
return m_Available.GetCount();
|
return m_Available.GetCount();
|
||||||
|
|
|
@ -63,7 +63,31 @@ IsInstalledEnum(INT x)
|
||||||
return (x >= ENUM_INSTALLED_MIN && x <= ENUM_INSTALLED_MAX);
|
return (x >= ENUM_INSTALLED_MIN && x <= ENUM_INSTALLED_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum UninstallCommandFlags
|
||||||
|
{
|
||||||
|
UCF_NONE = 0x00,
|
||||||
|
UCF_MODIFY = 0x01,
|
||||||
|
UCF_SILENT = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum InstallerType
|
||||||
|
{
|
||||||
|
INSTALLER_UNKNOWN,
|
||||||
|
INSTALLER_GENERATE, // .zip file automatically converted to installer by rapps
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DB_VERSION L"Version"
|
||||||
|
#define DB_CATEGORY L"Category"
|
||||||
|
#define DB_PUBLISHER L"Publisher"
|
||||||
|
#define DB_REGNAME L"RegName"
|
||||||
|
#define DB_INSTALLER L"Installer"
|
||||||
|
#define DB_SCOPE L"Scope" // User or Machine
|
||||||
|
|
||||||
|
#define DB_GENINSTSECTION L"Generate"
|
||||||
|
#define GENERATE_ARPSUBKEY L"RApps" // Our uninstall data is stored here
|
||||||
|
|
||||||
class CAppRichEdit;
|
class CAppRichEdit;
|
||||||
|
class CConfigParser;
|
||||||
|
|
||||||
class CAppInfo
|
class CAppInfo
|
||||||
{
|
{
|
||||||
|
@ -93,13 +117,15 @@ class CAppInfo
|
||||||
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const = 0;
|
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const = 0;
|
||||||
virtual VOID
|
virtual VOID
|
||||||
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) = 0;
|
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) = 0;
|
||||||
|
virtual InstallerType
|
||||||
|
GetInstallerType() const { return INSTALLER_UNKNOWN; }
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
UninstallApplication(BOOL bModify) = 0;
|
UninstallApplication(UninstallCommandFlags Flags) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CAvailableApplicationInfo : public CAppInfo
|
class CAvailableApplicationInfo : public CAppInfo
|
||||||
{
|
{
|
||||||
class CConfigParser *m_Parser;
|
CConfigParser *m_Parser;
|
||||||
CSimpleArray<CStringW> m_szScrnshotLocation;
|
CSimpleArray<CStringW> m_szScrnshotLocation;
|
||||||
bool m_ScrnshotRetrieved;
|
bool m_ScrnshotRetrieved;
|
||||||
CStringW m_szUrlDownload;
|
CStringW m_szUrlDownload;
|
||||||
|
@ -125,6 +151,9 @@ class CAvailableApplicationInfo : public CAppInfo
|
||||||
const CPathW &BasePath);
|
const CPathW &BasePath);
|
||||||
~CAvailableApplicationInfo();
|
~CAvailableApplicationInfo();
|
||||||
|
|
||||||
|
CConfigParser *
|
||||||
|
GetConfigParser() const { return m_Parser; }
|
||||||
|
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
Valid() const override;
|
Valid() const override;
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
|
@ -139,8 +168,10 @@ class CAvailableApplicationInfo : public CAppInfo
|
||||||
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const override;
|
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const override;
|
||||||
virtual VOID
|
virtual VOID
|
||||||
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) override;
|
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) override;
|
||||||
|
virtual InstallerType
|
||||||
|
GetInstallerType() const override;
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
UninstallApplication(BOOL bModify) override;
|
UninstallApplication(UninstallCommandFlags Flags) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CInstalledApplicationInfo : public CAppInfo
|
class CInstalledApplicationInfo : public CAppInfo
|
||||||
|
@ -163,10 +194,12 @@ class CInstalledApplicationInfo : public CAppInfo
|
||||||
RetrieveUninstallStrings();
|
RetrieveUninstallStrings();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const int iKeyIndex;
|
UINT m_KeyInfo;
|
||||||
CInstalledApplicationInfo(HKEY Key, const CStringW &KeyName, AppsCategories Category, int KeyIndex);
|
CInstalledApplicationInfo(HKEY Key, const CStringW &KeyName, AppsCategories Category, UINT KeyInfo);
|
||||||
~CInstalledApplicationInfo();
|
~CInstalledApplicationInfo();
|
||||||
|
|
||||||
|
CRegKey& GetRegKey() { return m_hKey; }
|
||||||
|
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
Valid() const override;
|
Valid() const override;
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
|
@ -181,6 +214,13 @@ class CInstalledApplicationInfo : public CAppInfo
|
||||||
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const override;
|
GetDownloadInfo(CStringW &Url, CStringW &Sha1, ULONG &SizeInBytes) const override;
|
||||||
virtual VOID
|
virtual VOID
|
||||||
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) override;
|
GetDisplayInfo(CStringW &License, CStringW &Size, CStringW &UrlSite, CStringW &UrlDownload) override;
|
||||||
|
virtual InstallerType
|
||||||
|
GetInstallerType() const override;
|
||||||
virtual BOOL
|
virtual BOOL
|
||||||
UninstallApplication(BOOL bModify) override;
|
UninstallApplication(UninstallCommandFlags Flags) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
UninstallGenerated(CInstalledApplicationInfo &AppInfo, UninstallCommandFlags Flags);
|
||||||
|
BOOL
|
||||||
|
ExtractAndRunGeneratedInstaller(const CAvailableApplicationInfo &AppInfo, LPCWSTR Archive);
|
||||||
|
|
|
@ -19,4 +19,10 @@ class CConfigParser
|
||||||
GetString(const CStringW &KeyName, CStringW &ResultString);
|
GetString(const CStringW &KeyName, CStringW &ResultString);
|
||||||
BOOL
|
BOOL
|
||||||
GetInt(const CStringW &KeyName, INT &iResult);
|
GetInt(const CStringW &KeyName, INT &iResult);
|
||||||
|
|
||||||
|
UINT
|
||||||
|
GetSectionString(LPCWSTR Section, LPCWSTR Name, CStringW &Result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
ReadIniValue(LPCWSTR File, LPCWSTR Section, LPCWSTR Name, CStringW &Output);
|
||||||
|
|
|
@ -16,6 +16,16 @@
|
||||||
#define CurrentArchitecture L"ppc"
|
#define CurrentArchitecture L"ppc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline UINT
|
||||||
|
ErrorFromHResult(HRESULT hr)
|
||||||
|
{
|
||||||
|
// Attempt to extract the original Win32 error code from the HRESULT
|
||||||
|
if (HIWORD(hr) == HIWORD(HRESULT_FROM_WIN32(!0)))
|
||||||
|
return LOWORD(hr);
|
||||||
|
else
|
||||||
|
return hr >= 0 ? ERROR_SUCCESS : hr;
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
CopyTextToClipboard(LPCWSTR lpszText);
|
CopyTextToClipboard(LPCWSTR lpszText);
|
||||||
VOID
|
VOID
|
||||||
|
@ -34,8 +44,28 @@ WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg);
|
||||||
BOOL
|
BOOL
|
||||||
GetInstalledVersion(CStringW *pszVersion, const CStringW &szRegName);
|
GetInstalledVersion(CStringW *pszVersion, const CStringW &szRegName);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const CStringW &ItemPath;
|
||||||
|
UINT64 UncompressedSize;
|
||||||
|
UINT FileAttributes;
|
||||||
|
} EXTRACTCALLBACKINFO;
|
||||||
|
typedef BOOL (CALLBACK*EXTRACTCALLBACK)(const EXTRACTCALLBACKINFO &Info, void *Cookie);
|
||||||
|
|
||||||
|
static inline BOOL
|
||||||
|
NotifyFileExtractCallback(const CStringW &ItemPath, UINT64 UncompressedSize, UINT FileAttributes,
|
||||||
|
EXTRACTCALLBACK Callback, void *Cookie)
|
||||||
|
{
|
||||||
|
EXTRACTCALLBACKINFO eci = { ItemPath, UncompressedSize, FileAttributes };
|
||||||
|
return Callback ? Callback(eci, Cookie) : TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir);
|
ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir,
|
||||||
|
EXTRACTCALLBACK Callback = NULL, void *Cookie = NULL);
|
||||||
|
BOOL
|
||||||
|
ExtractFilesFromCab(LPCWSTR FullCabPath, const CStringW &szOutputDir,
|
||||||
|
EXTRACTCALLBACK Callback = NULL, void *Cookie = NULL);
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
IsSystem64Bit();
|
IsSystem64Bit();
|
||||||
|
@ -49,6 +79,39 @@ UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime);
|
||||||
BOOL
|
BOOL
|
||||||
SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle);
|
SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle);
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam = 0);
|
||||||
|
LPCWSTR
|
||||||
|
GetRegString(CRegKey &Key, LPCWSTR Name, CStringW &Value);
|
||||||
|
|
||||||
|
bool
|
||||||
|
ExpandEnvStrings(CStringW &Str);
|
||||||
|
|
||||||
|
template <class T> static CStringW
|
||||||
|
BuildPath(const T &Base, LPCWSTR Append)
|
||||||
|
{
|
||||||
|
CStringW path = Base;
|
||||||
|
SIZE_T len = path.GetLength();
|
||||||
|
if (len && path[len - 1] != L'\\' && path[len - 1] != L'/')
|
||||||
|
path += L'\\';
|
||||||
|
while (*Append == L'\\' || *Append == L'/')
|
||||||
|
++Append;
|
||||||
|
return path + Append;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStringW
|
||||||
|
SplitFileAndDirectory(LPCWSTR FullPath, CStringW *pDir = NULL);
|
||||||
|
BOOL
|
||||||
|
DeleteDirectoryTree(LPCWSTR Dir, HWND hwnd = NULL);
|
||||||
|
UINT
|
||||||
|
CreateDirectoryTree(LPCWSTR Dir);
|
||||||
|
HRESULT
|
||||||
|
GetSpecialPath(UINT csidl, CStringW &Path, HWND hwnd = NULL);
|
||||||
|
HRESULT
|
||||||
|
GetKnownPath(REFKNOWNFOLDERID kfid, CStringW &Path, DWORD Flags = KF_FLAG_CREATE);
|
||||||
|
HRESULT
|
||||||
|
GetProgramFilesPath(CStringW &Path, BOOL PerUser, HWND hwnd = NULL);
|
||||||
|
|
||||||
template <class T> class CLocalPtr : public CHeapPtr<T, CLocalAllocator>
|
template <class T> class CLocalPtr : public CHeapPtr<T, CLocalAllocator>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
|
@ -228,6 +228,9 @@
|
||||||
#define IDS_CMD_PACKAGE_NOT_FOUND 959
|
#define IDS_CMD_PACKAGE_NOT_FOUND 959
|
||||||
#define IDS_CMD_PACKAGE_INFO 960
|
#define IDS_CMD_PACKAGE_INFO 960
|
||||||
|
|
||||||
|
/* Generated installer */
|
||||||
|
#define IDS_INSTGEN_CONFIRMUNINST 1000
|
||||||
|
|
||||||
/* Accelerators */
|
/* Accelerators */
|
||||||
#define HOTKEYS 715
|
#define HOTKEYS 715
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define CMD_KEY_APPWIZ L"APPWIZ"
|
#define CMD_KEY_APPWIZ L"APPWIZ"
|
||||||
|
#define CMD_KEY_GENINST L"GENINST"
|
||||||
|
#define CMD_KEY_UNINSTALL L"UNINSTALL"
|
||||||
#define CMD_KEY_INSTALL L"INSTALL"
|
#define CMD_KEY_INSTALL L"INSTALL"
|
||||||
#define CMD_KEY_SETUP L"SETUP"
|
#define CMD_KEY_SETUP L"SETUP"
|
||||||
#define CMD_KEY_FIND L"FIND"
|
#define CMD_KEY_FIND L"FIND"
|
||||||
|
@ -12,6 +14,7 @@
|
||||||
const WCHAR UsageString[] = L"RAPPS \
|
const WCHAR UsageString[] = L"RAPPS \
|
||||||
[/" CMD_KEY_HELP L"] \
|
[/" CMD_KEY_HELP L"] \
|
||||||
[/" CMD_KEY_INSTALL L" packagename] \
|
[/" CMD_KEY_INSTALL L" packagename] \
|
||||||
|
[/" CMD_KEY_UNINSTALL L" packagename|displayname] \
|
||||||
[/" CMD_KEY_SETUP L" filename] \
|
[/" CMD_KEY_SETUP L" filename] \
|
||||||
[/" CMD_KEY_FIND L" string] \
|
[/" CMD_KEY_FIND L" string] \
|
||||||
[/" CMD_KEY_INFO L" packagename]";
|
[/" CMD_KEY_INFO L" packagename]";
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -271,3 +271,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Paket %1 konnte nicht gefunden werden.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Paket %1 konnte nicht gefunden werden.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Informationen über das Paket %1:\n"
|
IDS_CMD_PACKAGE_INFO "Informationen über das Paket %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -271,3 +271,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "No se pudo encontrar el paquete %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "No se pudo encontrar el paquete %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Información del paquete %1:\n"
|
IDS_CMD_PACKAGE_INFO "Información del paquete %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -271,3 +271,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Impossible de trouver le paquet %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Impossible de trouver le paquet %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information sur le paquet %1 :\n"
|
IDS_CMD_PACKAGE_INFO "Information sur le paquet %1 :\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -273,3 +273,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "החבילה %1 לא נמצאה.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "החבילה %1 לא נמצאה.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "מידע על החבילה %1:\n"
|
IDS_CMD_PACKAGE_INFO "מידע על החבילה %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Nem sikerült a(z) %1 csomagot megtalálni.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Nem sikerült a(z) %1 csomagot megtalálni.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Információ a(z) %1 csomagról:\n"
|
IDS_CMD_PACKAGE_INFO "Információ a(z) %1 csomagról:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "パッケージ %1 を探すのに失敗しました。\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "パッケージ %1 を探すのに失敗しました。\n"
|
||||||
IDS_CMD_PACKAGE_INFO "パッケージ %1 に関する情報:\n"
|
IDS_CMD_PACKAGE_INFO "パッケージ %1 に関する情報:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Nie można odnaleźć pakietu %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Nie można odnaleźć pakietu %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Informacje o pakiecie %1:\n"
|
IDS_CMD_PACKAGE_INFO "Informacje o pakiecie %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Falhou a procura do pacote %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Falhou a procura do pacote %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Informações acerca do pacote %1:\n"
|
IDS_CMD_PACKAGE_INFO "Informações acerca do pacote %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Nu se poate găsi pachetul %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Nu se poate găsi pachetul %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Informații despre pachet %1:\n"
|
IDS_CMD_PACKAGE_INFO "Informații despre pachet %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -273,3 +273,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Не удалось найти пакет %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Не удалось найти пакет %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Информация о пакете %1:\n"
|
IDS_CMD_PACKAGE_INFO "Информация о пакете %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -268,3 +268,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -270,3 +270,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "%1 paketi bulunmadı.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "%1 paketi bulunmadı.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "%1 paketi hakkındaki bilgi:\n"
|
IDS_CMD_PACKAGE_INFO "%1 paketi hakkındaki bilgi:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -271,3 +271,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "Failed to find package %1.\n"
|
||||||
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
IDS_CMD_PACKAGE_INFO "Information about package %1:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -272,3 +272,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "未能找到软件包 %1。\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "未能找到软件包 %1。\n"
|
||||||
IDS_CMD_PACKAGE_INFO "有关软件包 %1 的信息:\n"
|
IDS_CMD_PACKAGE_INFO "有关软件包 %1 的信息:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -269,3 +269,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "無法找到軟件套件 %1。\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "無法找到軟件套件 %1。\n"
|
||||||
IDS_CMD_PACKAGE_INFO "有關軟件套件 %1 的資訊:\n"
|
IDS_CMD_PACKAGE_INFO "有關軟件套件 %1 的資訊:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -269,3 +269,8 @@ BEGIN
|
||||||
IDS_CMD_PACKAGE_NOT_FOUND "無法找到軟體套件 %1。\n"
|
IDS_CMD_PACKAGE_NOT_FOUND "無法找到軟體套件 %1。\n"
|
||||||
IDS_CMD_PACKAGE_INFO "有關軟體套件 %1 的資訊:\n"
|
IDS_CMD_PACKAGE_INFO "有關軟體套件 %1 的資訊:\n"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_INSTGEN_CONFIRMUNINST "Are you sure you want to uninstall %s?"
|
||||||
|
END
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "rosui.h"
|
#include "rosui.h"
|
||||||
#include "dialogs.h"
|
#include "dialogs.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "unattended.h"
|
||||||
|
|
||||||
#ifdef USE_CERT_PINNING
|
#ifdef USE_CERT_PINNING
|
||||||
#define CERT_ISSUER_INFO_OLD "US\r\nLet's Encrypt\r\nLet's Encrypt Authority X3"
|
#define CERT_ISSUER_INFO_OLD "US\r\nLet's Encrypt\r\nLet's Encrypt Authority X3"
|
||||||
|
@ -107,19 +108,26 @@ UrlUnescapeAndMakeFileNameValid(CStringW& str)
|
||||||
|
|
||||||
struct DownloadInfo
|
struct DownloadInfo
|
||||||
{
|
{
|
||||||
DownloadInfo()
|
DownloadInfo() : DLType(DLTYPE_APPLICATION), IType(INSTALLER_UNKNOWN), SizeInBytes(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
DownloadInfo(const CAppInfo &AppInfo) : DLType(DLTYPE_APPLICATION)
|
DownloadInfo(const CAppInfo &AppInfo) : DLType(DLTYPE_APPLICATION)
|
||||||
{
|
{
|
||||||
AppInfo.GetDownloadInfo(szUrl, szSHA1, SizeInBytes);
|
AppInfo.GetDownloadInfo(szUrl, szSHA1, SizeInBytes);
|
||||||
szName = AppInfo.szDisplayName;
|
szName = AppInfo.szDisplayName;
|
||||||
|
IType = AppInfo.GetInstallerType();
|
||||||
|
if (IType == INSTALLER_GENERATE)
|
||||||
|
{
|
||||||
|
szPackageName = AppInfo.szIdentifier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadType DLType;
|
DownloadType DLType;
|
||||||
|
InstallerType IType;
|
||||||
CStringW szUrl;
|
CStringW szUrl;
|
||||||
CStringW szName;
|
CStringW szName;
|
||||||
CStringW szSHA1;
|
CStringW szSHA1;
|
||||||
|
CStringW szPackageName;
|
||||||
ULONG SizeInBytes;
|
ULONG SizeInBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -400,12 +408,12 @@ CertGetSubjectAndIssuer(HINTERNET hFile, CLocalPtr<char> &subjectInfo, CLocalPtr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline VOID
|
inline VOID
|
||||||
MessageBox_LoadString(HWND hMainWnd, INT StringID)
|
MessageBox_LoadString(HWND hOwnerWnd, INT StringID)
|
||||||
{
|
{
|
||||||
CStringW szMsgText;
|
CStringW szMsgText;
|
||||||
if (szMsgText.LoadStringW(StringID))
|
if (szMsgText.LoadStringW(StringID))
|
||||||
{
|
{
|
||||||
MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR);
|
MessageBoxW(hOwnerWnd, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1011,6 +1019,7 @@ CDownloadManager::ThreadFunc(LPVOID param)
|
||||||
// run it
|
// run it
|
||||||
if (InfoArray[iAppId].DLType == DLTYPE_APPLICATION)
|
if (InfoArray[iAppId].DLType == DLTYPE_APPLICATION)
|
||||||
{
|
{
|
||||||
|
CStringW app, params;
|
||||||
SHELLEXECUTEINFOW shExInfo = {0};
|
SHELLEXECUTEINFOW shExInfo = {0};
|
||||||
shExInfo.cbSize = sizeof(shExInfo);
|
shExInfo.cbSize = sizeof(shExInfo);
|
||||||
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
@ -1019,6 +1028,17 @@ CDownloadManager::ThreadFunc(LPVOID param)
|
||||||
shExInfo.lpParameters = L"";
|
shExInfo.lpParameters = L"";
|
||||||
shExInfo.nShow = SW_SHOW;
|
shExInfo.nShow = SW_SHOW;
|
||||||
|
|
||||||
|
if (InfoArray[iAppId].IType == INSTALLER_GENERATE)
|
||||||
|
{
|
||||||
|
params = L"/" + CStringW(CMD_KEY_GENINST) + L" \"" +
|
||||||
|
InfoArray[iAppId].szPackageName + L"\" \"" +
|
||||||
|
CStringW(shExInfo.lpFile) + L"\"";
|
||||||
|
shExInfo.lpParameters = params;
|
||||||
|
shExInfo.lpFile = app.GetBuffer(MAX_PATH);
|
||||||
|
GetModuleFileNameW(NULL, const_cast<LPWSTR>(shExInfo.lpFile), MAX_PATH);
|
||||||
|
app.ReleaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: Do we want to log installer status? */
|
/* FIXME: Do we want to log installer status? */
|
||||||
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_INSTALL, InfoArray[iAppId].szName);
|
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_INSTALL, InfoArray[iAppId].szName);
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
|
|
||||||
static HANDLE hLog = NULL;
|
static HANDLE hLog = NULL;
|
||||||
|
|
||||||
static BOOL bIsSys64ResultCached = FALSE;
|
|
||||||
static BOOL bIsSys64Result = FALSE;
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
CopyTextToClipboard(LPCWSTR lpszText)
|
CopyTextToClipboard(LPCWSTR lpszText)
|
||||||
{
|
{
|
||||||
|
@ -289,32 +286,14 @@ GetInstalledVersion(CStringW *pszVersion, const CStringW &szRegName)
|
||||||
BOOL
|
BOOL
|
||||||
IsSystem64Bit()
|
IsSystem64Bit()
|
||||||
{
|
{
|
||||||
if (bIsSys64ResultCached)
|
#ifdef _WIN64
|
||||||
{
|
return TRUE;
|
||||||
// just return cached result
|
#else
|
||||||
return bIsSys64Result;
|
static UINT cache = 0;
|
||||||
}
|
if (!cache)
|
||||||
|
cache = 1 + (IsOS(OS_WOW6432) != FALSE);
|
||||||
SYSTEM_INFO si;
|
return cache - 1;
|
||||||
typedef void(WINAPI * LPFN_PGNSI)(LPSYSTEM_INFO);
|
#endif
|
||||||
LPFN_PGNSI pGetNativeSystemInfo =
|
|
||||||
(LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo");
|
|
||||||
if (pGetNativeSystemInfo)
|
|
||||||
{
|
|
||||||
pGetNativeSystemInfo(&si);
|
|
||||||
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
|
|
||||||
si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
|
|
||||||
{
|
|
||||||
bIsSys64Result = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bIsSys64Result = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result
|
|
||||||
return bIsSys64Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INT
|
INT
|
||||||
|
@ -368,6 +347,64 @@ UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime)
|
||||||
pFileTime->dwHighDateTime = ll >> 32;
|
pFileTime->dwHighDateTime = ll >> 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam)
|
||||||
|
{
|
||||||
|
CRegKey key;
|
||||||
|
LONG err = key.Open(hKey, Path, KEY_QUERY_VALUE | wowsam);
|
||||||
|
if (err == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
WCHAR name[1];
|
||||||
|
DWORD cchname = _countof(name), cbsize = 0;
|
||||||
|
err = RegEnumValueW(key, 0, name, &cchname, NULL, NULL, NULL, &cbsize);
|
||||||
|
if (err == ERROR_NO_MORE_ITEMS)
|
||||||
|
return S_FALSE;
|
||||||
|
if (err == ERROR_MORE_DATA)
|
||||||
|
err = ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
return HRESULT_FROM_WIN32(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
LPCWSTR
|
||||||
|
GetRegString(CRegKey &Key, LPCWSTR Name, CStringW &Value)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
ULONG cb = 0, cch;
|
||||||
|
ULONG err = Key.QueryValue(Name, NULL, NULL, &cb);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
cch = cb / sizeof(WCHAR);
|
||||||
|
LPWSTR p = Value.GetBuffer(cch + 1);
|
||||||
|
p[cch] = UNICODE_NULL;
|
||||||
|
err = Key.QueryValue(Name, NULL, (BYTE*)p, &cb);
|
||||||
|
if (err == ERROR_MORE_DATA)
|
||||||
|
continue;
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
Value.ReleaseBuffer();
|
||||||
|
return Value.GetString();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ExpandEnvStrings(CStringW &Str)
|
||||||
|
{
|
||||||
|
CStringW buf;
|
||||||
|
DWORD cch = ExpandEnvironmentStringsW(Str, NULL, 0);
|
||||||
|
if (cch)
|
||||||
|
{
|
||||||
|
if (ExpandEnvironmentStringsW(Str, buf.GetBuffer(cch), cch) == cch)
|
||||||
|
{
|
||||||
|
buf.ReleaseBuffer(cch - 1);
|
||||||
|
Str = buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
|
SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
|
||||||
{
|
{
|
||||||
|
@ -376,3 +413,78 @@ SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
|
||||||
/* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
|
/* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
|
||||||
return StrStrIW(szHaystack, szNeedle) != NULL;
|
return StrStrIW(szHaystack, szNeedle) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
DeleteDirectoryTree(LPCWSTR Dir, HWND hwnd)
|
||||||
|
{
|
||||||
|
CStringW from(Dir);
|
||||||
|
UINT cch = from.GetLength();
|
||||||
|
from.Append(L"00");
|
||||||
|
LPWSTR p = from.GetBuffer();
|
||||||
|
p[cch] = p[cch + 1] = L'\0'; // Double null-terminate
|
||||||
|
UINT fof = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
|
||||||
|
SHFILEOPSTRUCT shfos = { hwnd, FO_DELETE, p, NULL, (FILEOP_FLAGS)fof };
|
||||||
|
return SHFileOperationW(&shfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT
|
||||||
|
CreateDirectoryTree(LPCWSTR Dir)
|
||||||
|
{
|
||||||
|
UINT err = SHCreateDirectory(NULL, Dir);
|
||||||
|
return err == ERROR_ALREADY_EXISTS ? 0 : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStringW
|
||||||
|
SplitFileAndDirectory(LPCWSTR FullPath, CStringW *pDir)
|
||||||
|
{
|
||||||
|
CPathW dir = FullPath;
|
||||||
|
//int win = dir.ReverseFind(L'\\'), nix = dir.ReverseFind(L'/'), sep = max(win, nix);
|
||||||
|
int sep = dir.FindFileName();
|
||||||
|
CStringW file = dir.m_strPath.Mid(sep);
|
||||||
|
if (pDir)
|
||||||
|
*pDir = sep == -1 ? L"" : dir.m_strPath.Left(sep - 1);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
GetSpecialPath(UINT csidl, CStringW &Path, HWND hwnd)
|
||||||
|
{
|
||||||
|
if (!SHGetSpecialFolderPathW(hwnd, Path.GetBuffer(MAX_PATH), csidl, TRUE))
|
||||||
|
return E_FAIL;
|
||||||
|
Path.ReleaseBuffer();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
GetKnownPath(REFKNOWNFOLDERID kfid, CStringW &Path, DWORD Flags)
|
||||||
|
{
|
||||||
|
PWSTR p;
|
||||||
|
FARPROC f = GetProcAddress(LoadLibraryW(L"SHELL32"), "SHGetKnownFolderPath");
|
||||||
|
if (!f)
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
|
||||||
|
HRESULT hr = ((HRESULT(WINAPI*)(REFKNOWNFOLDERID,UINT,HANDLE,PWSTR*))f)(kfid, Flags, NULL, &p);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
Path = p;
|
||||||
|
CoTaskMemFree(p);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
GetProgramFilesPath(CStringW &Path, BOOL PerUser, HWND hwnd)
|
||||||
|
{
|
||||||
|
if (!PerUser)
|
||||||
|
return GetSpecialPath(CSIDL_PROGRAM_FILES, Path, hwnd);
|
||||||
|
|
||||||
|
HRESULT hr = GetKnownPath(FOLDERID_UserProgramFiles, Path);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
hr = GetSpecialPath(CSIDL_LOCAL_APPDATA, Path, hwnd);
|
||||||
|
// Use the correct path on NT6 (on NT5 the path becomes a bit long)
|
||||||
|
if (SUCCEEDED(hr) && LOBYTE(GetVersion()) >= 6)
|
||||||
|
{
|
||||||
|
Path = BuildPath(Path, L"Programs"); // Should not be localized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "unattended.h"
|
#include "unattended.h"
|
||||||
|
#include "configparser.h"
|
||||||
#include <setupapi.h>
|
#include <setupapi.h>
|
||||||
#include <conutils.h>
|
#include <conutils.h>
|
||||||
|
|
||||||
|
@ -42,6 +43,22 @@ InitRappsConsole()
|
||||||
ConInitStdStreams(); // Initialize the Console Standard Streams
|
ConInitStdStreams(); // Initialize the Console Standard Streams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CAppInfo *
|
||||||
|
SearchForAppWithDisplayName(CAppDB &db, AppsCategories Type, LPCWSTR Name)
|
||||||
|
{
|
||||||
|
CAtlList<CAppInfo *> List;
|
||||||
|
db.GetApps(List, Type);
|
||||||
|
for (POSITION it = List.GetHeadPosition(); it;)
|
||||||
|
{
|
||||||
|
CAppInfo *Info = List.GetNext(it);
|
||||||
|
if (SearchPatternMatch(Info->szDisplayName, Name))
|
||||||
|
{
|
||||||
|
return Info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL
|
static BOOL
|
||||||
HandleInstallCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
|
HandleInstallCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
|
||||||
{
|
{
|
||||||
|
@ -104,6 +121,95 @@ HandleSetupCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
|
||||||
return DownloadListOfApplications(Applications, TRUE);
|
return DownloadListOfApplications(Applications, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
HandleUninstallCommand(CAppDB &db, UINT argcLeft, LPWSTR *argvLeft)
|
||||||
|
{
|
||||||
|
UINT argi = 0, silent = FALSE, byregkeyname = FALSE;
|
||||||
|
for (; argcLeft; ++argi, --argcLeft)
|
||||||
|
{
|
||||||
|
if (!StrCmpIW(argvLeft[argi], L"/S"))
|
||||||
|
++silent;
|
||||||
|
else if (!StrCmpIW(argvLeft[argi], L"/K"))
|
||||||
|
++byregkeyname;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (argcLeft != 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
CStringW buf;
|
||||||
|
LPCWSTR name = argvLeft[argi];
|
||||||
|
BOOL retval = FALSE;
|
||||||
|
CAppInfo *pInfo = NULL, *pDelete = NULL;
|
||||||
|
|
||||||
|
if (!byregkeyname)
|
||||||
|
{
|
||||||
|
for (UINT i = ENUM_INSTALLED_MIN; !pInfo && i <= ENUM_INSTALLED_MAX; ++i)
|
||||||
|
{
|
||||||
|
pInfo = SearchForAppWithDisplayName(db, AppsCategories(i), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pInfo)
|
||||||
|
{
|
||||||
|
CAvailableApplicationInfo *p = db.FindAvailableByPackageName(name);
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
CConfigParser *cp = p->GetConfigParser();
|
||||||
|
if (cp && cp->GetString(DB_REGNAME, buf) && !buf.IsEmpty())
|
||||||
|
{
|
||||||
|
name = buf.GetString();
|
||||||
|
byregkeyname = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byregkeyname)
|
||||||
|
{
|
||||||
|
// Force a specific key type if requested (<M|U>[32|64]<\\KeyName>)
|
||||||
|
if (name[0])
|
||||||
|
{
|
||||||
|
REGSAM wow = 0;
|
||||||
|
UINT i = 1;
|
||||||
|
if (name[i] == '3' && name[i + 1])
|
||||||
|
wow = KEY_WOW64_32KEY, i += 2;
|
||||||
|
else if (name[i] == '6' && name[i + 1])
|
||||||
|
wow = KEY_WOW64_64KEY, i += 2;
|
||||||
|
|
||||||
|
if (name[i++] == '\\')
|
||||||
|
{
|
||||||
|
pInfo = CAppDB::CreateInstalledAppInstance(name + i, name[0] == 'U', wow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pInfo)
|
||||||
|
{
|
||||||
|
pInfo = CAppDB::CreateInstalledAppByRegistryKey(name);
|
||||||
|
}
|
||||||
|
pDelete = pInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pInfo)
|
||||||
|
{
|
||||||
|
retval = pInfo->UninstallApplication(silent ? UCF_SILENT : UCF_NONE);
|
||||||
|
}
|
||||||
|
delete pDelete;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
HandleGenerateInstallerCommand(CAppDB &db, UINT argcLeft, LPWSTR *argvLeft)
|
||||||
|
{
|
||||||
|
if (argcLeft != 2)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
CAvailableApplicationInfo *pAI = db.FindAvailableByPackageName(argvLeft[0]);
|
||||||
|
if (!pAI)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return ExtractAndRunGeneratedInstaller(*pAI, argvLeft[1]);
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL
|
static BOOL
|
||||||
HandleFindCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
|
HandleFindCommand(CAppDB *db, LPWSTR szCommand, int argcLeft, LPWSTR *argvLeft)
|
||||||
{
|
{
|
||||||
|
@ -276,6 +382,14 @@ ParseCmdAndExecute(LPWSTR lpCmdLine, BOOL bIsFirstLaunch, int nCmdShow)
|
||||||
{
|
{
|
||||||
return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2);
|
return HandleSetupCommand(&db, argv[1], argc - 2, argv + 2);
|
||||||
}
|
}
|
||||||
|
else if (MatchCmdOption(argv[1], CMD_KEY_UNINSTALL))
|
||||||
|
{
|
||||||
|
return HandleUninstallCommand(db, argc - 2, argv + 2);
|
||||||
|
}
|
||||||
|
else if (MatchCmdOption(argv[1], CMD_KEY_GENINST))
|
||||||
|
{
|
||||||
|
return HandleGenerateInstallerCommand(db, argc - 2, argv + 2);
|
||||||
|
}
|
||||||
|
|
||||||
InitRappsConsole();
|
InitRappsConsole();
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nSh
|
||||||
|
|
||||||
InitLogs();
|
InitLogs();
|
||||||
InitCommonControls();
|
InitCommonControls();
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // Give UI higher priority than background threads
|
||||||
|
|
||||||
// parse cmd-line and perform the corresponding operation
|
// parse cmd-line and perform the corresponding operation
|
||||||
BOOL bSuccess = ParseCmdAndExecute(GetCommandLineW(), bIsFirstLaunch, SW_SHOWNORMAL);
|
BOOL bSuccess = ParseCmdAndExecute(GetCommandLineW(), bIsFirstLaunch, SW_SHOWNORMAL);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue