Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

允许更改用于启动的可执行文件 #641

Merged
merged 9 commits into from
Jun 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Magpie.App/AppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ static void WriteProfile(rapidjson::PrettyWriter<rapidjson::StringBuffer>& write
writer.String(StrUtils::UTF16ToUTF8(profile.pathRule).c_str());
writer.Key("classNameRule");
writer.String(StrUtils::UTF16ToUTF8(profile.classNameRule).c_str());
writer.Key("launcherPath");
writer.String(StrUtils::UTF16ToUTF8(profile.launcherPath).c_str());
writer.Key("autoScale");
writer.Bool(profile.isAutoScale);
writer.Key("launchParameters");
Expand Down Expand Up @@ -751,6 +753,7 @@ bool AppSettings::_LoadProfile(
return false;
}

JsonHelper::ReadString(profileObj, "launcherPath", profile.launcherPath);
JsonHelper::ReadBool(profileObj, "autoScale", profile.isAutoScale);
JsonHelper::ReadString(profileObj, "launchParameters", profile.launchParameters);
}
Expand Down
38 changes: 38 additions & 0 deletions src/Magpie.App/FileDialogHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "pch.h"
#include "FileDialogHelper.h"
#include "Logger.h"
#include "App.h"

namespace winrt::Magpie::App {

// 出错返回空,取消返回空字符串
std::optional<std::wstring> FileDialogHelper::OpenFileDialog(IFileDialog* fileDialog, FILEOPENDIALOGOPTIONS options) noexcept {
FILEOPENDIALOGOPTIONS options1{};
fileDialog->GetOptions(&options1);
fileDialog->SetOptions(options1 | options | FOS_FORCEFILESYSTEM);

if (fileDialog->Show((HWND)Application::Current().as<App>().HwndMain()) != S_OK) {
// 被用户取消
return std::wstring();
}

com_ptr<IShellItem> file;
HRESULT hr = fileDialog->GetResult(file.put());
if (FAILED(hr)) {
Logger::Get().ComError("IFileSaveDialog::GetResult 失败", hr);
return std::nullopt;
}

wchar_t* fileName = nullptr;
hr = file->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &fileName);
if (FAILED(hr)) {
Logger::Get().ComError("IShellItem::GetDisplayName 失败", hr);
return std::nullopt;
}

std::wstring result(fileName);
CoTaskMemFree(fileName);
return std::move(result);
}

}
12 changes: 12 additions & 0 deletions src/Magpie.App/FileDialogHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

