Page 1 of 1

Stop Program Execution of RunProgram("Robocopy",...)

Posted: Fri Dec 11, 2020 6:09 pm
by Axolotl
Hi Guys,

how can I stop the program execution of RunProgram("Robocopy",...) before it ends regularly.
On console it works by ctrl+c
in my test code there are three ideas - none of them are working (?)
if you want to check them out play with the comment/uncomment feature. See the lines ;' TEST-1 to ;' TEST-3

I read about killprogram() but i am not really sure about that. Mayby someone can confirm that it is a proper way to cancel the execution.

Any kind of information will be helpful.
Thanks in Advance.

Here is the code:

Code: Select all

EnableExplicit 
;DebugLevel 9 

;' Robocopy Constants 
#ROBOCOPY_Exec$         = "c:\windows\system32\robocopy.exe" 
#ROBOCOPY_ProgramFlags  = #PB_Program_Open|#PB_Program_Read|#PB_Program_Write ;|#PB_Program_Hide 
#ROBOCOPY_ConsoleClass$ = "ConsoleWindowClass" 


;' Global Variables -- init to existing directories 
Global SourceDir$ = RTrim(#PB_Compiler_Home, "\")           ;' directory with a lot of files and folders 
Global DestDir$   = RTrim(GetTemporaryDirectory(), "\")     ;' some place to copy to 


Procedure.s FormatMessage(ErrorCode)  ;' returns 'message from system according to errorcode' 
  Protected result.s, *buf, buflen 

  buflen = FormatMessage_(#FORMAT_MESSAGE_ALLOCATE_BUFFER | #FORMAT_MESSAGE_FROM_SYSTEM, 0, ErrorCode, 0, @*buf, 0, 0)
  If buflen 
    result = PeekS(*buf, buflen-2)
    LocalFree_(*buf)
    result + " " + Str(ErrorCode) + ", 0x" + RSet(Hex(ErrorCode), 8, "0") 
  EndIf 
  ProcedureReturn result 
EndProcedure ;FormatMessage() 
Procedure.s GetClassName(hWnd)  ;' returns classname (string) 
  Protected Classname${256} 
  GetClassName_(hWnd, @Classname$, 256) 
  ProcedureReturn Classname$
EndProcedure ;() 
Procedure.s GetCaption(hWnd)  ;' returns classname (string) 
  Protected txt${256} 
  GetWindowText_(hWnd, @txt$, 256) 
  ProcedureReturn txt$
EndProcedure ;() 


Procedure RunRobocopy()  ;' the main procedure of this demo code 
  Protected PrgID, DataSize, bytes, rc, count , hWnd, value 
  Protected *MemBuf, MemSize = 1024 
  Protected param$, st$  
  
  SourceDir$ = #DQUOTE$+RTrim(SourceDir$, "\")+#DQUOTE$    :Debug "SourceDir = '"+SourceDir$+"'"
  DestDir$   = #DQUOTE$+RTrim(DestDir$  , "\")+#DQUOTE$    :Debug "DestDir   = '"+DestDir$+"'"  

  param$ = SourceDir$ + " " + DestDir$ + " *.* /E /COPY:DAT /R:1 /W:1 " ;' add some options 
  param$ + " /L"                                                        ;' list all files only - no copy action will take place 
  Debug "  Parameters = '"+param$+"'" 

  *MemBuf = AllocateMemory(MemSize, #PB_Memory_NoClear)
  If *MemBuf 
    PrgID = RunProgram(#ROBOCOPY_Exec$, param$, "", #ROBOCOPY_ProgramFlags) 
    If PrgID <> 0                                                      :Debug #LF$+"start"
      While ProgramRunning(PrgID) 
        DataSize = AvailableProgramOutput(PrgID)
        If DataSize > 0                                                :Debug "  datasize = "+DataSize, 9  
           While DataSize > MemSize 
              bytes = ReadProgramData(PrgID, *MemBuf, MemSize)         :Debug "    Bytes0:"+Str(bytes), 9 
              WriteConsoleData(*MemBuf, MemSize)
              DataSize - MemSize
           Wend
           bytes = ReadProgramData(PrgID, *MemBuf, DataSize)           :Debug "    Bytes1:"+Str(bytes), 9 
           WriteConsoleData(*MemBuf, DataSize) 
        EndIf 
Delay(10)  ;' give the force to someone else 
count + 1  ;' simulate the user wish 

If count = 10                                                          :Debug "Force an user requested stop (at count ="+count+")" 

;' TEST-1 ;­---------------------------------------------------------------------------------
      rc = WriteProgramData(PrgID, #PB_Program_Eof, 0)                    :Debug " --> Write EOF -- written=" +rc

; ;' TEST-2 ;­---------------------------------------------------------------------------------
; ;      hWnd = FindWindow_(0, #ROBOCOPY_Exec$)                         :Debug "  found hWnd = "+hWnd 
;         hWnd = FindWindow_(#ROBOCOPY_ConsoleClass$, 0)                 :Debug "  found hWnd = "+hWnd 
;         If hWnd 
;           Debug "  Caption   = "+GetCaption(hWnd) 
;           Debug "  Classname = "+GetClassName(hWnd) 
;           Debug "" 
;           value = 1 ;' get the ProcessID back ....
;           Debug "  GetWindowThreadProcessID = "+GetWindowThreadProcessId_(hWnd, @value)
;           Debug "    -> ProcessID           = "+value  
;           
; ;         SendMessage_(hWnd, #WM_CLOSE, 0, 0)                          :Debug "Send WM_CLOSE " 
;         EndIf 

; ;' TEST-3 ;­---------------------------------------------------------------------------------
;         If GenerateConsoleCtrlEvent_(#CTRL_C_EVENT, ProgramID(PrgID))  :Debug " --> send Ctrl+C " 
;           rc = GetLastError_()                              
;           Debug "error " + FormatMessage(rc) 
;         EndIf 

EndIf ;' count = 10 

      Wend ;' ProgramRunning() 
      CloseProgram(PrgID)

Debug #LF$+"count = "+count   ;'  should be 10 if successfully 
Debug "stop"

    EndIf ;PrgID <> 0 

    FreeMemory(*MemBuf) 
  EndIf ; *MemBuf 
  ProcedureReturn 0 
EndProcedure ;() 


;' Main Program -- starts here 
If OpenConsole() 
  ConsoleTitle ("PureBasic - Console Robocopy Test")  
  PrintN("Robocopy started ")
  RunRobocopy() 
  PrintN("Robocopy done ") 

  CompilerIf Not #PB_Editor_CreateExecutable ;' at development time 
    PrintN(""+#LF$+"Press return to close the console window ") 
    Input() 
  CompilerEndIf 

  PrintN("Bye") 
  CloseConsole() 
EndIf 

Re: Stop Program Execution of RunProgram("Robocopy",...)

Posted: Fri Dec 11, 2020 6:31 pm
by RASHAD
Maybe you can add #PB_Program_Wait to RunProgram flags

Code: Select all

#ROBOCOPY_ProgramFlags  = #PB_Program_Open|#PB_Program_Read|#PB_Program_Write |#PB_Program_Wait
Then you can use KillProgram(Program)
Or
Send Ctrl-c to Console

Code: Select all

Global hInput

Procedure SendConsole(szText.s)
  szLength = Len(szText.s)
  For sz = 0 To szLength+1
    szCharacter$ = Mid(szText.s,sz+1,1)
    szLetterByLetter=Asc(szCharacter$)
    PostMessage_(hInput,#WM_CHAR,szLetterByLetter,0)
  Next
  PostMessage_(hInput,#WM_CHAR,13,0)
EndProcedure

hInput = FindWindow_(0,"Command Prompt")
If hInput = 0
  Debug "Error, command prompt not open or string mismatch!"
Else
  SendConsole("echo Hello world!! This is a string, I think? What about numbers? 1+1=393")
EndIf
Or send multiple keys (Ctrl-c) using the splendid code by LJ

Not tested

Re: Stop Program Execution of RunProgram("Robocopy",...)

Posted: Fri Dec 11, 2020 7:56 pm
by fryquez

Code: Select all

Procedure _ConsoleSendCtrlC(iPID, iLoop = 1, iSlp = 10)
  
  Protected x
  
  If AttachConsole_(iPid)
    If Not SetConsoleCtrlHandler_(0, 1)
      FreeConsole_()
      ProcedureReturn 0
    EndIf
    
    For x = 1 To iLoop
      GenerateConsoleCtrlEvent_(0, 0)
      Sleep_(iSlp)
    Next
    
    FreeConsole_()
    SetConsoleCtrlHandler_(0, 0)   
    
  EndIf
EndProcedure


Compiler = RunProgram("cmd", "/c dir C:\ /b /s", "", #PB_Program_Open | #PB_Program_Read)
Output$ = ""
If Compiler
  While ProgramRunning(Compiler)
    If AvailableProgramOutput(Compiler)
      Output$ + ReadProgramString(Compiler) + Chr(13)
    EndIf
    
    If Len(Output$) > 1024
      _ConsoleSendCtrlC(ProgramID(Compiler))
    EndIf
    
  Wend
  
  Output$ + Chr(13) + Chr(13)
  Output$ + "Exitcode: " + Str(ProgramExitCode(Compiler))
  
  CloseProgram(Compiler) ; Close the connection to the program
EndIf

Debug Output$

Re: Stop Program Execution of RunProgram("Robocopy",...)

Posted: Sat Dec 12, 2020 3:14 pm
by Axolotl
Thank you for your support.

Here you can read my current status (not finalize, I would like to be responsive)
Honestly I am lost in space with this stuff...

Unfortunately your suggestions do not work here.
I tried your codes and changed some to get rid of the compiler and linker error messages, but nothing was really successful.

in detail:
@RASHAD:
#PB_Program_Wait cannot combined with communication flags.
My goal is to use this from an windows gui. (console app is only for testing, sorry if I did not mentioned that earlier)
BTW I run PB 5.73 LTS (x64)

your code SendConsole(hConsoleWnd, szText.s)
works with some modifications. (Sorry) But it is not possible to send an ctrl+c via #WM_CHAR. surely you can send a 3 to the console, it still reacts not as expected.


@fryquez:
AttachConsole_() is not a known procedure.
adding the AttachConsole() in an import "Kernel32.lib" section the procedure returns an errorcode 5 (Acces denied (translated from german Zugriff verweigert!)
edit: another change helps with my robocopy call....

according to msdn text:
A process can be associated with only one console, so the AllocConsole function fails if the calling process already has a console.


i made a change (no success anyway )

Code: Select all


Import "Kernel32.lib"
  AttachConsole(dwProcessId) 
EndImport 

Procedure _ConsoleSendCtrlC(iPID, iLoop = 1, iSlp = 10)
  Static count=0
  Protected x

  FreeConsole_()  ;' <== this helps a lot !! -- because AttachConsole returns nonzero 

  If AttachConsole(iPid)                          :Debug "AttachConsole() " 
    If Not SetConsoleCtrlHandler_(0, 1)           :Debug "  Not SetConsoleCtrlHandler() " 
      FreeConsole_()
      ProcedureReturn 0
    EndIf
   
    For x = 1 To iLoop
      GenerateConsoleCtrlEvent_(0, 0)
      Sleep_(iSlp)
    Next
    FreeConsole_()
    SetConsoleCtrlHandler_(0, 0)   
  Else 
    x = GetLastError_()
    Debug "Error: "+FormatMessage(x) 

    FreeConsole_()
    If count = 0 
      count + 1 
      _ConsoleSendCtrlC(iPID, iLoop, iSlp) :Debug "recursive call !!??? " 
    EndIf 

  EndIf
EndProcedure
Hopefully i find a solution otherwise I have to stick with my good old batch scripts and have to say Goodbye to the idea of a nice fancy gui.
edit: Not so frustrated any more :-)

Re: Stop Program Execution of RunProgram("Robocopy",...)

Posted: Sat Dec 12, 2020 5:07 pm
by Axolotl
over all it seems to me that this code does the trick.
Still not sure whether the console app is terminated in a correct way.
And I have the same concerns on KillProgram(). (BTW KillProgram() works without #PB_Program_Wait)

Code: Select all

Import "Kernel32.lib"
  AttachConsole(dwProcessId) 
EndImport 

Procedure SendControlC(PID) 
  Protected rc 
  rc = FreeConsole_()                               :Debug "FreeConsole() "+rc 
  rc = AttachConsole(PID)                           :Debug "AttachConsole() "+rc                    ;// attach to process console
  rc = SetConsoleCtrlHandler_(#Null, #True)         :Debug "SetConsoleCtrlHandler() "+rc            ;// disable Control+C handling for our app
  rc = GenerateConsoleCtrlEvent_(#CTRL_C_EVENT, 0)  :Debug "GenerateConsoleCtrlEvent() "+rc         ;// generate Control+C event
; rc = SetConsoleCtrlHandler_(#Null, #False)        :Debug "SetConsoleCtrlHandler() "+rc            ;// disable Control+C handling for our app
EndProcedure 
There are so many ways to solve a problem. you just have to find the right one.
Thanks a lot for your support.

Stay healthy and Happy Coding!