| 227 | #ifdef _WIN32 |
| 228 | |
| 229 | static int cbm_run_win(const cbm_proc_opts_t *opts, cbm_proc_result_t *out) { |
| 230 | const char *bin = opts->bin; |
| 231 | const char *const default_argv[] = {bin, NULL}; |
| 232 | const char *const *argv = opts->argv ? opts->argv : default_argv; |
| 233 | |
| 234 | char cmdline[8192]; |
| 235 | if (!cbm_build_win_cmdline(cmdline, sizeof(cmdline), argv)) { |
| 236 | out->outcome = CBM_PROC_SPAWN_FAILED; |
| 237 | out->exit_code = -1; |
| 238 | out->term_signal = 0; |
| 239 | return -1; |
| 240 | } |
| 241 | /* Spawn via CreateProcessW with a WIDE command line. CreateProcessA would |
| 242 | * re-interpret our UTF-8 cmdline bytes through the ANSI code page (CP_ACP), |
| 243 | * re-mangling a non-ASCII repo path at the parent->worker boundary — so the |
| 244 | * worker's own wide-argv read could never recover it (#423/#20). */ |
| 245 | wchar_t *wcmd = cbm_utf8_to_wide(cmdline); |
| 246 | if (!wcmd) { |
| 247 | out->outcome = CBM_PROC_SPAWN_FAILED; |
| 248 | out->exit_code = -1; |
| 249 | out->term_signal = 0; |
| 250 | return -1; |
| 251 | } |
| 252 | |
| 253 | HANDLE hlog = INVALID_HANDLE_VALUE; |
| 254 | STARTUPINFOW si = {.cb = sizeof(si)}; |
| 255 | if (opts->log_file) { |
| 256 | hlog = CreateFileA(opts->log_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, |
| 257 | FILE_ATTRIBUTE_NORMAL, NULL); |
| 258 | if (hlog != INVALID_HANDLE_VALUE) { |
| 259 | si.dwFlags = STARTF_USESTDHANDLES; |
| 260 | si.hStdError = hlog; |
| 261 | si.hStdOutput = hlog; |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | PROCESS_INFORMATION pi = {0}; |
| 266 | BOOL ok = CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); |
| 267 | free(wcmd); |
| 268 | if (hlog != INVALID_HANDLE_VALUE) { |
| 269 | CloseHandle(hlog); |
| 270 | } |
| 271 | if (!ok) { |
| 272 | out->outcome = CBM_PROC_SPAWN_FAILED; |
| 273 | out->exit_code = -1; |
| 274 | out->term_signal = 0; |
| 275 | return -1; |
| 276 | } |
| 277 | |
| 278 | long tail_pos = 0; |
| 279 | uint64_t last_activity = cbm_now_ms(); |
| 280 | bool timed_out = false; |
| 281 | for (;;) { |
| 282 | DWORD w = WaitForSingleObject(pi.hProcess, 200); |
| 283 | if (cbm_tail_log(opts->log_file, &tail_pos, opts->on_log_line, opts->log_ud)) { |
| 284 | last_activity = cbm_now_ms(); |
| 285 | } |
| 286 | if (w == WAIT_OBJECT_0) { |
no test coverage detected