Probleme mit RunProgram

Anfängerfragen zum Programmieren mit PureBasic.
bin_neu_hier
Beiträge: 105
Registriert: 06.03.2019 21:52

Probleme mit RunProgram

Beitrag von bin_neu_hier »

Hallo Allerseits!

habe mit folgendem Programmaufruf

Code: Alles auswählen

err = RunProgram(progpath$, string$, workdir$, #PB_Program_Wait)
das Problem, dass - wenn das aufgerufene Programm zu lange geöffnet bleibt - das aufrufende Programm "Keine Rückmeldung" meldet und das "Schließen"-X verändert sich (größer, rot, halt anders). Wenn es zu lange dauert, stürzt das aufrufende Prog ab.

Rumgegoogelt habe ich den Tipp gefunden

Code: Alles auswählen

while windowevent()
delay(1)
wend
verhindere das Problem - bei mir leider nicht. Irgendwo habe ich die Empfehlung gelesen, man müsse das RunProgram in einem eigenen Thread starten. Falls das eine gute Idee wäre, wie macht man das?

Gibt es eine "elegante" Lösung, mit der das Problem angegangen werden kann?
Bin mit 21 erstmals mit Computern in Kontakt gekommen und konnte mich daher in meiner Jugend ganz auf den Alkohol konzentrieren. Bin nun seit fast 40 Jahren programmiertechnisch konstant auf Anfänger-Level, konnte jedoch beim Thema Alkohol eine gewisse Virtuosität erreichen.
Irgendwas muss man ja gut können.
bin_neu_hier
Beiträge: 105
Registriert: 06.03.2019 21:52

Re: Probleme mit RunProgram

Beitrag von bin_neu_hier »

Hallo!

habe das jetzt mal mit einem Thread probiert

Code: Alles auswählen

 Structure RunProgX
    prog$
    param$
    workdir$
  EndStructure
  
  Procedure RunProgXThread(*Parameters.RunProgX)
    
    RunProgram(*Parameters\prog$, *Parameters\param$, *Parameters\workdir$)
    ClearStructure(*Parameters, RunProgX)
    FreeMemory(*Parameters)
    
  EndProcedure
  
  parentwindow = OpenWindow(#PB_Any,0,0,300,300,titel$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  bg = ButtonGadget(#PB_Any,10,10,100,30,"notepad")
  
  Repeat
    event = WaitWindowEvent()
    etype = EventType()
    egadget = EventGadget()
    
    Select event
      Case #PB_Event_CloseWindow
        End
        
      Case #PB_Event_Gadget
        
        Select egadget
          Case bg
            *Parameters.RunProgX = AllocateMemory(SizeOf(RunProgX))
            *Parameters\prog$ = "notepad.exe"
            *Parameters\param$   = ""
            *Parameters\workdir$ = "c:\"
            WaitThread(CreateThread(@RunProgXThread(), *Parameters))
            
        EndSelect
    EndSelect
  ForEver
  End
  
Leider hat das Waitthread überhaupt keine Auswirkung! Was muss man tun, dass das Prog so lange wartet, bis dieser Thread beendet ist?
Bin mit 21 erstmals mit Computern in Kontakt gekommen und konnte mich daher in meiner Jugend ganz auf den Alkohol konzentrieren. Bin nun seit fast 40 Jahren programmiertechnisch konstant auf Anfänger-Level, konnte jedoch beim Thema Alkohol eine gewisse Virtuosität erreichen.
Irgendwas muss man ja gut können.
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: Probleme mit RunProgram

Beitrag von Bisonte »

Was genau willst du denn machen ?

Mit der jetzigen "Thread" Methode hast du kein Stück gewonnen... Das ist wie dein erster Versuch ohne Thread.

Wenn du warten willst, musst du das ganze per Eventsteuerung erledigen, damit dein Fenster noch "aktiv" bleibt.

Dazu müsstest du ein Event an das Fenster schicken, das der Thread fertig ist.
Und in der Eventschleife dieses Event auswerten.
Dazu solltest du... nachdem der Button gedrückt wurde... auch dafür sorgen, das der Button nicht nochmal gedrückt werden kann.

Sozusagen läuft dein Programm weiter, aber es wird nur auf das Event gewartet, das der Thread fertig ist.

P.S.: Bei RunProgram() hast du den Parameter #PB_Program_Wait vergessen....

Code: Alles auswählen

Structure RunProgX
  Window.i
  prog$
  param$
  workdir$
EndStructure

Enumeration EnumEvent #PB_Event_FirstCustomValue
  #Thread_Fertig
EndEnumeration

Procedure RunProgXThread(*Parameters.RunProgX)
  
  Debug "Thread Gestartet"
  RunProgram(*Parameters\prog$, *Parameters\param$, *Parameters\workdir$, #PB_Program_Wait)
  
  Debug "Event gesendet"
  PostEvent(#Thread_Fertig, *Parameters\Window, 0)
  
EndProcedure

Define *Parameters.RunProgX

parentwindow = OpenWindow(#PB_Any,0,0,300,300,titel$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
bg = ButtonGadget(#PB_Any,10,10,100,30,"notepad")

Repeat
  
  event = WaitWindowEvent()
  etype = EventType()
  egadget = EventGadget()
  
  Select event
    Case #PB_Event_CloseWindow
      End
      
    Case #Thread_Fertig
      ClearStructure(*Parameters, RunProgX)
      FreeMemory(*Parameters)
      DisableGadget(bg, #False)
      Debug "Fertig"
      
    Case #PB_Event_Gadget
      
      Select egadget
        Case bg
          DisableGadget(bg, #True)
          *Parameters.RunProgX = AllocateMemory(SizeOf(RunProgX))
          *Parameters\prog$ = "notepad.exe"
          *Parameters\param$   = ""
          *Parameters\workdir$ = "c:\"
          *Parameters\Window   = parentwindow
          CreateThread(@RunProgXThread(), *Parameters)
          
      EndSelect
  EndSelect
ForEver
End
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Probleme mit RunProgram

Beitrag von mk-soft »

Hatte ich schon fertig ... Mal schauen ob wieder erst nach drei Tagen wieder rein geschaut wird

Code: Alles auswählen

;-TOP

;- Begin Mini Thread Control

;  by mk-soft, Version 1.06, 20.07.2019

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler-Option ThreadSafe!"
CompilerEndIf

Structure udtThreadControl
  ThreadID.i
  UserID.i
  Signal.i
  Pause.i
  Exit.i
EndStructure

Procedure StartThread(*Data.udtThreadControl, *Procedure) ; ThreadID
  If Not IsThread(*Data\ThreadID)
    *Data\ThreadID = CreateThread(*Procedure, *Data)
  EndIf
  ProcedureReturn *Data\ThreadID
EndProcedure

Procedure StopThread(*Data.udtThreadControl, Wait = 1000) ; Void
  If IsThread(*Data\ThreadID)
    *Data\Exit = #True
    If *Data\Pause
      *Data\Pause = #False
      SignalSemaphore(*Data\Signal)
    EndIf
    If WaitThread(*Data\ThreadID, Wait) = 0
      KillThread(*Data\ThreadID)
    EndIf
    *Data\ThreadID = 0
    *Data\Pause = #False
    *Data\Exit = #False
  EndIf
  If *Data\Signal
    FreeSemaphore(*Data\Signal)
    *Data\Signal = 0
  EndIf
EndProcedure

Procedure FreeThread(*Data.udtThreadControl, Stop = #True, Wait = 1000) ; True or False
  If IsThread(*Data\ThreadID)
    If Stop
      StopThread(*Data, Wait)
      FreeStructure(*Data)
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  Else
    If *Data\Signal
      FreeSemaphore(*Data\Signal)
    EndIf
    FreeStructure(*Data)
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure ThreadPause(*Data.udtThreadControl) ; Void
  If IsThread(*Data\ThreadID)
    If Not *Data\Signal
      *Data\Signal = CreateSemaphore()
    EndIf
    If Not *Data\Pause
      *Data\Pause = #True
    EndIf
  EndIf
EndProcedure

Procedure ThreadResume(*Data.udtThreadControl) ; Void
  If IsThread(*Data\ThreadID)
    If *Data\Pause
      *Data\Pause = #False
      SignalSemaphore(*Data\Signal)
    EndIf
  EndIf
EndProcedure

;- End Mini Thread Control

; ****

#Example = 2

;- Example 1

CompilerIf #Example = 1
  
  Enumeration #PB_Event_FirstCustomValue
    #MyEvent_ThreadFinished
  EndEnumeration
  
  Enumeration Gadget
    #ButtonStart1
    #ButtonStart2
    #ButtonPauseResume1
    #ButtonPauseResume2
    #ButtonStop1
    #ButtonStop2
  EndEnumeration
  
  Structure udtFileData
    Name.s
    Result.s
  EndStructure
  
  ; Extends always own data structure with structure from thread control
  Structure udtThreadData Extends udtThreadControl
    ; Data
    Window.i
    Event.i
    List Files.udtFileData()
  EndStructure
  
  Procedure MyThread(*Data.udtThreadData)
    Protected c
    
    With *Data
      Debug "Init Thread " + \UserID
      ;TODO
      Delay(500)
      Debug "Start Thread " + \UserID
      ;TODO
      ForEach \Files()
        ; 1. Query on thread pause
        If \Pause
          Debug "Pause Thread " + \UserID
          WaitSemaphore(\Signal)
          Debug "Resume Thread " + \UserID
        EndIf
        ; 2. Query on thread cancel
        If \Exit
          Break
        EndIf
        ;TODO
        Debug "Busy Thread " + \UserID + ": File " + \Files()\Name
        Delay(500)
        \Files()\Result = "Ready."
      Next
      If \Exit
        ;TODO Thread Cancel
        Debug "Cancel Thread " + \UserID
      Else
        ;TODO Thread Finished
        Debug "Shutdown Thread " + \UserID
        PostEvent(\Event, \Window, 0, 0, *Data) ; <- EventData = Pointer to ThreadData
      EndIf
      Debug "Exit Thread " + \UserID
      ; 3. Clear ThreadID 
      \ThreadID = 0
    EndWith
  EndProcedure
  
  ; Create Data always with AllocateStructure  
  Global *th1.udtThreadData = AllocateStructure(udtThreadData)
  *th1\UserID = 1
  *th1\Window = 1
  *th1\Event = #MyEvent_ThreadFinished
  For i = 10 To 30
    AddElement(*th1\Files())
    *th1\Files()\Name = "Data_" + i
  Next
  
  Global *th2.udtThreadData = AllocateStructure(udtThreadData)
  *th2\UserID = 2
  *th2\Window = 1
  *th2\Event = #MyEvent_ThreadFinished
  For i = 31 To 60
    AddElement(*th2\Files())
    *th2\Files()\Name = "Data_" + i
  Next
  
  ; Output Data
  Procedure Output(*Data.udtThreadData)
    Debug "Thread Finished UserID " + *Data\UserID
    MessageRequester("Thread Message", "Thread Finished UserID " + *Data\UserID)
    ForEach *Data\Files()
      Debug *Data\Files()\Name + " - Result " + *Data\Files()\Result
    Next
  EndProcedure
  
  If OpenWindow(1, 0, 0, 222, 250, "Mini Thread Control", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ButtonGadget(#ButtonStart1, 10, 10, 200, 30, "Start 1")
    ButtonGadget(#ButtonStart2, 10, 50, 200, 30, "Start 2")
    ButtonGadget(#ButtonPauseResume1, 10, 90, 200, 30, "Pause 1")
    ButtonGadget(#ButtonPauseResume2, 10, 130, 200, 30, "Pause 2")
    ButtonGadget(#ButtonStop1, 10, 170, 200, 30, "Stop 1")
    ButtonGadget(#ButtonStop2, 10, 210, 200, 30, "Stop 2")
    Repeat
      Select WaitWindowEvent() 
        Case #PB_Event_CloseWindow
          FreeThread(*th1)
          FreeThread(*th2)
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #ButtonStart1
              StartThread(*th1, @MyThread())
            Case #ButtonStart2
              StartThread(*th2, @MyThread())
            Case #ButtonPauseResume1
              If IsThread(*th1\ThreadID)
                If Not *th1\Pause
                  ThreadPause(*th1)
                  SetGadgetText(#ButtonPauseResume1, "Resume 1")
                Else
                  ThreadResume(*th1)
                  SetGadgetText(#ButtonPauseResume1, "Pause 1")
                EndIf
              EndIf
            Case #ButtonPauseResume2
              If IsThread(*th2\ThreadID)
                If Not *th2\Pause
                  ThreadPause(*th2)
                  SetGadgetText(#ButtonPauseResume2, "Resume 2")
                Else
                  ThreadResume(*th2)
                  SetGadgetText(#ButtonPauseResume2, "Pause 2")
                EndIf
              EndIf
            Case #ButtonStop1
              StopThread(*th1)
              SetGadgetText(#ButtonPauseResume1, "Pause 1")
            Case #ButtonStop2
              StopThread(*th2)
              SetGadgetText(#ButtonPauseResume2, "Pause 2")
          EndSelect
          
        Case #MyEvent_ThreadFinished
          Output(EventData())
          
      EndSelect
    ForEver
  EndIf
  
CompilerEndIf

;- Example 2

CompilerIf #Example = 2 
  
  Enumeration #PB_Event_FirstCustomValue
    #MyEvent_ThreadFinished
  EndEnumeration
  
  Enumeration Gadget
    #ButtonStart1
    #ButtonPauseResume1
    #ButtonStop1
  EndEnumeration
  
  ; Extends always own data structure with structure from thread control
  Structure udtThreadData Extends udtThreadControl
    ; Data
    Window.i
    Event.i
    Output.s
  EndStructure
  
  Procedure MyThread(*Data.udtThreadData)
    Protected Compiler, Output.s
    
    With *Data
      Debug "Init Thread " + \UserID
      ;- Begin Thread init
      Compiler = RunProgram(#PB_Compiler_Home+"/Compilers/pbcompiler", "-h", "", #PB_Program_Open | #PB_Program_Read)
      Output = ""
      ;- End Thread init
      Debug "Start Thread " + \UserID
      ;- Begin Thread loop
      If Compiler
        While ProgramRunning(Compiler)
          ; 1. Query on thread pause
          If \Pause
            Debug "Pause Thread " + \UserID
            WaitSemaphore(\Signal)
            Debug "Resume Thread " + \UserID
          EndIf
          ; 2. Query on thread cancel
          If \Exit
            Break
          EndIf
          ; Input
          If AvailableProgramOutput(Compiler)
            Output + ReadProgramString(Compiler) + Chr(13)
          Else
            Delay(10)
          EndIf
        Wend
        Output + Chr(13) + Chr(13)
        Output + "Exitcode: " + Str(ProgramExitCode(Compiler))
        CloseProgram(Compiler) ; Close the connection to the program
      Else
        Output = "Error - Programm konnte nicht gestartet werden!"
      EndIf
      ;- End Thread lopp
      If \Exit
        ;- TODO Thread Cancel
        Debug "Cancel Thread " + \UserID
        \Output = "Error - Thread vom user abgebrochen!"
      Else
        ;- TODO Thread Finished
        Debug "Shutdown Thread " + \UserID
        \Output = Output
        PostEvent(\Event, \Window, 0, 0, *Data) ; <- EventData = Pointer to ThreadData
      EndIf
      Debug "Exit Thread " + \UserID
      ; 3. Clear ThreadID 
      \ThreadID = 0
    EndWith
  EndProcedure
  
  ; Create Data always with AllocateStructure  
  Global *th1.udtThreadData = AllocateStructure(udtThreadData)
  *th1\UserID = 1
  *th1\Window = 1
  *th1\Event = #MyEvent_ThreadFinished
  
  ; Output Data
  Procedure Output(*Data.udtThreadData)
    Debug "Thread Finished UserID " + *Data\UserID
    MessageRequester("Thread Message", "Thread Finished " + #LF$ + #LF$ + *Data\Output)
  EndProcedure
  
  If OpenWindow(1, 0, 0, 222, 250, "Mini Thread Control", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ButtonGadget(#ButtonStart1, 10, 10, 200, 30, "Start 1")
    ButtonGadget(#ButtonPauseResume1, 10, 90, 200, 30, "Pause 1")
    ButtonGadget(#ButtonStop1, 10, 170, 200, 30, "Stop 1")
    Repeat
      Select WaitWindowEvent() 
        Case #PB_Event_CloseWindow
          FreeThread(*th1)
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #ButtonStart1
              StartThread(*th1, @MyThread())
            Case #ButtonPauseResume1
              If IsThread(*th1\ThreadID)
                If Not *th1\Pause
                  ThreadPause(*th1)
                  SetGadgetText(#ButtonPauseResume1, "Resume 1")
                Else
                  ThreadResume(*th1)
                  SetGadgetText(#ButtonPauseResume1, "Pause 1")
                EndIf
              EndIf
            Case #ButtonStop1
              StopThread(*th1)
              SetGadgetText(#ButtonPauseResume1, "Pause 1")
          EndSelect
          
        Case #MyEvent_ThreadFinished
          Output(EventData())
          
      EndSelect
    ForEver
  EndIf
  
CompilerEndIf
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
bin_neu_hier
Beiträge: 105
Registriert: 06.03.2019 21:52

Re: Probleme mit RunProgram

Beitrag von bin_neu_hier »

Wahnsinn!

Die Antworten haben mich umgehauen, ...

... weil so schnell und umfassend und professionell und und und ...
... weil's ziemlich starker Tobag für mich ist und das nachträgliche Einbauen in ein für meine Verhältnisse recht umfangreiches Projekt eine ganze Weile dauern wird ...

... weil einfach klasse!

Bestätigt sich immer wieder: PureBasic war die richtige Wahl - allein schon wegen diesem Forum hier! Herzlichen Dank!
Bin mit 21 erstmals mit Computern in Kontakt gekommen und konnte mich daher in meiner Jugend ganz auf den Alkohol konzentrieren. Bin nun seit fast 40 Jahren programmiertechnisch konstant auf Anfänger-Level, konnte jedoch beim Thema Alkohol eine gewisse Virtuosität erreichen.
Irgendwas muss man ja gut können.
Antworten