Something went wrong. Try again.
Reactos
Something went wrong. Try again.
30 kB · 890 lines
C++
at listview
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891/* * 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_NONE L"!" // Skip/Ignore#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_LNKARGS L"LnkParameters"#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',};
BOOL IsZipFile(PCWSTR Path){ zlib_filefunc64_def zff; fill_win32_filefunc64W(&zff); unzFile hzf = unzOpen2_64(Path, &zff); if (!hzf) return FALSE; unzClose(hzf); return TRUE;}
static intExtractFilesFromZip(LPCWSTR Archive, const CStringW &OutputDir, EXTRACTCALLBACK Callback, void *Context){ 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, Context)) 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 UINTExtractZip(LPCWSTR Archive, const CStringW &OutputDir, EXTRACTCALLBACK Callback, void *Context){ int zerr = ExtractFilesFromZip(Archive, OutputDir, Callback, Context); return zerr == UNZ_ERRNO ? GetLastError() : zerr ? ERROR_INTERNAL_ERROR : 0;}
static UINTExtractCab(LPCWSTR Archive, const CStringW &OutputDir, EXTRACTCALLBACK Callback, void *Context){ if (ExtractFilesFromCab(Archive, OutputDir, Callback, Context)) return ERROR_SUCCESS; UINT err = GetLastError(); return err ? err : ERROR_INTERNAL_ERROR;}
static UINTExtractArchive(LPCWSTR Archive, const CStringW &OutputDir, EXTRACTCALLBACK Callback, void *Context){ BOOL isCab = LOBYTE(ClassifyFile(Archive)) == 'C'; return isCab ? ExtractCab(Archive, OutputDir, Callback, Context) : ExtractZip(Archive, OutputDir, Callback, Context);}
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 inline UINTErrorBox(UINT Error = GetLastError()){ return g_pInfo->Error = ErrorBox(g_pInfo->GetGuiOwner(), Error);}
static LPCWSTRGetCommonString(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 LPCWSTRGetGenerateString(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 voidWriteArpEntry(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 BOOLAddEntry(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 HRESULTGetCustomIconPath(InstallInfo &Info, CStringW &Path){ if (*GetGenerateString(DB_GENINST_ICON, Path)) { if (Path.Compare(DB_NONE) == 0) return HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE); 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 BOOLGetLocalizedSMFolderName(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 CStringWCreateMainShortcut(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 L""; // 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 L""; }
CComPtr<IShellLinkW> link; hr = link.CoCreateInstance(CLSID_ShellLink, IID_IShellLinkW); if (SUCCEEDED(hr)) { if (SUCCEEDED(hr = link->SetPath(Target))) { SplitFileAndDirectory(Target, &tmp); link->SetWorkingDirectory(tmp);
if (*GetGenerateString(DB_GENINST_LNKARGS, tmp)) link->SetArguments(tmp);
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 ? L"" : path;}
static BOOLInstallFiles(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); }
if (success) { success = InstallFiles(from, filespec, to); } AddEntry(UNOP_EMPTYDIR, uninstpath); } } 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 voidAddUninstallOperationsFromDB(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 CALLBACKExtractCallback(const EXTRACTCALLBACKINFO &, void *Context){ InstallInfo &Info = *(InstallInfo *)Context; Info.Count += 1; return TRUE;}
static DWORD CALLBACKExtractAndInstallThread(LPVOID Parameter){ const BOOL PerUserModeDefault = TRUE; InstallInfo &Info = *static_cast<InstallInfo *>(g_pInfo); LPCWSTR AppName = Info.AppName, Archive = Info.ArchivePath, None = DB_NONE; 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) Info.Error = ExtractArchive(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);
hr = GetCustomIconPath(Info, tmp); if (hr != HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE)) WriteArpEntry(L"DisplayIcon", hr == S_OK ? tmp : Info.MainApp);
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) { tmp = CreateMainShortcut(Info.MainApp); if (!tmp.IsEmpty() && !Info.Silent) { CStringW message, format; format.LoadString(IDS_INSTGEN_CONFIRMINSTRUNAPP); message.Format(format, const_cast<PCWSTR>(AppName)); if (MessageBoxW(Info.GetGuiOwner(), message, AppName, MB_YESNO | MB_ICONQUESTION) == IDYES) { SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_NOASYNC, Info.GetGuiOwner() }; sei.lpFile = tmp; sei.nShow = SW_SHOW; ShellExecuteExW(&sei); } } } }
DeleteDirectoryTree(tempdir.GetString()); RemoveDirectory(installdir.GetString()); // This is harmless even if we installed something return 0;}
static DWORD CALLBACKWorkerThread(LPVOID Parameter){ ((LPTHREAD_START_ROUTINE)Parameter)(NULL); return SendMessage(g_pInfo->hDlg, IM_END, 0, 0);}
static INT_PTR CALLBACKUIDlgProc(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); } return TRUE; } case WM_CLOSE: return TRUE; case WM_DESTROY: PostMessage(NULL, WM_QUIT, 0, 0); break; } return FALSE;}
static BOOLCreateUI(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;}
BOOLExtractAndRunGeneratedInstaller(const CAvailableApplicationInfo &AppInfo, LPCWSTR Archive, bool Silent){ InstallInfo Info(AppInfo.szDisplayName, *AppInfo.GetConfigParser(), Archive); Info.Silent = Silent; 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 CALLBACKUninstallThread(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) key.DeleteSubKey(tmp); // DelRegEmpty ignores errors } 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;}
BOOLUninstallGenerated(CInstalledApplicationInfo &AppInfo, UninstallCommandFlags Flags){ UninstallInfo Info(AppInfo, Flags & UCF_SILENT); g_pInfo = &Info; return CreateUI(Info.Silent, UninstallThread) ? !Info.Error : FALSE;}
HRESULTExtractArchiveForExecution(PCWSTR pszArchive, const CStringW &PackageName, CStringW &TempDir, CStringW &App){ WCHAR TempDirBuf[MAX_PATH], UniqueDir[MAX_PATH]; CAppDB db(CAppDB::GetDefaultPath()); db.UpdateAvailable(); CAvailableApplicationInfo *pAppInfo = db.FindAvailableByPackageName(PackageName); if (!pAppInfo) return HResultFromWin32(ERROR_NOT_FOUND); CConfigParser *pCfg = pAppInfo->GetConfigParser();
if (!GetTempPathW(_countof(TempDirBuf), TempDirBuf)) return E_FAIL; wsprintfW(UniqueDir, L"~%s-%u", RAPPS_NAME, GetCurrentProcessId()); TempDir = BuildPath(TempDirBuf, UniqueDir); HRESULT hr = HResultFromWin32(CreateDirectoryTree(TempDir)); if (FAILED(hr)) return hr;
hr = HResultFromWin32(ExtractArchive(pszArchive, TempDir, NULL, NULL)); if (SUCCEEDED(hr)) { CStringW Exe; if (pCfg->GetSectionString(DB_EXEINZIPSECTION, DB_EXEINZIP_EXE, Exe) <= 0) { WIN32_FIND_DATAW wfd; HANDLE hFind = FindFirstFileW(Exe = BuildPath(TempDir, L"*.exe"), &wfd); if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); Exe = wfd.cFileName; } } App = BuildPath(TempDir, Exe); if (GetFileAttributesW(App) & FILE_ATTRIBUTE_DIRECTORY) hr = HResultFromWin32(ERROR_FILE_NOT_FOUND); }
if (FAILED(hr) && !TempDir.IsEmpty()) DeleteDirectoryTree(TempDir, hMainWnd); return hr;}