diff --git a/scripts/installer/Program.cpp b/scripts/installer/Program.cpp index ef44f81..999c815 100644 --- a/scripts/installer/Program.cpp +++ b/scripts/installer/Program.cpp @@ -248,6 +248,70 @@ void validateApplication(const fs::path& directory) { if (!fs::exists(asar) || fs::file_size(asar) < 1024 * 1024) fail(L"resources\\app.asar 缺失或不完整。"); } +bool chooseInstallDirectory(const fs::path& currentTarget, fs::path& selectedTarget) { + HRESULT initialized = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + IFileOpenDialog* dialog = nullptr; + HRESULT result = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, + reinterpret_cast(&dialog)); + if (FAILED(result) || !dialog) { + if (SUCCEEDED(initialized)) CoUninitialize(); + fail(L"无法打开安装目录选择窗口。"); + } + + FILEOPENDIALOGOPTIONS options = 0; + dialog->GetOptions(&options); + dialog->SetOptions(options | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_DONTADDTORECENT); + dialog->SetTitle(L"选择 RPA Studio 的安装位置"); + dialog->SetOkButtonLabel(L"选择此文件夹"); + dialog->SetFileName(L"RPA Studio"); + + fs::path initial = currentTarget.parent_path(); + while (!initial.empty() && !fs::exists(initial)) initial = initial.parent_path(); + if (!initial.empty()) { + IShellItem* initialItem = nullptr; + if (SUCCEEDED(SHCreateItemFromParsingName(initial.c_str(), nullptr, IID_IShellItem, + reinterpret_cast(&initialItem))) && initialItem) { + dialog->SetFolder(initialItem); + initialItem->Release(); + } + } + + result = dialog->Show(nullptr); + if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { + dialog->Release(); + if (SUCCEEDED(initialized)) CoUninitialize(); + return false; + } + if (FAILED(result)) { + dialog->Release(); + if (SUCCEEDED(initialized)) CoUninitialize(); + fail(L"安装目录选择失败。"); + } + + IShellItem* selectedItem = nullptr; + result = dialog->GetResult(&selectedItem); + dialog->Release(); + if (FAILED(result) || !selectedItem) { + if (SUCCEEDED(initialized)) CoUninitialize(); + fail(L"无法读取所选安装目录。"); + } + + PWSTR selectedPath = nullptr; + result = selectedItem->GetDisplayName(SIGDN_FILESYSPATH, &selectedPath); + selectedItem->Release(); + if (FAILED(result) || !selectedPath) { + if (SUCCEEDED(initialized)) CoUninitialize(); + fail(L"所选位置不是有效的本地文件夹。"); + } + + fs::path parent(selectedPath); + CoTaskMemFree(selectedPath); + if (SUCCEEDED(initialized)) CoUninitialize(); + selectedTarget = lower(parent.filename().wstring()) == L"rpa studio" ? parent : parent / L"RPA Studio"; + selectedTarget = fs::absolute(selectedTarget).lexically_normal(); + return true; +} + void createShortcut(const fs::path& targetExecutable, const fs::path& workingDirectory) { HRESULT initialized = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); IShellLinkW* shellLink = nullptr; @@ -350,6 +414,9 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { silent = options.silent; logLine(L"installer started"); if (!options.silent) { + fs::path selectedTarget; + if (!chooseInstallDirectory(options.target, selectedTarget)) return 0; + options.target = selectedTarget; std::wstring prompt = L"将 RPA Studio __PRODUCT_VERSION__ 安装到当前 Windows 用户?\r\n\r\n安装位置:" + options.target.wstring(); if (MessageBoxW(nullptr, prompt.c_str(), L"RPA Studio 安装", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND) != IDYES)