Launching and then reactivating another app from VB5

I need to launch another application from my VB5 app. No problem using Shell, but if the user switches back to the 1st app and then tries to launch the second app again, I need to see if it is running already, and if so restore it whether it is minimized or not. If it was closed, I know I can just use shell again. The code I need is how to determine whether it is running. FindWindow API won't do because the second app is an MDI and the Caption varies depending on the child windows caption. Thanks.
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

arnielsAuthor Commented:
Adjusted points to 200
When you fire the SHELL you get a return value. Before you fire it a second time try an App.Activate and see if you get an error #5 back. If the app is still hot, it'll come to the front. If it's been closed the error tells you to fire SHELL again.


arnielsAuthor Commented:
Mark2150 has come close. Yes, using AppActivate returns an error and I can then Shell again if the app is not running. If the app is running, it will then activate. But, if it is running and it is minimized, this method will not activate and restore the app as a normal window.
The Ultimate Tool Kit for Technolgy Solution Provi

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more!

here is what you do... you gotta assign your main form a unique class name... then you simply use FindWindow() by using the class parameter and not the caption, and if the handle returned is different than 0 then the app is running, otherwise it is not...

arnielsAuthor Commented:
Thanks, but the app I am shelling to may be someone elses and I cannot assign or may not know the Class Name.
App.Activate certainly *SHOULD* maximize the app! I use Adobe Acrobat as a child task and it enlarges just fine!

Let me get the code (hang on!)

... time passes ...

Ok, here is how I launch the app:

RetVal = Shell(Quote & Adobe_Path & Quote, vbMaximizedFocus)

This is sub that brings the app to the top and drives it:

Private Sub sendit(txt As String)
' Send string to child task
On Error Resume Next    'Ignore any errors that occur
AppActivate RetVal      'Make sure child task is active
DoEvents                'Give it a shot to run
SendKeys txt, True      'Tell it what to do and wait
DoEvents                'Cede some more time
End Sub

This is a sample code sequence I send:

Call log("Accessing Document: " & filename & " Page " & onpage, "I", vbBlue)
sendit ("%WA")                      'Close all windows
sendit ("%FO" & datadrive & RefPath & "\" & filename & "{enter}")
sendit ("%VG")                      ' Issue page select command
sendit (onpage & "{enter}")         ' Specify page

This code works like a top and every time a SENDIT is issued, the app comes to the top even if the operator has minimized it.


you can try the code below :

Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long

Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2

Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Const SW_RESTORE = 9

Dim HprocessProgram As Long
Dim HwndProgram As Long

Private Sub Command1_Click()

Dim lRes As Long

If HwndProgram = 0 Then 'not running --> shell
    HprocessProgram = Shell("myapp.exe", vbNormalFocus)
    HwndProgram = GetWindowHandle(HprocessProgram)
    lRes = ShowWindow(HwndProgram, SW_RESTORE) 'running --> show
    If lRes = 0 Then 'previously running, now, not runnnig --> shell
        HprocessProgram = Shell("myapp.exe", vbNormalFocus)
        HwndProgram = GetWindowHandle(HprocessProgram)
    End If
End If

End Sub
Private Function GetWindowHandleLow(ByVal hWndStart As Long, hProcess As Long)
Dim c_hWnd As Long
Dim hWndReturn As Long
Dim hProcessCur As Long

'Get first child
Dim c As Long
c_hWnd = GetWindow(hWndStart, GW_CHILD)

'loop through all children
Do Until c_hWnd = 0

    'Get the window's process handle
    GetWindowThreadProcessId c_hWnd, hProcessCur
    'if the process handle matches, return the window's handle
    If hProcessCur = hProcess Then
        GetWindowHandleLow = c_hWnd
        Exit Function
    End If
    'search all of the children of this window
    hWndReturn = GetWindowHandleLow(c_hWnd, hProcess)
    'return handle to caller if non-zero
    If hWndReturn <> 0 Then
        GetWindowHandleLow = hWndReturn
        Exit Function
    End If
    'no match get the next window
    c_hWnd = GetWindow(c_hWnd, GW_HWNDNEXT)


'return window handle not found
GetWindowHandleLow = 0

End Function

Private Function GetWindowHandle(hProcess As Long) As Long

'Recursively collect all of the window handless
'to search for the process handle (hprocess).
'return to caller (0 if not found)

GetWindowHandle = GetWindowHandleLow(GetDesktopWindow, hProcess)

End Function

arnielsAuthor Commented:
AppActivate doe not restore a minimized app. KHollAD's solution works fine if there is no splash screen or login screen preceeding the main mdi form, but it there is, it gets the hwnd of the login form which will not be opened when you want to reactivate the app.

I have tried the following code and it seems to work. If anyone has a better approach, let me know.

Public Sub RunApp(frm As Form, spath As String, sparam As String, stitle As String)

'where frm is the calling form, spath is the path and exe 'to run, sparam is is any command parameter, and stitle is 'the app caption to search

'(e.g. RunApp Me, "C:\folder\myapp.exe", "/Command", '"Parent Caption")

Dim hwndcurr As Long
Dim nlen As Integer
Dim wtitle As String
Dim x As Long
Dim apprunning As Boolean
Dim stitlelen As Integer

'get the length of the caption passed
'this should be just the parent part of the caption
stitlelen = Len(stitle)
apprunning = False

'add the parameter if not null
If sparam <> "" Then
   spath = spath & " " & sparam
End If

hwndcurr = GetWindow(frm.hwnd, GW_HWNDFIRST)
'loop through windows, getting caption text, to find target 'caption
Do While hwndcurr <> 0
   nlen = GetWindowTextLength(hwndcurr)
   wtitle = Space$(nlen + 1)
   nlen = GetWindowText(hwndcurr, wtitle, nlen + 1)
'if target title found then activate it. Since it may be 'an mdi app, just match on parent part of caption
   If UCase$(Left$(wtitle, stitlelen)) = stitle Then
      apprunning = True
      'restore in case it is minimized
      x = ShowWindow(hwndcurr, SW_RESTORE)
      AppActivate wtitle
      Exit Do
   End If
   hwndcurr = GetWindow(hwndcurr, GW_HWNDNEXT)

'if caption not found and thus app not running
'already, launch it
If apprunning = False Then
  x = Shell(spath, 1)
End If

End Sub
Or you can try this
Public bStillRunning as boolean
Public Const STILL_ACTIVE = &H103
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (hObject As Long) As Boolean

Dim hProcess as long
Dim lRetVal as long

hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, Shell("YourExe.exe")
        GetExitCodeProcess hProcess, lRetVal
        If lRetVal <> STILL_ACTIVE Then
              bStillRunning = false
               Exit Do
              bStillRunning = true
        end if
    Call CloseHandle(hProcess)
end sub

Check the variable bStillRunning and you may know you launch another application or not.
 I think Torus answer will do, if doesn't you can check if the EXE is running using PSAPI.DLL or sort of, that has some API functions that let you manage processes.
You should be able to check for a running application by looking for it's windows by class name using the FindWindow API call - the fist argument specifies the class name.

(You will need to determine the window class name "manually" by launch the app., looking for it by caption and then retrieving it's class name using the GetClassName API first time...)

If you get a window handle back, you can activate it, otherwise you should launch the application for the first time.


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.