Skip to content

Commit 24a7c17

Browse files
gave92gave92yaira2
authored
Code Quality: Use protocol launch for open and save file dialogs (#15280)
Co-authored-by: gave92 <gave@LAPTOP> Co-authored-by: Yair <[email protected]>
1 parent 1fa9d4e commit 24a7c17

File tree

3 files changed

+124
-26
lines changed

3 files changed

+124
-26
lines changed

src/Files.App.OpenDialog/FilesOpenDialog.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,43 @@ IShellItem* CloneShellItem(IShellItem* psi)
4848
return item;
4949
}
5050

51+
std::string wstring_to_utf8_hex(const std::wstring& input)
52+
{
53+
std::string output;
54+
55+
int cbNeeded = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, NULL, 0, NULL, NULL);
56+
if (cbNeeded > 0)
57+
{
58+
char* utf8 = new char[cbNeeded];
59+
if (WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, utf8, cbNeeded, NULL, NULL) != 0)
60+
{
61+
for (char* p = utf8; *p; p++)
62+
{
63+
char onehex[5];
64+
sprintf_s(onehex, sizeof(onehex), "%%%02.2X", (unsigned char)*p);
65+
output.append(onehex);
66+
}
67+
}
68+
69+
delete[] utf8;
70+
}
71+
72+
return output;
73+
}
74+
75+
std::wstring str2wstr(const std::string& str)
76+
{
77+
int cbNeeded = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
78+
if (cbNeeded > 0)
79+
{
80+
std::wstring wstrTo(cbNeeded, 0);
81+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], cbNeeded);
82+
return wstrTo;
83+
}
84+
85+
return L"";
86+
}
87+
5188
template <typename T>
5289
CComPtr<T> AsInterface(CComPtr<IFileOpenDialog> dialog)
5390
{
@@ -118,19 +155,27 @@ STDAPICALL CFilesOpenDialog::Show(HWND hwndOwner)
118155
SHELLEXECUTEINFO ShExecInfo = { 0 };
119156
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
120157
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
121-
ShExecInfo.lpFile = L"files.exe";
158+
122159
PWSTR pszPath = NULL;
160+
WCHAR szBuf[MAX_PATH];
161+
TCHAR args[1024] = { 0 };
162+
ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1);
123163

124164
HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG"));
125165

126166
if (_initFolder && SUCCEEDED(_initFolder->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszPath)))
127167
{
128-
TCHAR args[1024];
129-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\"", pszPath, _outputPath.c_str());
168+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\"", szBuf, pszPath, _outputPath.c_str());
130169
wcout << L"Invoking: " << args << endl;
131-
ShExecInfo.lpParameters = args;
132170
CoTaskMemFree(pszPath);
133171
}
172+
else
173+
{
174+
swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str());
175+
}
176+
177+
std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args));
178+
ShExecInfo.lpFile = uriWithArgs.c_str();
134179
ShExecInfo.nShow = SW_SHOW;
135180
ShellExecuteEx(&ShExecInfo);
136181

src/Files.App.SaveDialog/FilesSaveDialog.cpp

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,43 @@ IShellItem* CloneShellItem(IShellItem* psi)
4646
return item;
4747
}
4848

49+
std::string wstring_to_utf8_hex(const std::wstring& input)
50+
{
51+
std::string output;
52+
53+
int cbNeeded = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, NULL, 0, NULL, NULL);
54+
if (cbNeeded > 0)
55+
{
56+
char* utf8 = new char[cbNeeded];
57+
if (WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, utf8, cbNeeded, NULL, NULL) != 0)
58+
{
59+
for (char* p = utf8; *p; p++)
60+
{
61+
char onehex[5];
62+
sprintf_s(onehex, sizeof(onehex), "%%%02.2X", (unsigned char)*p);
63+
output.append(onehex);
64+
}
65+
}
66+
67+
delete[] utf8;
68+
}
69+
70+
return output;
71+
}
72+
73+
std::wstring str2wstr(const std::string& str)
74+
{
75+
int cbNeeded = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
76+
if (cbNeeded > 0)
77+
{
78+
std::wstring wstrTo(cbNeeded, 0);
79+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], cbNeeded);
80+
return wstrTo;
81+
}
82+
83+
return L"";
84+
}
85+
4986
template <typename T>
5087
CComPtr<T> AsInterface(CComPtr<IFileSaveDialog> dialog)
5188
{
@@ -394,26 +431,34 @@ HRESULT __stdcall CFilesSaveDialog::Show(HWND hwndOwner)
394431
SHELLEXECUTEINFO ShExecInfo = { 0 };
395432
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
396433
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
397-
ShExecInfo.lpFile = L"files.exe";
434+
398435
PWSTR pszPath = NULL;
436+
WCHAR szBuf[MAX_PATH];
437+
TCHAR args[1024] = { 0 };
438+
ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1);
399439

400440
HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG"));
401441

402442
if (_initFolder && SUCCEEDED(_initFolder->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszPath)))
403443
{
404-
TCHAR args[1024];
405444
if (!_initName.empty())
406445
{
407-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\" -select \"%s\"", pszPath, _outputPath.c_str(), _initName.c_str());
446+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\" -select \"%s\"", szBuf, pszPath, _outputPath.c_str(), _initName.c_str());
408447
}
409448
else
410449
{
411-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\"", pszPath, _outputPath.c_str());
450+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\"", szBuf, pszPath, _outputPath.c_str());
412451
}
413452
wcout << L"Invoking: " << args << endl;
414-
ShExecInfo.lpParameters = args;
415453
CoTaskMemFree(pszPath);
416454
}
455+
else
456+
{
457+
swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str());
458+
}
459+
460+
std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args));
461+
ShExecInfo.lpFile = uriWithArgs.c_str();
417462
ShExecInfo.nShow = SW_SHOW;
418463
ShellExecuteEx(&ShExecInfo);
419464

