Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Waiting for Another Application to Finish (not on startup). 2

Status
Not open for further replies.

TopJack

Programmer
Mar 10, 2001
153
GB
Hi, can you help,

I am trying to write code that can "Sendkeys" to another application for updates (not an ideal scenario, I know). I have mastered this successfully, but with fixed time delays between each update. This works well but can't cater for a slow network. The problem I have is that how do I know when the other application has finished processing each update.

Searching through this forum, I can find many examples of demonstrating the "WaitForSingleObject" API call. But they all seem to use a "Shell" startup reference. My other application will not be opened by me directly. All applications will be Windows based on a NT platform. Using my limited VB and API knowledge, I wrote this code ......

******************************************************
Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long)As Long
Const WAIT_FAILED = &HFFFFFFFF
Const WAIT_OBJECT_0 = &H0
Const WAIT_TIMEOUT = &H102

Sub try_me()
Dim hWnd As Long 'receives handle to window.
Dim retval As String 'generic return value.

hWnd = FindWindow(vbNullString, "Microsoft Excel - Book1")

If hWnd = 0 Then 'window not found.
MsgBox ("Target Window Not Open")
Else
retval = WaitForSingleObject(hWnd, 2000)
Select Case retval
Case WAIT_OBJECT_0: 'The process terminated.
MsgBox ("process terminated")
Case WAIT_TIMEOUT: 'The process did not terminate.
MsgBox ("timeout")
Case WAIT_FAILED: 'Bad call to function.
MsgBox ("failed")
End Select
End If
End Sub
******************************************************

Yes, I know its crude, but, I get a WAIT_FAILED return every time its run. I suspect its a misuse of the API calls.
Am I miles of the mark or barking up the wrong tree ? Any help is much appreciated.

Thanks, TopJack.
 
You are using the wrong handle . . . you can't use the Window handle for the WaitForSingleObject, you need the Process Handle. To get this, you need to search through the process list until you find the application that you want to wait on. Then, using THAT handle, call WaitForSingleObject.
Let me look around . . . I know that I have the API code to search the Process List somewhere . . . if I can find that code, I'll post it in here. But in the meantime, remember . . . you need the Process Handle, not the Window Handle. - Jeff Marler
(please note, that the page is under construction)
 
Thanks Jeff, for the pointer.

If you have that code, that would save me alot of time.

Cheers, TopJack.
 
OK, here is the code that you need along with a brief description of what is going on . . . ENJOY!


In a BAS Module's General Declarations - Declare your APIs


Public Const MAX_PATH As Integer = 260

'** OpenHandle Constants.
Public Const PROCESS_QUERY_INFORMATION = 1024
Public Const PROCESS_VM_READ = 16
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
Public Const SYNCHRONIZE = &H100000

'** WaitForSingleObject Return Values.
Public Const WAIT_FAILED = &HFFFFFFFF
Public Const WAIT_OBJECT_0 = &H0
Public Const WAIT_TIMEOUT = &H102

'** API declarations.
Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long

Public Declare Sub CloseHandle Lib "kernel32.dll" (ByVal hPass As Long)

Public Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long

Public Declare Function EnumProcesses Lib "psapi.dll" (ByRef lpidProcess As Long, ByVal cb As Long, ByRef cbNeeded As Long) As Long

Public Declare Function GetModuleBaseNameA Lib "psapi.dll" (ByVal lngProcessHandle As Long, ByVal lngModuleHandle As Long, ByVal strModuleName As String, ByVal lngSize As Long) As Long

Public Declare Function EnumProcessModules Lib "psapi.dll" (ByVal hProcess As Long, ByRef lphModule As Long, ByVal cb As Long, ByRef cbNeeded As Long) As Long



Next, define the actual function that finds the runnings application's process ID

Private Function GetProcessHandle(strName As String) As Long


'This Function accepts the name of the EXE (i.e. SomeProgram.exe) and scans
'the process list for it. When it is found, the Process ID is returned. If
'it is not found, then the function returns 0.

Dim lngProcessID() As Long
Dim lngCB As Long
Dim lngCBNeeded As Long
Dim lngRetCode As Long
Dim lngLoop As Long
Dim lngProcessHandle As Long
Dim strModuleName As String
Dim strTemp() As String
Dim lngModules(1 To 200) As Long
Dim lngCbNeeded2 As Long
Dim intTemp As Integer

