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