End a program started with RunProgram?

Just starting out? Need help? Post your questions and find answers here.
BarryG
Addict
Addict
Posts: 3293
Joined: Thu Apr 18, 2019 8:17 am

End a program started with RunProgram?

Post by BarryG »

I have this old code from this forum, but it doesn't work anymore. How can I make it work? I don't want to use KillProgram() to end the program because it won't close down cleanly if you do that (such as Firefox, which gives a message about improper shutdown when you next restart it).

Code: Select all

Procedure EndProgram(processID)
  w=GetWindow_(GetDesktopWindow_(),#GW_CHILD)
  Repeat
    GetWindowThreadProcessId_(w,@pid)
    If pid=processID
      hWnd=w
      Break
    EndIf
    w=GetWindow_(w,#GW_HWNDNEXT)
  Until w=0
  Debug PostMessage_(hWnd,#WM_SYSCOMMAND,#SC_CLOSE,0) ; Returns 1 but Notepad is still open.
EndProcedure

app$="c:\windows\system32\notepad.exe"
handle=RunProgram(app$,"",GetPathPart(app$),#PB_Program_Open)
pid=ProgramID(handle)
Sleep_(2000) ; Give it time to open.
CloseProgram(handle)
EndProgram(pid) ; End started program.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: End a program started with RunProgram?

Post by mk-soft »

It is better to close the window via the ClassName, as the title can change. I have extended my include.

Code: Select all

;-TOP

; Comment : Window Functions
; Author  : mk-soft
; Version : v1.03.0
; Create  : ?
; Update  : 20.11.2021

Structure udtListWindows
  Name.s
  Class.s
  Handle.i
  Process.i
  Childs.i
  Level.i
EndStructure

Threaded NewList ListWindows.udtListWindows()

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    
    ;- Windows
    
    Procedure.s GetTitle(Handle)
      Protected Name.s
      Name.s = Space(1024)
      GetWindowText_(Handle, @Name, Len(Name))
      ProcedureReturn Left(Name, Len(Name))
    EndProcedure
    
    Procedure.s GetClassName(Handle.i)
      Protected Class.s
      Class.s = Space(1024)
      GetClassName_(Handle, @Class, Len(Class))
      ProcedureReturn Left(Class, Len(Class))
    EndProcedure

    Procedure EnumProc(Handle.i, lParam.i)
      Protected *tmp.udtListWindows
      AddElement(ListWindows())
     
      ListWindows()\Handle = Handle
      ListWindows()\Process = 0
      GetWindowThreadProcessId_(Handle, @ListWindows()\Process)
      ListWindows()\Name = GetTitle(Handle)
      ListWindows()\Class = GetClassName(Handle)
      
      If lParam
        *tmp = lParam
        *tmp\Childs + 1
        ListWindows()\Level = *tmp\Level + 1
      Else
        ListWindows()\Level = 0
      EndIf
      
      EnumChildWindows_(Handle, @EnumProc(), @ListWindows())
      
      ProcedureReturn #True
    EndProcedure
    
    Procedure GetAllWindows() ; Result = Count of Windows
      Protected r1, len
      
      ClearList(ListWindows())
      r1 = EnumWindows_(@EnumProc(), 0)
      ProcedureReturn ListSize(ListWindows())
    EndProcedure
    
    Procedure FindNamedWindow(Name.s) ; Result = Handle
      Protected cnt, len
      len = Len(Name)
      
      GetAllWindows()
      ForEach ListWindows()
        If FindString(ListWindows()\Class, "CabinetWClass")
          Continue
        EndIf
        If FindString(ListWindows()\Class, "ShellTabWindowClass")
          Continue
        EndIf
        If Left(ListWindows()\Name, len) = Name
          ProcedureReturn ListWindows()\Handle
        EndIf
      Next
      ProcedureReturn 0
    EndProcedure
    
    Procedure FindClassWindow(Name.s) ; Result = Handle
      Protected cnt, len
      len = Len(Name)
      
      GetAllWindows()
      ForEach ListWindows()
        If FindString(ListWindows()\Class, "CabinetWClass")
          Continue
        EndIf
        If FindString(ListWindows()\Class, "ShellTabWindowClass")
          Continue
        EndIf
        If Left(ListWindows()\Class, len) = Name
          ProcedureReturn ListWindows()\Handle
        EndIf
      Next
      ProcedureReturn 0
    EndProcedure
    
    Procedure CloseNamedWindow(Name.s) ; Result = Count of Windows
      Protected cnt, len
      len = Len(Name)
      GetAllWindows()
      ForEach ListWindows()
        If Left(ListWindows()\Name, len) = Name
          If FindString(ListWindows()\Class, "CabinetWClass")
            Continue
          EndIf
          If FindString(ListWindows()\Class, "ShellTabWindowClass")
            Continue
          EndIf
          SendMessage_(ListWindows()\Handle, #WM_CLOSE, 0, 0)
          cnt + 1
        EndIf
      Next
      ProcedureReturn cnt
    EndProcedure
    
    Procedure CloseClassWindow(Name.s) ; Result = Count of Windows
      Protected cnt, len
      len = Len(Name)
      GetAllWindows()
      ForEach ListWindows()
        If ListWindows()\Class = Name
          SendMessage_(ListWindows()\Handle, #WM_CLOSE, 0, 0)
          cnt + 1
        EndIf
      Next
      ProcedureReturn cnt
    EndProcedure
    

  CompilerCase #PB_OS_MacOS
    
    ;- MacOS
    Threaded __IsMainScope
    __IsMainScope = #True
    
    Procedure GetAllWindows() ; Result = Count of Windows
      Protected RunningApps.i, RunningAppsCount.i, RunningApp.i, AppName.s, i, Pool
      
      If Not __IsMainScope
        Pool = CocoaMessage(0, 0, "NSAutoreleasePool new")
      EndIf
      
      ClearList(ListWindows())
      RunningApps = CocoaMessage(0, CocoaMessage(0, 0, "NSWorkspace sharedWorkspace"), "runningApplications")
      RunningAppsCount = CocoaMessage(0, RunningApps, "count")
      i = 0
      While i < RunningAppsCount
        RunningApp = CocoaMessage(0, RunningApps, "objectAtIndex:", i)
        AppName.s = PeekS(CocoaMessage(0, CocoaMessage(0, RunningApp, "localizedName"), "UTF8String"), -1, #PB_UTF8)
        AddElement(ListWindows())
        ListWindows()\Name = AppName
        ListWindows()\Handle = RunningApp
        i + 1
      Wend
      
      If Pool
        CocoaMessage(0, Pool, "release")
      EndIf
      
      ProcedureReturn i
    EndProcedure
    
    Procedure FindNamedWindow(Name.s) ; Result = RunningApp
      Protected r1, RunningApps.i, RunningAppsCount.i, RunningApp.i, AppName.s, i, cnt, Pool
      
      If Not __IsMainScope
        Pool = CocoaMessage(0, 0, "NSAutoreleasePool new")
      EndIf
      
      RunningApps = CocoaMessage(0, CocoaMessage(0, 0, "NSWorkspace sharedWorkspace"), "runningApplications")
      RunningAppsCount = CocoaMessage(0, RunningApps, "count")
      i = 0
      While i < RunningAppsCount
        RunningApp = CocoaMessage(0, RunningApps, "objectAtIndex:", i)
        AppName.s = PeekS(CocoaMessage(0, CocoaMessage(0, RunningApp, "localizedName"), "UTF8String"), -1, #PB_UTF8)
        If Name = AppName
          r1 = RunningApp
          Break
        EndIf
        i + 1
      Wend
      
      If Pool
        CocoaMessage(0, Pool, "release")
      EndIf
      
      ProcedureReturn r1
    EndProcedure
    
    Procedure CloseNamedWindow(Name.s) ;  ; Result = Count of Windows
      Protected RunningApps.i, RunningAppsCount.i, RunningApp.i, AppName.s, i, cnt, Pool
      
      If Not __IsMainScope
        Pool = CocoaMessage(0, 0, "NSAutoreleasePool new")
      EndIf
      
      RunningApps = CocoaMessage(0, CocoaMessage(0, 0, "NSWorkspace sharedWorkspace"), "runningApplications")
      RunningAppsCount = CocoaMessage(0, RunningApps, "count")
      i = 0
      While i < RunningAppsCount
        RunningApp = CocoaMessage(0, RunningApps, "objectAtIndex:", i)
        AppName.s = PeekS(CocoaMessage(0, CocoaMessage(0, RunningApp, "localizedName"), "UTF8String"), -1, #PB_UTF8)
        If Name = AppName
          CocoaMessage(0, RunningApp, "terminate")
          cnt + 1
        EndIf
        i + 1
      Wend
      
      If Pool
        CocoaMessage(0, Pool, "release")
      EndIf
      
      ProcedureReturn cnt
    EndProcedure
      
  CompilerCase #PB_OS_Linux
    
    ;- Linux
    
    ;- Install Paket 'sudo apt-get install wmctrl'
    
    Procedure GetAllWindows()
      Protected Compiler, Output.s, Temp.s, pos
      
        ClearList(ListWindows())
        Compiler = RunProgram("wmctrl", "-l -x", "", #PB_Program_Open | #PB_Program_Read)
        Output = ""
        If Compiler
          While ProgramRunning(Compiler)
            If AvailableProgramOutput(Compiler)
              Output = ReadProgramString(Compiler)
              AddElement(ListWindows())
              temp = "$" + Mid(Output, 3, 8)
              ListWindows()\Handle = Val(temp)
              temp = Mid(Output, 15)
              pos = FindString(temp, "  ")
              ListWindows()\Class = Left(temp, pos)
              temp = Trim(Mid(temp, pos))
              pos = FindString(temp, " ")
              ListWindows()\Name = Mid(temp, pos + 1)
            Else
              Delay(10)
            EndIf
          Wend
          CloseProgram(Compiler) ; Close the connection to the program
        Else
          Output = "Error - Programm konnte nicht gestartet werden!"
        EndIf
        
    EndProcedure
    
    Procedure FindNamedWindow(Name.s)
      Protected len = Len(Name)
      GetAllWindows()
      ForEach ListWindows()
        If Left(ListWindows()\Name, len) = Name
          If Not FindString(ListWindows()\Class, "nautilus", 1, #PB_String_NoCase)
            ProcedureReturn ListWindows()\Handle
          EndIf
        EndIf
      Next
      ProcedureReturn 0
    EndProcedure
    
    Procedure CloseNamedWindow(Name.s)  ; Result = #True or #False
      RunProgram("wmctrl", "-a " + Name, "")
      Delay(100)
      RunProgram("wmctrl", "-c " + Name, "")
      Delay(100)
      ProcedureReturn #True
    EndProcedure
    
CompilerEndSelect

;- Example

CompilerIf #PB_Compiler_IsMainFile
  
  Enumeration Windows
    #Main
  EndEnumeration
  
  Enumeration Gadgets
    
  EndEnumeration
  
  Enumeration Status
    #MainStatusBar
  EndEnumeration
  
  Procedure Main()
    
    If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 400, 200, "Window" , #PB_Window_SystemMenu)
      
      GetAllWindows()
      
;       ForEach ListWindows()
;         If FindString(ListWindows()\Name, "Editor")
;           Debug ListWindows()\Name
;           Debug ListWindows()\Class
;         EndIf
;       Next
      
      r1 = FindClassWindow("Notepad")
      Debug "Notepad Handle = " + Hex(r1)
      Debug "Notepad Title  = " + GetTitle(r1)
      CloseClassWindow("Notepad")
      
      ;Debug GetTitle(r1)
      
      Repeat
        Select WaitWindowEvent()
          Case #PB_Event_CloseWindow
            Break
        EndSelect
      ForEver
      
    EndIf
    
  EndProcedure : Main()
  
CompilerEndIf
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: End a program started with RunProgram?

Post by mk-soft »

Another way ...

CloseWindowFromPID

Code: Select all

;-TOP by mk-soft

Threaded Handle_From_Process

Procedure EnumWindowsProcPID(hWnd, lParam)
  Protected lpdwProcessId
  GetWindowThreadProcessId_(hWnd, @lpdwProcessId)
  If lpdwProcessId = lParam
    Handle_From_Process = hWnd
    ProcedureReturn 0
  EndIf
  ProcedureReturn 1
EndProcedure

Procedure GetWindowFromPID(ProcessID)
  Protected r1
  Handle_From_Process = 0
  r1 = EnumWindows_(@EnumWindowsProcPID(), ProcessId);
  ProcedureReturn Handle_From_Process
EndProcedure

Procedure CloseWindowFromPID(ProcessID)
  Protected hWnd
  hWnd = GetWindowFromPID(ProcessID)
  If hWnd
    PostMessage_(hWnd, #WM_CLOSE, 0, 0)
  EndIf
EndProcedure

app$="c:\windows\system32\notepad.exe"
handle=RunProgram(app$,"",GetPathPart(app$),#PB_Program_Open)
pid=ProgramID(handle)
Delay(100) ; need some time to open notepad window
r1 = GetWindowFromPID(pid)
Debug r1
Sleep_(2000) ; Give it time to open.
CloseWindowFromPID(pid)

My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
BarryG
Addict
Addict
Posts: 3293
Joined: Thu Apr 18, 2019 8:17 am

Re: End a program started with RunProgram?

Post by BarryG »

Thanks mk-soft, I went for your second code because (a) it's much smaller, and (b) it uses RunProgram like I asked - I wasn't trying to close an existing window. Thanks for your help.
Post Reply