@@ -173,27 +173,34 @@ pub(super) fn spawn(
173173 } ;
174174 let mut process_information = PROCESS_INFORMATION :: default ( ) ;
175175
176- let start_directory = options
176+ let start_directory_path = options
177177 . start_dir
178178 . filter ( |start_dir| start_dir. is_dir ( ) )
179179 . or_else ( || {
180180 std:: env:: var_os ( "USERPROFILE" )
181181 . map ( PathBuf :: from)
182182 . filter ( |path| path. is_dir ( ) )
183- } )
183+ } ) ;
184+ let start_directory_wide = start_directory_path
185+ . as_ref ( )
186+ . map ( |path| path. as_os_str ( ) . encode_wide ( ) . collect :: < Vec < u16 > > ( ) ) ;
187+ let start_directory = start_directory_path
188+ . as_ref ( )
184189 . map ( |path| HSTRING :: from ( path. as_os_str ( ) ) ) ;
185190
191+ let creation_flags = PROCESS_CREATION_FLAGS ( 0 )
192+ | EXTENDED_STARTUPINFO_PRESENT
193+ | CREATE_UNICODE_ENVIRONMENT
194+ | CREATE_BREAKAWAY_FROM_JOB ;
195+
186196 unsafe {
187197 CreateProcessW (
188198 PCWSTR :: null ( ) , /* lpApplicationName */
189- Some ( PWSTR :: from_raw ( shell_command. as_ptr ( ) . cast_mut ( ) ) ) ,
199+ Some ( PWSTR :: from_raw ( shell_command. raw . as_ptr ( ) . cast_mut ( ) ) ) ,
190200 None , /* lpProcessAttributes */
191201 None , /* lpThreadAttributes */
192202 false , /* bInheritHandles */
193- PROCESS_CREATION_FLAGS ( 0 )
194- | EXTENDED_STARTUPINFO_PRESENT
195- | CREATE_UNICODE_ENVIRONMENT
196- | CREATE_BREAKAWAY_FROM_JOB ,
203+ creation_flags,
197204 Some ( environment_block. as_ptr ( ) as * const std:: ffi:: c_void ) ,
198205 start_directory
199206 . as_ref ( )
@@ -204,6 +211,17 @@ pub(super) fn spawn(
204211 )
205212 . map_err ( |error| {
206213 let detail = shell_starter. shell_detail ( ) ;
214+ log_create_process_failure (
215+ & error,
216+ & detail,
217+ & shell_command,
218+ & environment_block,
219+ start_directory_wide. as_deref ( ) ,
220+ creation_flags,
221+ & startup_info,
222+ size. columns ,
223+ size. rows ,
224+ ) ;
207225 PtySpawnError :: CreateShellProcessFailed { detail, error }
208226 } ) ?;
209227 }
@@ -232,7 +250,23 @@ pub enum EncodingError {
232250 Other ( #[ from] windows:: core:: Error ) ,
233251}
234252
235- fn wsl_shell_command ( wsl_shell_starter : & WslShellStarter ) -> Result < HSTRING , EncodingError > {
253+ struct EncodedShellCommand {
254+ raw : HSTRING ,
255+ wide_units : Vec < u16 > ,
256+ }
257+
258+ impl EncodedShellCommand {
259+ fn new ( wide_units : Vec < u16 > ) -> Self {
260+ Self {
261+ raw : HSTRING :: from_wide ( & wide_units) ,
262+ wide_units,
263+ }
264+ }
265+ }
266+
267+ fn wsl_shell_command (
268+ wsl_shell_starter : & WslShellStarter ,
269+ ) -> Result < EncodedShellCommand , EncodingError > {
236270 let mut encoded_shell_command = Vec :: < u16 > :: new ( ) ;
237271
238272 log:: info!(
@@ -247,14 +281,14 @@ fn wsl_shell_command(wsl_shell_starter: &WslShellStarter) -> Result<HSTRING, Enc
247281 }
248282 append_quoted ( arg, & mut encoded_shell_command) ;
249283 }
250- Ok ( HSTRING :: from_wide ( & encoded_shell_command) )
284+ Ok ( EncodedShellCommand :: new ( encoded_shell_command) )
251285}
252286
253287/// Constructs the shell command in a Windows-native encoding using 16-bit values.
254288///
255289/// Windows expects a single string for its command which includes all arguments, so we need to
256290/// string them together and escape them appropriately.
257- fn shell_command ( shell_starter : & DirectShellStarter ) -> Result < HSTRING , EncodingError > {
291+ fn shell_command ( shell_starter : & DirectShellStarter ) -> Result < EncodedShellCommand , EncodingError > {
258292 let mut encoded_shell_command = Vec :: < u16 > :: new ( ) ;
259293
260294 log:: info!(
@@ -273,7 +307,63 @@ fn shell_command(shell_starter: &DirectShellStarter) -> Result<HSTRING, Encoding
273307 }
274308 append_quoted ( arg, & mut encoded_shell_command) ;
275309 }
276- Ok ( HSTRING :: from_wide ( & encoded_shell_command) )
310+ Ok ( EncodedShellCommand :: new ( encoded_shell_command) )
311+ }
312+
313+ fn log_create_process_failure (
314+ error : & windows:: core:: Error ,
315+ detail : & str ,
316+ shell_command : & EncodedShellCommand ,
317+ environment_block : & [ u16 ] ,
318+ start_directory : Option < & [ u16 ] > ,
319+ creation_flags : PROCESS_CREATION_FLAGS ,
320+ startup_info : & STARTUPINFOEXW ,
321+ columns : usize ,
322+ rows : usize ,
323+ ) {
324+ let shell_command_bytes = wide_units_as_le_bytes_with_nul ( & shell_command. wide_units , 1 ) ;
325+ let environment_block_bytes = wide_units_as_le_bytes ( environment_block) ;
326+ let start_directory_bytes = start_directory
327+ . map ( |wide| wide_units_as_le_bytes_with_nul ( wide, 1 ) )
328+ . unwrap_or_default ( ) ;
329+
330+ log:: error!(
331+ "CreateProcessW failed for shell process {detail}: {error:#}. \
332+ debug lpApplicationName_is_null=true \
333+ lpCommandLine_utf16_units_len={} lpCommandLine_bytes={:?} \
334+ lpEnvironment_utf16_units_len={} lpEnvironment_bytes={:?} \
335+ lpCurrentDirectory_is_null={} lpCurrentDirectory_utf16_units_len={} lpCurrentDirectory_bytes={:?} \
336+ dwCreationFlags={:#010x} bInheritHandles=false \
337+ startup_cb={} startup_dwFlags={:#010x} startup_lpAttributeList_is_null={} \
338+ conpty_size_columns={} conpty_size_rows={}",
339+ shell_command. wide_units. len( ) + 1 ,
340+ shell_command_bytes,
341+ environment_block. len( ) ,
342+ environment_block_bytes,
343+ start_directory. is_none( ) ,
344+ start_directory. map_or( 0 , |wide| wide. len( ) + 1 ) ,
345+ start_directory_bytes,
346+ creation_flags. 0 ,
347+ startup_info. StartupInfo . cb,
348+ startup_info. StartupInfo . dwFlags. 0 ,
349+ startup_info. lpAttributeList. is_null( ) ,
350+ columns,
351+ rows,
352+ ) ;
353+ }
354+
355+ fn wide_units_as_le_bytes ( wide_units : & [ u16 ] ) -> Vec < u8 > {
356+ wide_units
357+ . iter ( )
358+ . flat_map ( |unit| unit. to_le_bytes ( ) )
359+ . collect ( )
360+ }
361+
362+ fn wide_units_as_le_bytes_with_nul ( wide_units : & [ u16 ] , trailing_nuls : usize ) -> Vec < u8 > {
363+ let mut bytes = Vec :: with_capacity ( ( wide_units. len ( ) + trailing_nuls) * 2 ) ;
364+ bytes. extend ( wide_units_as_le_bytes ( wide_units) ) ;
365+ bytes. resize ( bytes. len ( ) + trailing_nuls * 2 , 0 ) ;
366+ bytes
277367}
278368
279369/// Appends an argument and properly quotes it.
0 commit comments