Skip to content

Commit 205b112

Browse files
Fix issue in winows cli (#4881)
1 parent 344aa78 commit 205b112

1 file changed

Lines changed: 99 additions & 37 deletions

File tree

src/windows-cli.cpp

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,119 @@
11
#include <iostream>
2+
#include <string>
3+
#include <vector>
24
#include <windows.h>
35

4-
std::wstring joinArgs(int argc, wchar_t* argv[])
6+
// Quote a single argument per the CommandLineToArgvW rules so that spaces,
7+
// quotes, and backslashes are preserved as literal data.
8+
static void appendQuotedArg(std::wstring& cmdline, const std::wstring& arg)
59
{
6-
std::wstring result;
7-
for (int i = 1; i < argc; ++i) {
8-
if (i > 1) {
9-
result += L" ";
10+
if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
11+
cmdline.append(arg);
12+
return;
13+
}
14+
15+
cmdline.push_back(L'"');
16+
for (auto it = arg.begin();; ++it) {
17+
unsigned backslashes = 0;
18+
while (it != arg.end() && *it == L'\\') {
19+
++it;
20+
++backslashes;
21+
}
22+
23+
if (it == arg.end()) {
24+
cmdline.append(backslashes * 2, L'\\');
25+
break;
26+
} else if (*it == L'"') {
27+
cmdline.append(backslashes * 2 + 1, L'\\');
28+
cmdline.push_back(*it);
29+
} else {
30+
cmdline.append(backslashes, L'\\');
31+
cmdline.push_back(*it);
1032
}
11-
result += argv[i];
1233
}
13-
return result;
34+
cmdline.push_back(L'"');
1435
}
1536

16-
void CallFlameshot(const std::wstring args, bool wait)
37+
// Launch flameshot.exe (located next to this wrapper) with argv[1..],
38+
// forwarding each argument as literal data. flameshot.exe is passed via
39+
// lpApplicationName so no command interpreter is involved and shell
40+
// metacharacters have no effect. When wait is true, the child's stdout is
41+
// captured and relayed.
42+
void CallFlameshot(int argc, wchar_t* argv[], bool wait)
1743
{
18-
// generate full path for flameshot executable
44+
// Full path to flameshot.exe, in the same directory as this executable.
1945
wchar_t path[MAX_PATH];
20-
int pathLength = GetModuleFileNameW(NULL, path, MAX_PATH);
46+
GetModuleFileNameW(NULL, path, MAX_PATH);
2147
std::wstring pathstring(path);
22-
23-
// Find the last backslash to isolate the filename
2448
size_t lastBackslash = pathstring.find_last_of(L'\\');
2549
std::wstring directory = (lastBackslash != std::wstring::npos)
2650
? pathstring.substr(0, lastBackslash + 1)
2751
: L"";
52+
std::wstring exePath = directory + L"flameshot.exe";
53+
54+
// Build the command line with each argument individually quoted.
55+
std::wstring cmdline;
56+
appendQuotedArg(cmdline, exePath);
57+
for (int i = 1; i < argc; ++i) {
58+
cmdline.push_back(L' ');
59+
appendQuotedArg(cmdline, argv[i]);
60+
}
61+
std::vector<wchar_t> mutableCmd(cmdline.begin(), cmdline.end());
62+
mutableCmd.push_back(L'\0');
63+
64+
HANDLE readEnd = NULL, writeEnd = NULL;
65+
STARTUPINFOW si{};
66+
si.cb = sizeof(si);
2867

29-
// generate command string
30-
// note: binary path placed within quotes in case of spaces in path
31-
int cmdSize = 32 + sizeof(directory) + sizeof(args);
32-
wchar_t* cmd = (wchar_t*)malloc(sizeof(wchar_t) * cmdSize);
33-
swprintf(cmd,
34-
cmdSize,
35-
L"\"%s\\flameshot.exe\" %s",
36-
directory.c_str(),
37-
args.c_str());
38-
// call subprocess
39-
FILE* stream = _wpopen(cmd, L"r");
40-
free(cmd);
4168
if (wait) {
42-
if (stream) {
43-
const int MAX_BUFFER = 2048;
44-
char buffer[MAX_BUFFER];
45-
while (!feof(stream)) {
46-
if (fgets(buffer, MAX_BUFFER, stream) != NULL) {
47-
std::cout << buffer;
48-
}
49-
}
69+
SECURITY_ATTRIBUTES sa{};
70+
sa.nLength = sizeof(sa);
71+
sa.bInheritHandle = TRUE;
72+
if (!CreatePipe(&readEnd, &writeEnd, &sa, 0)) {
73+
return;
5074
}
51-
_pclose(stream);
75+
SetHandleInformation(readEnd, HANDLE_FLAG_INHERIT, 0);
76+
77+
si.dwFlags = STARTF_USESTDHANDLES;
78+
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
79+
si.hStdOutput = writeEnd;
80+
si.hStdError = writeEnd;
5281
}
53-
return;
82+
83+
PROCESS_INFORMATION pi{};
84+
BOOL ok = CreateProcessW(exePath.c_str(),
85+
mutableCmd.data(),
86+
NULL,
87+
NULL,
88+
wait ? TRUE : FALSE,
89+
0,
90+
NULL,
91+
NULL,
92+
&si,
93+
&pi);
94+
95+
if (writeEnd) {
96+
CloseHandle(writeEnd);
97+
}
98+
if (!ok) {
99+
if (readEnd) {
100+
CloseHandle(readEnd);
101+
}
102+
return;
103+
}
104+
105+
if (wait) {
106+
char buffer[2048];
107+
DWORD n = 0;
108+
while (ReadFile(readEnd, buffer, sizeof(buffer), &n, NULL) && n > 0) {
109+
std::cout.write(buffer, n);
110+
}
111+
CloseHandle(readEnd);
112+
WaitForSingleObject(pi.hProcess, INFINITE);
113+
}
114+
115+
CloseHandle(pi.hProcess);
116+
CloseHandle(pi.hThread);
54117
}
55118

56119
// Console 'wrapper' for flameshot on windows
@@ -59,10 +122,9 @@ int wmain(int argc, wchar_t* argv[])
59122
// if no args, do not wait for stdout
60123
if (argc == 1) {
61124
std::cout << "Starting flameshot in daemon mode" << std::endl;
62-
CallFlameshot(L"", false);
125+
CallFlameshot(argc, argv, false);
63126
} else {
64-
std::wstring argString = joinArgs(argc, argv);
65-
CallFlameshot(argString, true);
127+
CallFlameshot(argc, argv, true);
66128
}
67129
std::cout.flush();
68130
return 0;

0 commit comments

Comments
 (0)