@@ -27,104 +27,110 @@ internal actual suspend fun executeCommand(
2727 memScoped {
2828 // 1) Make a temp file to capture stdout+stderr
2929 val tmpDirBuf = allocArray<UShortVar >(MAX_PATH )
30- val tmpNameBuf = allocArray<UShortVar >(MAX_PATH )
31- val gotTmp = GetTempPathW (MAX_PATH , tmpDirBuf) != 0u
32- if (! gotTmp) error(" GetTempPathW failed" )
30+ val gotTmp = GetTempPathW (MAX_PATH .toUInt(), tmpDirBuf)
31+ if (gotTmp == 0u ) error(" GetTempPathW failed" )
3332
34- val gotName = GetTempFileNameW (tmpDirBuf, " KNR" .wideCString(this ), 0u , tmpNameBuf) != 0u
35- if (! gotName) error(" GetTempFileNameW failed" )
36- val outPath = tmpNameBuf
33+ val tmpNameBuf = allocArray<UShortVar >(MAX_PATH )
34+ val tmpDirStr = tmpDirBuf.toKString()
35+ val gotName = GetTempFileNameW (tmpDirStr, " KNR" , 0u , tmpNameBuf)
36+ if (gotName == 0u ) error(" GetTempFileNameW failed" )
37+ val outPath: String = tmpNameBuf.toKString()
3738
38- // Create the file for the child to write into (inherit handle)
39+ // 2) Create the output file ( child inherits this handle)
3940 val sa = alloc<SECURITY_ATTRIBUTES >().apply {
4041 nLength = sizeOf<SECURITY_ATTRIBUTES >().toUInt()
4142 bInheritHandle = TRUE
4243 lpSecurityDescriptor = null
4344 }
45+
4446 val hOut: HANDLE = CreateFileW (
45- outPath,
46- ( GENERIC_WRITE or FILE_GENERIC_WRITE ) .toUInt(),
47- FILE_SHARE_READ or FILE_SHARE_WRITE ,
48- sa.ptr,
49- CREATE_ALWAYS ,
50- FILE_ATTRIBUTE_NORMAL .toUInt(),
51- null ,
47+ /* lpFileName = */ outPath,
48+ /* dwDesiredAccess = */ GENERIC_WRITE .toUInt(),
49+ /* dwShareMode = */ ( FILE_SHARE_READ or FILE_SHARE_WRITE ) ,
50+ /* lpSecurityAttributes = */ sa.ptr,
51+ /* dwCreationDisposition = */ CREATE_ALWAYS ,
52+ /* dwFlagsAndAttributes = */ FILE_ATTRIBUTE_NORMAL .toUInt(),
53+ /* hTemplateFile = */ null ,
5254 )
53- if (hOut == INVALID_HANDLE_VALUE ) error(" CreateFileW failed for temp output" )
55+ if (hOut == INVALID_HANDLE_VALUE ) error(" CreateFileW failed for temp output (GetLastError= ${ GetLastError ()} ) " )
5456
5557 try {
56- // 2) Build command: use cmd.exe /C "<command>"
57- // Prefer %ComSpec% if present, else fallback.
58- val comspecBuf = allocArray<WCHARVar >(MAX_PATH )
59- val comspecLen = GetEnvironmentVariableW (" ComSpec" .wideCString(this ), comspecBuf, MAX_PATH )
60- val cmdExe = if (comspecLen > 0u ) {
61- comspecBuf
58+ // 3) Resolve the shell (prefer %ComSpec%)
59+ val comspecBuf = allocArray<UShortVar >(MAX_PATH )
60+ val comspecLen = GetEnvironmentVariableW (" ComSpec" , comspecBuf, MAX_PATH .toUInt())
61+ val cmdExe: String = if (comspecLen > 0u ) {
62+ comspecBuf.toKString()
6263 } else {
63- " C:\\ Windows\\ System32\\ cmd.exe" .wideCString( this )
64+ " C:\\ Windows\\ System32\\ cmd.exe"
6465 }
6566
66- val cmdLine = (" /C " + command).wideCString(this )
67+ // Build mutable command line buffer for CreateProcessW
68+ // CreateProcessW expects the *command line* buffer to be mutable (it may write into it)
69+ val cmdLineStr = " /C $command "
70+ val cmdLineBuf = cmdLineStr.wideCString(this ) // writable buffer from MemScope
6771
68- // 3 ) Launch child with redirected stdout/stderr
72+ // 4 ) Launch child with redirected stdout/stderr
6973 val si = alloc<STARTUPINFOW >().apply {
7074 cb = sizeOf<STARTUPINFOW >().toUInt()
7175 dwFlags = STARTF_USESTDHANDLES .toUInt()
7276 hStdOutput = hOut
7377 hStdError = hOut
74- hStdInput = GetStdHandle (STD_INPUT_HANDLE ) // leave as-is
78+ hStdInput = GetStdHandle (STD_INPUT_HANDLE )
7579 }
7680 val pi = alloc<PROCESS_INFORMATION >()
7781
7882 val created = CreateProcessW (
79- cmdExe,
80- cmdLine, // mutable buffer OK; wcstr gives writable copy here
81- null ,
82- null ,
83- TRUE , // inherit handles (so child gets hOut)
84- CREATE_NO_WINDOW .toUInt(),
85- null ,
86- null ,
87- si.ptr,
88- pi.ptr,
83+ /* lpApplicationName = */ cmdExe,
84+ /* lpCommandLine = */ cmdLineBuf, // mutable
85+ /* lpProcessAttributes = */ null ,
86+ /* lpThreadAttributes = */ null ,
87+ /* bInheritHandles = */ TRUE ,
88+ /* dwCreationFlags = */ CREATE_NO_WINDOW .toUInt(),
89+ /* lpEnvironment = */ null ,
90+ /* lpCurrentDirectory = */ null ,
91+ /* lpStartupInfo = */ si.ptr,
92+ /* lpProcessInformation= */ pi.ptr,
8993 )
90- if (created == 0 ) error(" CreateProcessW failed: ${GetLastError ()} " )
94+ if (created == 0 ) error(" CreateProcessW failed (GetLastError= ${GetLastError ()} ) " )
9195
9296 try {
93- // 4 ) Wait up to timeout; if it times out, terminate
94- val waitRc = WaitForSingleObject (pi.hProcess, timeoutMillis.toUInt())
95- if (waitRc == WAIT_TIMEOUT ) {
97+ // 5 ) Wait up to timeout; if it times out, terminate
98+ val waitRc: UInt = WaitForSingleObject (pi.hProcess, timeoutMillis.toUInt())
99+ if (waitRc == WAIT_TIMEOUT .toUInt() ) {
96100 TerminateProcess (pi.hProcess, 124u )
97- // close I/O + process handles and delete temp before throwing
98101 CloseHandle (hOut)
99102 CloseHandle (pi.hThread)
100103 CloseHandle (pi.hProcess)
101- _wunlink (outPath)
104+ _wunlink (outPath.wideCString( this ) )
102105 error(" Process timed out after ${timeoutMillis} ms" )
103106 }
104107
105- // 5 ) Get exit code
108+ // 6 ) Get exit code
106109 val exitCodeVar = alloc<DWORDVar >()
107- GetExitCodeProcess (pi.hProcess, exitCodeVar.ptr)
110+ if (GetExitCodeProcess (pi.hProcess, exitCodeVar.ptr) == 0 ) {
111+ // If this fails, propagate a generic error but still try to read output
112+ // (fall through with exitCode = -1)
113+ exitCodeVar.value = 0xFFFFFFFFu
114+ }
108115 val exitCode = exitCodeVar.value.toInt()
109116
110- // 6) Read the file (up to maxOutputLengthBytes)
111- // Re-open for reading (child still closed hOut on exit)
117+ // 7) Read back the temp file (bounded)
112118 val hIn: HANDLE = CreateFileW (
113- outPath,
114- GENERIC_READ .toUInt(),
115- FILE_SHARE_READ or FILE_SHARE_WRITE ,
116- null ,
117- OPEN_EXISTING ,
118- FILE_ATTRIBUTE_NORMAL .toUInt(),
119- null ,
119+ /* lpFileName = */ outPath,
120+ /* dwDesiredAccess = */ GENERIC_READ .toUInt(),
121+ /* dwShareMode = */ ( FILE_SHARE_READ or FILE_SHARE_WRITE ) ,
122+ /* lpSecurityAttributes = */ null ,
123+ /* dwCreationDisposition = */ OPEN_EXISTING ,
124+ /* dwFlagsAndAttributes = */ FILE_ATTRIBUTE_NORMAL .toUInt(),
125+ /* hTemplateFile = */ null ,
120126 )
121127 if (hIn == INVALID_HANDLE_VALUE ) {
122- // Clean up and bail
123- _wunlink (outPath)
128+ _wunlink (outPath.wideCString(this ))
124129 CloseHandle (pi.hThread)
125130 CloseHandle (pi.hProcess)
126131 return @memScoped exitCode to " "
127132 }
133+
128134 val sb = StringBuilder ()
129135 val buf = ByteArray (4096 )
130136 val bytesReadVar = alloc<DWORDVar >()
@@ -135,7 +141,7 @@ internal actual suspend fun executeCommand(
135141 if (toRead <= 0 ) {
136142 // ensure cleanup before throwing
137143 CloseHandle (hIn)
138- _wunlink (outPath)
144+ _wunlink (outPath.wideCString( this ) )
139145 throw CredentialsProviderException (" Process output exceeded limit of $maxOutputLengthBytes bytes" )
140146 }
141147 val n = buf.usePinned {
@@ -148,8 +154,9 @@ internal actual suspend fun executeCommand(
148154 }
149155 } finally {
150156 CloseHandle (hIn)
151- _wunlink (outPath)
157+ _wunlink (outPath.wideCString( this ) )
152158 }
159+
153160 exitCode to sb.toString()
154161 } finally {
155162 CloseHandle (pi.hThread)
0 commit comments