Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Codes specifiques à Windows
tatanas
Messages : 39
Inscription : mar. 05/nov./2019 18:40

Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Message par tatanas »

Bonjour tout le monde,

J'ai trouvé un (vieux) code sur le forum allemand qui redirige les STDOutput/In/Err pour une utilisation de CreateProcess.
Malheureusement, comme souvent avec des appels à "cmd", j'ai des problèmes d'affichage avec certains caractères.
J'ai besoin d'utiliser CreateProcessAsUser donc pas de possibilité de substituer avec un RunProgram().
Une idée ?

Merci par avance.

Code : Tout sélectionner

; German forum: http://www.purebasic.fr/german/viewtopic.php?t=2893&highlight=
; Author: Unknown (posted by MXVA)
; Date: 09. April 2005
; OS: Windows
; Demo: No


; Redirecting the (static) console output to a stringgadget 


#D_ReadBufferSize = 2048

Structure D_ProgramInfo 
	StartUpInfo.STARTUPINFO 
	ProcessInfo.PROCESS_INFORMATION 
	SECURITYATTRIBUTES.SECURITY_ATTRIBUTES 
	hOutputPipeRead.i
	hOutputPipeWrite.i
	hInputPipeRead.i
	hInputPipeWrite.i
	hReadThread.i
	ReadThreadStat.i
	ReadBuffer.i
	ReadDataLen.i
EndStructure 


