1

I was wondering about how to run external applications using Visual Basic 6.0. Running the application is not the problem, my problem is that I want to run the external application in a Modal way, that is, my application is inactive while the external application is running.

Option Explicit

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Const SYNCHRONIZE = &H100000
Private Const INFINITE = &HFFFF
Private Const WAIT_OBJECT_0 = 0
Private Const WAIT_TIMEOUT = &H102

Private Sub Form_click()
Dim llngProcID As Long
Dim llngWinHwnd As Long
Dim llngRetVal As Long

llngProcID = Shell("c:\windows\system32\notepad.exe", vbNormalFocus)

If llngProcID <> 0 Then

llngWinHwnd = OpenProcess(SYNCHRONIZE, 0, llngProcID)
DoEvents
If llngWinHwnd <> 0 Then
llngRetVal = WaitForSingleObject(llngWinHwnd, INFINITE)
CloseHandle (llngWinHwnd)
End If
End If
MsgBox "Finished"
End Sub

This code solves me, but the way in which it blocks the vb6 application leaving it in "Not Responding" is not very pleasant, if anyone had any other way to do this, I would appreciate it.

  • Possibly a duplicate of this? https://stackoverflow.com/questions/5685972/how-to-wait-for-a-shell-process-to-finish-before-executing-further-code-in-vb6 – User51 Jan 07 '22 at 18:50

1 Answers1

3

I've not done it for years, but looking at my old code I have:

Option Explicit
Public Const PROCESS_QUERY_INFORMATION = &H400
Public Const STATUS_PENDING = &H103&

Declare Function OpenProcess Lib "kernel32" _
    (ByVal dwDesiredAccess As Long, _
     ByVal bInheritHandle As Long, _
     ByVal dwProcessId As Long) As Long

Declare Function GetExitCodeProcess Lib "kernel32" _
    (ByVal hProcess As Long, lpExitCode As Long) As Long

Declare Function CloseHandle Lib "kernel32" _
    (ByVal hObject As Long) As Long

Public Sub RunShell(cmdline As String, windowstyle As Integer)
  Dim lHprocess As Long
  Dim lProcessId As Long
  Dim lExitCode As Long
    
  lProcessId = Shell(cmdline, windowstyle)
  lHprocess = OpenProcess(PROCESS_QUERY_INFORMATION, False, lProcessId)
  Do
    Call GetExitCodeProcess(lHprocess, lExitCode)
    DoEvents
  Loop While lExitCode = STATUS_PENDING
  Call CloseHandle(lHprocess)
End Sub
John Eason
  • 436
  • 2
  • 4
  • Points to be aware of with this particular solution - having the DoEvents in the loop means that Windows will not consider the app as non-responsive, but it also means that the external app isn't completely being handled "modal". Any enabled UI elements on this parent app (eg, buttons, fields, menus, links, etc) will be responsive to clicks and keystrokes. Second, while the loop is being performed waiting for the external app to exit, the cpu will likely be pegged, at least the one core this thread is running on. Consider adding a short sleep call (eg 50ms) in the loop to avoid this. – MarkL Dec 09 '21 at 19:03
  • @MarkL is correct in that the DoEvents loop will make the launched app no longer modal. A relatively easy way to keep the DoEvents statement but prevent any interaction with the VB6 app is just set FormX.Enabled =False. The sleep call is a good idea too, although it can be much shorter than 50ms and still reduce the CPU usage to near-nil - I generally use 10 ms; even a 1ms sleep seems to reduce the CPU usage to almost nothing. – Mark Moulding Dec 10 '21 at 08:31