src/Files.App/Program.cs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,11 @@ static bool ProcessPathPredicate(Process p)
101101

102102
var OpenTabInExistingInstance = ApplicationData.Current.LocalSettings.Values.Get("OpenTabInExistingInstance", true);
103103
var activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
104+
var commandLineArgs = GetCommandLineArgs(activatedArgs);
104105

105-
// WINUI3: When launching from commandline the argument is not ICommandLineActivatedEventArgs (#10370)
106-
var isCommadLineLaunch = activatedArgs.Data is ILaunchActivatedEventArgs args &&
107-
args.Arguments is not null &&
108-
(CommandLineParser.SplitArguments(args.Arguments, true)[0].EndsWith($"files.exe", StringComparison.OrdinalIgnoreCase)
109-
|| CommandLineParser.SplitArguments(args.Arguments, true)[0].EndsWith($"files", StringComparison.OrdinalIgnoreCase));
110-
111-
if (activatedArgs.Data is ICommandLineActivatedEventArgs || isCommadLineLaunch)
106+
if (commandLineArgs is not null)
112107
{
113-
var cmdLineArgs = activatedArgs.Data as ICommandLineActivatedEventArgs;
114-
var cmdLineLaunchArgs = activatedArgs.Data as ILaunchActivatedEventArgs;
115-
var parsedCommands = CommandLineParser.ParseUntrustedCommands(cmdLineArgs?.Operation.Arguments ?? cmdLineLaunchArgs!.Arguments);
108+
var parsedCommands = CommandLineParser.ParseUntrustedCommands(commandLineArgs);
116109

117110
if (parsedCommands is not null)
118111
{
@@ -124,11 +117,8 @@ args.Arguments is not null &&
124117
if (!Constants.UserEnvironmentPaths.ShellPlaces.ContainsKey(command.Payload.ToUpperInvariant()))
125118
{
126119
OpenShellCommandInExplorer(command.Payload, Environment.ProcessId);
127-
128-
// Exit
129120
return;
130121
}
131-
132122
break;
133123

134124
default:
@@ -154,7 +144,6 @@ args.Arguments is not null &&
154144
else if (activatedArgs.Data is ILaunchActivatedEventArgs tileArgs)
155145
{
156146
if (tileArgs.Arguments is not null &&
157-
!tileArgs.Arguments.Contains($"files.exe", StringComparison.OrdinalIgnoreCase) &&
158147
FileExtensionHelpers.IsExecutableFile(tileArgs.Arguments))
159148
{
160149
if (File.Exists(tileArgs.Arguments))
@@ -165,7 +154,7 @@ args.Arguments is not null &&
165154
}
166155
}
167156

168-
if (OpenTabInExistingInstance && !isCommadLineLaunch)
157+
if (OpenTabInExistingInstance && commandLineArgs is null)
169158
{
170159
if (activatedArgs.Data is ILaunchActivatedEventArgs launchArgs)
171160
{
@@ -180,8 +169,7 @@ args.Arguments is not null &&
180169
else if (activatedArgs.Data is IProtocolActivatedEventArgs protocolArgs)
181170
{
182171
var parsedArgs = protocolArgs.Uri.Query.TrimStart('?').Split('=');
183-
if ((parsedArgs.Length == 2 && parsedArgs[0] == "cmd") ||
184-
parsedArgs.Length == 1) // Treat Win+E & Open file location as command line launch
172+
if (parsedArgs.Length == 1)
185173
{
186174
var activePid = ApplicationData.Current.LocalSettings.Values.Get("INSTANCE_ACTIVE", -1);
187175
var instance = AppInstance.FindOrRegisterForKey(activePid.ToString());
@@ -219,6 +207,26 @@ args.Arguments is not null &&
219207
});
220208
}
221209

210+
/// <summary>
211+
/// Gets command line args from AppActivationArguments
212+
/// Command line args can be ILaunchActivatedEventArgs, ICommandLineActivatedEventArgs or IProtocolActivatedEventArgs
213+
/// </summary>
214+
private static string? GetCommandLineArgs(AppActivationArguments activatedArgs)
215+
{
216+
// WINUI3: When launching from commandline the argument is not ICommandLineActivatedEventArgs (#10370)
217+
var cmdLaunchArgs = activatedArgs.Data is ILaunchActivatedEventArgs launchArgs &&
218+
launchArgs.Arguments is not null &&
219+
CommandLineParser.SplitArguments(launchArgs.Arguments, true).FirstOrDefault() is string arg0 &&
220+
(arg0.EndsWith($"files.exe", StringComparison.OrdinalIgnoreCase) ||
221+
arg0.EndsWith($"files", StringComparison.OrdinalIgnoreCase)) ? launchArgs.Arguments : null;
222+
var cmdProtocolArgs = activatedArgs.Data is IProtocolActivatedEventArgs protocolArgs &&
223+
protocolArgs.Uri.Query.TrimStart('?').Split('=') is string[] parsedArgs &&
224+
parsedArgs.Length == 2 && parsedArgs[0] == "cmd" ? Uri.UnescapeDataString(parsedArgs[1]) : null;
225+
var cmdLineArgs = activatedArgs.Data is ICommandLineActivatedEventArgs cmdArgs ? cmdArgs.Operation.Arguments : null;
226+
227+
return cmdLaunchArgs ?? cmdProtocolArgs ?? cmdLineArgs;
228+
}
229+
222230
/// <summary>
223231
/// Gets invoked when the application is activated.
224232
/// </summary>

0 commit comments

Comments
 (0)