; Procedure.s _OEMToAnsi(sOEM.s)
;    Protected iByteLength = Len(sOEM) + 2
;    Protected sOem_in_Ascii.s = Space(iByteLength)
;    PokeS(@sOem_in_Ascii, sOEM, -1, #PB_Ascii)
;    Protected sUnicode.s = Space(iByteLength)
;    OemToChar_(@sOem_in_Ascii, @sUnicode)
; 
; 	ProcedureReturn sUnicode
; EndProcedure

Procedure D_ReadConsoleOutputThread(*ProgramInfo.D_ProgramInfo) 
	*ProgramInfo\ReadBuffer = AllocateMemory(#D_ReadBufferSize) 
	*ProgramInfo\ReadThreadStat = 1 
	Repeat 
		If *ProgramInfo\ReadThreadStat = 1 
			If ReadFile_(*ProgramInfo\hOutputPipeRead, *ProgramInfo\ReadBuffer, #D_ReadBufferSize, @*ProgramInfo\ReadDataLen, 0) 
				*ProgramInfo\ReadThreadStat = 2 
			EndIf 
		EndIf 
		Delay(1) 
	Until *ProgramInfo\ReadThreadStat = 0 
	FreeMemory(*ProgramInfo\ReadBuffer) 
	*ProgramInfo\hReadThread = #Null 
EndProcedure 

Procedure D_RunProgram(EXEFileName.s, Parameter.s, *ProgramInfo.D_ProgramInfo) 
	Protected result 
	Protected command.s

	result = #False 
	*ProgramInfo\SECURITYATTRIBUTES\nLength = SizeOf(SECURITY_ATTRIBUTES) 
	*ProgramInfo\SECURITYATTRIBUTES\bInheritHandle = 1 
	*ProgramInfo\SECURITYATTRIBUTES\lpSecurityDescriptor = 0 
	
	If CreatePipe_(@*ProgramInfo\hOutputPipeRead, @*ProgramInfo\hOutputPipeWrite, @*ProgramInfo\SECURITYATTRIBUTES, 0) And
		CreatePipe_(@*ProgramInfo\hInputPipeRead, @*ProgramInfo\hInputPipeWrite, @*ProgramInfo\SECURITYATTRIBUTES, 0) 

		*ProgramInfo\StartUpInfo\cb = SizeOf(STARTUPINFO) 
		*ProgramInfo\StartUpInfo\lpDesktop = @"WinSta0\Default"
		*ProgramInfo\StartUpInfo\dwFlags = #STARTF_USESTDHANDLES | #STARTF_USESHOWWINDOW 
		*ProgramInfo\StartUpInfo\hStdOutput = *ProgramInfo\hOutputPipeWrite 
		*ProgramInfo\StartUpInfo\hStdError = *ProgramInfo\hOutputPipeWrite 
		*ProgramInfo\StartUpInfo\hStdInput = *ProgramInfo\hInputPipeRead

		If Parameter <> ""
			command = Chr(34) + EXEFileName + Chr(34) + " " + Parameter
		Else
			command = Chr(34) + EXEFileName + Chr(34)
		EndIf

		If CreateProcess_(#Null, command, @*ProgramInfo\SECURITYATTRIBUTES, @*ProgramInfo\SECURITYATTRIBUTES, 1, #NORMAL_PRIORITY_CLASS, #Null, #Null, @*ProgramInfo\StartUpInfo, @*ProgramInfo\ProcessInfo) 
			*ProgramInfo\hReadThread = CreateThread(@D_ReadConsoleOutputThread(), *ProgramInfo) 
			If *ProgramInfo\hReadThread 
				Delay(10) 
				result = #True 
			EndIf 
		EndIf 

		CloseHandle_(*ProgramInfo\hOutputPipeWrite) 
		CloseHandle_(*ProgramInfo\hInputPipeRead) 
	EndIf 
	ProcedureReturn result 
EndProcedure 

Procedure D_CloseProgram(*ProgramInfo.D_ProgramInfo) 
	*ProgramInfo\ReadThreadStat = 0 
	CloseHandle_(*ProgramInfo\ProcessInfo\hThread) 
	CloseHandle_(*ProgramInfo\hOutputPipeRead) 
	CloseHandle_(*ProgramInfo\hInputPipeWrite) 
	TerminateProcess_(*ProgramInfo\ProcessInfo\hProcess, 0) 
	CloseHandle_(*ProgramInfo\ProcessInfo\hProcess) 
	If *ProgramInfo\hReadThread 
		KillThread(*ProgramInfo\hReadThread) 
	EndIf 
EndProcedure 

Procedure D_GetOutput(*ProgramInfo.D_ProgramInfo, Buffer) 
	Delay(5) 
	If *ProgramInfo\ReadThreadStat = 2 

		OemToCharBuff_(*ProgramInfo\ReadBuffer, Buffer, *ProgramInfo\ReadDataLen) 

		*ProgramInfo\ReadThreadStat = 1 
		ProcedureReturn *ProgramInfo\ReadDataLen
	EndIf 
	ProcedureReturn #False 
EndProcedure 

Procedure D_SendInputString(*ProgramInfo.D_ProgramInfo,string.s) 
	Protected tmp
	CharToOemBuff_(string,string,Len(string)) 
	If WriteFile_(*ProgramInfo\hInputPipeWrite,string,Len(string),@tmp,0) 
		ProcedureReturn tmp 
	EndIf 
	ProcedureReturn #False 
EndProcedure 


;__________________________________________________________________________________________________


#Gadget_String = 0 

OpenWindow(0,0,0,400,200,"Console", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
StringGadget(#Gadget_String,0,0,400,200,"", #ES_MULTILINE|#PB_String_ReadOnly) 

Mem = AllocateMemory(#D_ReadBufferSize) 

If D_RunProgram("cmd.exe", "/c dir", @ProgramInfo.D_ProgramInfo)
	
	Repeat 
		len = D_GetOutput(@ProgramInfo, Mem) 
		If len 
			SetGadgetText(#Gadget_String, GetGadgetText(#Gadget_String) + PeekS(Mem,len)) 
		EndIf 
	Until len = 0 
	
	tmpstring.s = "" 
	D_SendInputString(@ProgramInfo,tmpstring) 
	
	Repeat 
		len = D_GetOutput(@ProgramInfo, Mem) 
		If len 
			SetGadgetText(#Gadget_String, GetGadgetText(#Gadget_String) + PeekS(Mem,len)) 
			Received + len 
		EndIf 
	Until WaitWindowEvent() = #PB_Event_CloseWindow 
	
	D_CloseProgram(@ProgramInfo) 
Else 
	MessageRequester("","Exécutable introuvable",16) 
EndIf 

FreeMemory(Mem)
Marc56
Messages : 2196
Inscription : sam. 08/févr./2014 15:19

Re: Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Message par Marc56 »

Hello,

Un programme avec API et vieux de 15 ans, je cherche pas à réparer, mais voilà quelques idées:
  • Changement de code page de la console pour avoir l'Utf-8 (envoyer CHCP 65001 et chainer les commandes "&")
    Exemple:
    RunProgram("cmd", "/c chcp 65001 & dir & pause ", "")
  • Utilisation du canal d'erreur en ligne de commande 2>&1
  • Le canal d'erreur est utilisable dans RunProgram #PB_Program_Error
:wink:
Avatar de l’utilisateur
Pierre Bellisle
Messages : 25
Inscription : jeu. 21/juin/2018 6:01

Re: Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Message par Pierre Bellisle »

Salut tatanas,
Les CrLf reviennent sous forme Chr($266A) + Chr($25D9).
Une lecture intéressante... Injecting new line characters (e.g. CR LF) into security logs with Unicode
> 000D 266A 02 IBMGRAPH
> 000A 25D9 -- IBMGRAPH
Remplace la partie suivante de ton code et essai l'exe qui en résulte.

Salutation...

Code : Tout sélectionner

;______________________________________________________________________________

#Gadget_String = 101 
OpenWindow(0, 0, 0, 400, 600, "Console text", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
EditorGadget(#Gadget_String, 0, 0, 400, 600, #PB_Editor_WordWrap) ; Instead of StringGadget

*Mem = AllocateMemory(#D_ReadBufferSize) 

If D_RunProgram("cmd.exe", "/c dir", @ProgramInfo.D_ProgramInfo)
  
  Repeat 
    len = D_GetOutput(@ProgramInfo, *Mem) 
    If len 
      ;SetGadgetText(#Gadget_String, GetGadgetText(#Gadget_String) + PeekS(*Mem,len)) 
      log.s = log + PeekS(*Mem, len)
      log   = ReplaceString(log, Chr($266a) + Chr($25d9), Chr(13)  + Chr(10)) ;266a 25d9 > CrLf Unicode
      SetGadgetText(#Gadget_String, log) 
    EndIf 
  Until len = 0 
  
  tmpstring.s = "" 
  D_SendInputString(@ProgramInfo,tmpstring) 
  
  Repeat 
    len = D_GetOutput(@ProgramInfo, *Mem) 
    If len 
      ;SetGadgetText(#Gadget_String, GetGadgetText(#Gadget_String) + PeekS(*Mem,len)) 
      log = log + PeekS(*Mem, len)
      log = ReplaceString(log, Chr($266a) +  Chr($25d9), Chr(13) + Chr(10)) ;266a 25d9 > CrLf Unicode
      SetGadgetText(#Gadget_String, log) 
      ;Received + len 
    EndIf 
  Until WaitWindowEvent() = #PB_Event_CloseWindow 
  
  D_CloseProgram(@ProgramInfo) 
Else 
  MessageRequester("","Exécutable introuvable",16) 
EndIf 

;Seems it is too soon to FreeMemory(*Mem), so let it be done automatically by the program. 
;FreeMemory(*Mem) ;Will be done automatically at the end of program
;______________________________________________________________________________
;
tatanas
Messages : 39
Inscription : mar. 05/nov./2019 18:40

Re: Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Message par tatanas »

Salut Pierre,

Désolé d'avoir tardé à répondre, j'étais en congé :).

Ton code fonctionne parfaitement, l'affichage est bien meilleur !

Merci encore.
Avatar de l’utilisateur
Pierre Bellisle
Messages : 25
Inscription : jeu. 21/juin/2018 6:01

Re: Redirection de STDIN/OUT/ERR pour CreateProcess - problème d'affichage

Message par Pierre Bellisle »

Merveilleux...
Au plaisir ;-)
Répondre