'** Get the current list of running Processes IDs
'******************************************************************************
lngCB = 8
lngCBNeeded = 96
Do While lngCB <= lngCBNeeded
lngCB = lngCB * 2
ReDim lngProcessIDs(lngCB / 4) As Long
lngRetCode = EnumProcesses(lngProcessIDs(1), lngCB, lngCBNeeded)

If lngRetCode = 0 Then
MsgBox Err.LastDllError
Exit Do
End If
Loop
'******************************************************************************


'** Get the name of the Base Module in each Process Space.
'** NOTE : If you wanted to, you could also use the GetModuleBaseNameA API
'** to get every DLL and OCX loaded in the process space.
'******************************************************************************

For lngLoop = 1 To lngCBNeeded / 4
'Get a handle to the Process
lngProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION _
Or PROCESS_VM_READ, 0, lngProcessIDs(lngLoop))
'Got a Process handle
If lngProcessHandle <> 0 Then
'Get an array of the module handles for the specified
'process
lngRetCode = EnumProcessModules(lngProcessHandle, lngModules(1), 200, lngCbNeeded2)
'If the Module Array is retrieved, Get the ModuleFileName
If lngRetCode <> 0 Then
strModuleName = Space(MAX_PATH)
lngRetCode = GetModuleBaseNameA(lngProcessHandle, lngModules(1), strModuleName, 500)
ReDim Preserve strTemp(intTemp)
strTemp(intTemp) = Trim$(strModuleName)


'** Check to see if the current process is the target process.
If UCase$(Trim$(strModuleName)) = UCase$(Trim$(strName & Chr$(0))) Then
GetProcessHandle = lngProcessIDs(lngLoop)
Exit For
End If


intTemp = intTemp + 1
End If
End If
Next lngLoop
'******************************************************************************



End Function

Finally, define the code that performs the actual wait.
NOTE : Notice that we can't simply use the Process ID that was returned by GetProcessHandle . . . This
is because we do not have the proper rights to it. We can get a handle for the desired process with the proper rights
by calling OpenProcess and using the SYNCHRONIZE constant. It is with this process that we call WaitForSingleObject.


Private Sub Command1_Click()

Dim lngProcessHandle As Long
Dim lngRetCode As Long

'** Finr the EXE's process ID.
lngProcessHandle = GetProcessHandle(txtExeName.Text)

If lngProcessHandle <> 0 Then
'** Get a usable handle to the EXE.
lngProcessHandle = OpenProcess(SYNCHRONIZE, False, lngProcessHandle)
Else
MsgBox txtExeName.Text & &quot; not found!&quot;
Exit Sub
End If


If lngProcessHandle <> 0 Then


lngRetCode = WaitForSingleObject(lngProcessHandle, 30000)


Caption = &quot;Waiting . . .&quot;

Select Case lngRetCode

Case WAIT_TIMEOUT

Caption = &quot;Timed Out&quot;

Case WAIT_FAILED

Caption = &quot;Failed&quot;

MsgBox Err.LastDllError

Case WAIT_OBJECT_0


Caption = &quot;Done!&quot;

Case Else

Caption = &quot;Unknown:= &quot; & lngRetCode

End Select

'** Close the handle once we are done with it.
Call CloseHandle(lngProcessHandle)

End If

End Sub



Final Note
This code will cause your application to search the process list for a running EXE that you have specified. If that EXE is running, then it will wait until that EXE is closed or until the WaitForSingleObject API call times out. Please note however, that these APIs are for WINNT 4.0 and up . . . there is a different set of APIs for WIN9x machines and if your final application needs to run on both platforms, then you will need to check for the OS and run time and use the corresponding WIN9x APIS to perform the same function (searching the process list and generating a usable process handle). One more note . . . this code stops at the first instance of the application that it finds . . . in other words, if you had 2 different versions of the target application running, then you may get stuck waiting on the wrong one. If you expect to see more than one version of the target EXE at any given time, then you will need to perform some more checks to make sure that you do indeed have the right process.
There . . . I know that this post was a bit long, but I do hope that it helps. I will be posting some more indepth information on this along with some more ADVANCED options on my WebSite in the near future. The site is still under construction, but the first release of it is almost ready. s-) - Jeff Marler
(please note, that the page is under construction)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top