namespace winrt::Magpie::App {

struct FileDialogHelper {
static std::optional<std::wstring> OpenFileDialog(
IFileDialog* fileDialog,
FILEOPENDIALOGOPTIONS options = 0
) noexcept;
};

}
2 changes: 2 additions & 0 deletions src/Magpie.App/Magpie.App.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="EffectsService.h" />
<ClInclude Include="FileDialogHelper.h" />
<ClInclude Include="HomeViewModel.h">
<DependentUpon>HomeViewModel.idl</DependentUpon>
<SubType>Code</SubType>
Expand Down Expand Up @@ -293,6 +294,7 @@
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="EffectsService.cpp" />
<ClCompile Include="FileDialogHelper.cpp" />
<ClCompile Include="HomeViewModel.cpp">
<DependentUpon>HomeViewModel.idl</DependentUpon>
<SubType>Code</SubType>
Expand Down
6 changes: 6 additions & 0 deletions src/Magpie.App/Magpie.App.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<ClCompile Include="LocalizationService.cpp">
<Filter>Services</Filter>
</ClCompile>
<ClCompile Include="FileDialogHelper.cpp">
<Filter>Helpers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand Down Expand Up @@ -114,6 +117,9 @@
<ClInclude Include="LocalizationService.h">
<Filter>Services</Filter>
</ClInclude>
<ClInclude Include="FileDialogHelper.h">
<Filter>Helpers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Pages">
Expand Down
4 changes: 4 additions & 0 deletions src/Magpie.App/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ struct Profile {
std::wstring pathRule;
std::wstring classNameRule;

// 如果在同一个驱动器上则存储相对路径,否则存储绝对路径
// 若为空使用 pathRule
std::wstring launcherPath;

CursorScaling cursorScaling = CursorScaling::NoScaling;
float customCursorScaling = 1.0;

Expand Down
10 changes: 9 additions & 1 deletion src/Magpie.App/ProfilePage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
<FontIcon Glyph="&#xE8DA;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="Profile_MoreOptions_ChangeExecutableForLaunching"
Click="{x:Bind ViewModel.ChangeExeForLaunching}"
IsEnabled="{x:Bind ViewModel.IsProgramExist, Mode=OneTime}"
Visibility="{x:Bind ViewModel.IsNotPackaged, Mode=OneTime}">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="&#xE8E5;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutItem x:Uid="Profile_MoreOptions_Rename"
Click="RenameMenuItem_Click">
Expand Down Expand Up @@ -478,7 +486,7 @@
Margin="0,0,0,2"
Visibility="{x:Bind ViewModel.IsNotDefaultProfile, Mode=OneTime}">
<local:SettingsCard.Icon>
<FontIcon Glyph="&#xF259;" />
<FontIcon Glyph="&#xE756;" />
</local:SettingsCard.Icon>
<local:SettingsCard.ActionContent>
<Grid MinHeight="36">
Expand Down
130 changes: 128 additions & 2 deletions src/Magpie.App/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "ScalingMode.h"
#include <dxgi.h>
#include "MagService.h"
#include "FileDialogHelper.h"

using namespace winrt;
using namespace Windows::Graphics::Display;
Expand Down Expand Up @@ -128,6 +129,10 @@ bool ProfileViewModel::IsNotDefaultProfile() const noexcept {
return !_data->name.empty();
}

bool ProfileViewModel::IsNotPackaged() const noexcept {
return !_data->isPackaged;
}

fire_and_forget ProfileViewModel::OpenProgramLocation() const noexcept {
if (!_isProgramExist) {
co_return;
Expand All @@ -153,6 +158,95 @@ fire_and_forget ProfileViewModel::OpenProgramLocation() const noexcept {
Win32Utils::OpenFolderAndSelectFile(programLocation.c_str());
}

std::wstring GetStartFolderForSettingLauncher(const Profile& profile) noexcept {
if (profile.launcherPath.empty()) {
// 没有指定启动器
size_t delimPos = profile.pathRule.find_last_of(L'\\');
return delimPos == std::wstring::npos ? std::wstring() : profile.pathRule.substr(0, delimPos);
}

if (!PathIsRelative(profile.launcherPath.c_str())) {
// 位于不同的驱动器上
size_t delimPos = profile.launcherPath.find_last_of(L'\\');
return delimPos == std::wstring::npos ? std::wstring() : profile.launcherPath.substr(0, delimPos);
}

size_t delimPos = profile.pathRule.find_last_of(L'\\');
if (delimPos == std::wstring::npos) {
return {};
}

// 相对路径转绝对路径
std::wstring combinedPath(MAX_PATH, 0);
if (!PathCombine(
combinedPath.data(),
profile.pathRule.substr(0, delimPos).c_str(),
profile.launcherPath.c_str()
)) {
return {};
}

combinedPath.resize(StrUtils::StrLen(combinedPath.c_str()));
delimPos = combinedPath.find_last_of(L'\\');
if (delimPos == std::wstring::npos) {
return {};
}

combinedPath.resize(delimPos);
return combinedPath;
}

void ProfileViewModel::ChangeExeForLaunching() const noexcept {
if (!_isProgramExist || _data->isPackaged) {
return;
}

com_ptr<IFileOpenDialog> fileDialog = try_create_instance<IFileOpenDialog>(CLSID_FileOpenDialog);
if (!fileDialog) {
Logger::Get().Error("创建 FileSaveDialog 失败");
return;
}

static std::wstring titleStr(ResourceLoader::GetForCurrentView().GetString(L"SelectLauncherDialog_Title"));
fileDialog->SetTitle(titleStr.c_str());

static std::wstring exeFileStr(ResourceLoader::GetForCurrentView().GetString(L"FileDialog_ExeFile"));
const COMDLG_FILTERSPEC fileType{ exeFileStr.c_str(), L"*.exe"};
fileDialog->SetFileTypes(1, &fileType);
fileDialog->SetDefaultExtension(L"exe");

std::wstring startFolder = GetStartFolderForSettingLauncher(*_data);
if (!startFolder.empty() && Win32Utils::DirExists(startFolder.c_str())) {
com_ptr<IShellItem> shellItem;
SHCreateItemFromParsingName(startFolder.c_str(), nullptr, IID_PPV_ARGS(&shellItem));
fileDialog->SetFolder(shellItem.get());
}

std::optional<std::wstring> exePath = FileDialogHelper::OpenFileDialog(fileDialog.get(), FOS_STRICTFILETYPES);
if (!exePath || exePath->empty() || *exePath == _data->pathRule) {
return;
}

if (PathIsSameRoot(exePath->c_str(), _data->pathRule.c_str())) {
// 绝对路径转相对路径
_data->launcherPath.clear();
_data->launcherPath.resize(MAX_PATH, 0);
PathRelativePathTo(
_data->launcherPath.data(),
_data->pathRule.c_str(),
FILE_ATTRIBUTE_NORMAL,
exePath->c_str(),
FILE_ATTRIBUTE_NORMAL
);
_data->launcherPath.resize(StrUtils::StrLen(_data->launcherPath.c_str()));
} else {
// 位于不同的驱动器上
_data->launcherPath = std::move(*exePath);
}

AppSettings::Get().SaveAsync();
}

hstring ProfileViewModel::Name() const noexcept {
if (_data->name.empty()) {
return ResourceLoader::GetForCurrentView().GetString(L"Main_Defaults/Content");
Expand All @@ -161,7 +255,7 @@ hstring ProfileViewModel::Name() const noexcept {
}
}

static void LaunchPackagedApp(const Profile& profile) {
static void LaunchPackagedApp(const Profile& profile) noexcept {
// 关于启动打包应用的讨论:
// https://github.com/microsoft/WindowsAppSDK/issues/2856#issuecomment-1224409948
// 使用 CLSCTX_LOCAL_SERVER 以在独立的进程中启动应用
Expand All @@ -187,6 +281,38 @@ static void LaunchPackagedApp(const Profile& profile) {
}
}

static void LaunchWin32App(const Profile& profile) noexcept {
if (profile.launcherPath.empty()) {
Win32Utils::ShellOpen(profile.pathRule.c_str(), profile.launchParameters.c_str());
return;
}

if (!PathIsRelative(profile.launcherPath.c_str())) {
// 位于不同的驱动器上
if (Win32Utils::FileExists(profile.launcherPath.c_str())) {
Win32Utils::ShellOpen(profile.launcherPath.c_str(), profile.launchParameters.c_str());
} else {
Win32Utils::ShellOpen(profile.pathRule.c_str(), profile.launchParameters.c_str());
}
return;
}

size_t delimPos = profile.pathRule.find_last_of(L'\\');
if (delimPos == std::wstring::npos) {
return;
}

// 相对路径转绝对路径
wchar_t combinedPath[MAX_PATH];
if (!PathCombine(combinedPath, profile.pathRule.substr(0, delimPos).c_str(), profile.launcherPath.c_str())
|| !Win32Utils::FileExists(combinedPath)) {
Win32Utils::ShellOpen(profile.pathRule.c_str(), profile.launchParameters.c_str());
return;
}

Win32Utils::ShellOpen(combinedPath, profile.launchParameters.c_str());
}

void ProfileViewModel::Launch() const noexcept {
if (!_isProgramExist) {
return;
Expand All @@ -195,7 +321,7 @@ void ProfileViewModel::Launch() const noexcept {
if (_data->isPackaged) {
LaunchPackagedApp(*_data);
} else {
Win32Utils::ShellOpen(_data->pathRule.c_str(), _data->launchParameters.c_str());
LaunchWin32App(*_data);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/Magpie.App/ProfileViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ struct ProfileViewModel : ProfileViewModelT<ProfileViewModel> {
return _isProgramExist;
}

bool IsNotPackaged() const noexcept;

fire_and_forget OpenProgramLocation() const noexcept;

void ChangeExeForLaunching() const noexcept;

hstring Name() const noexcept;

void Launch() const noexcept;
Expand Down
3 changes: 3 additions & 0 deletions src/Magpie.App/ProfileViewModel.idl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ namespace Magpie.App {
void Launch();

Boolean IsProgramExist { get; };
Boolean IsNotPackaged { get; };

void OpenProgramLocation();
void ChangeExeForLaunching();

String RenameText;
Boolean IsRenameConfirmButtonEnabled { get; };
Expand Down
14 changes: 13 additions & 1 deletion src/Magpie.App/Resources.language-en-US.resw
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,18 @@
<value>Disable font cache</value>
</data>
<data name="Settings_Advanced_AllowScalingMaximized.Title" xml:space="preserve">
<value>Allow scaling maximized or full-screen windows</value>
<value>Allow scaling maximized or fullscreen windows</value>
</data>
<data name="FileDialog_JsonFile" xml:space="preserve">
<value>JSON file</value>
</data>
<data name="Profile_MoreOptions_ChangeExecutableForLaunching.Text" xml:space="preserve">
<value>Change the executable file for launching</value>
</data>
<data name="FileDialog_ExeFile" xml:space="preserve">
<value>Executable file</value>
</data>
<data name="SelectLauncherDialog_Title" xml:space="preserve">
<value>Choose the executable file to launch the program</value>
</data>
</root>
12 changes: 12 additions & 0 deletions src/Magpie.App/Resources.language-zh-Hans.resw
Original file line number Diff line number Diff line change
Expand Up @@ -826,4 +826,16 @@
<data name="Settings_Advanced_AllowScalingMaximized.Title" xml:space="preserve">
<value>允许缩放最大化或全屏的窗口</value>
</data>
<data name="FileDialog_JsonFile" xml:space="preserve">
<value>JSON 文件</value>
</data>
<data name="Profile_MoreOptions_ChangeExecutableForLaunching.Text" xml:space="preserve">
<value>更改用于启动程序的可执行文件</value>
</data>
<data name="FileDialog_ExeFile" xml:space="preserve">
<value>可执行文件</value>
</data>
<data name="SelectLauncherDialog_Title" xml:space="preserve">
<value>选择用于启动程序的可执行文件</value>
</data>
</root>
Loading