need to wait for app

G

Gary Roach

i'm invoking an application (photo editor) using "shell" from vba under
access and i need to wait for it to come up before i send keystrokes to it
(i'm sending "%wt" to tile the pictures). if i don't wait, the keystrokes
get sent to access. right now i'm using a sleep (from kernel32) of 1500
milliseconds but this seems unreliable. is there a way to find out if an app
is up and has the focus and it ready to take keystrokes? any help is greatly
appreciated.

gary
 
J

Jonathan West

Hi Gary,

Go to www.mvps.org/vb/, click on the Samples link on the left, and scroll
down to the Shell32.zip section. From there you can download a code sample
that does a Shell&Wait function, in other words does not continue until the
shelled application is closed.
 
G

Gary Roach

i've already got the shell&wait functionality. but in this case i don't want
to wait until the app closes. i want to wait until it fully opens so that i
can send keystrokes to it. maybe the answer is to use a win32 api like
sendmessage instead of vba's sendkeys.
 
K

Karl E. Peterson

Hi Gary --

(Crossed to the API group, to solicit feedback on VB's internal operation --
see bottom of post for the question this raises in my mind.)
i've already got the shell&wait functionality. but in this case i don't want
to wait until the app closes. i want to wait until it fully opens so that i
can send keystrokes to it. maybe the answer is to use a win32 api like
sendmessage instead of vba's sendkeys.

Usually, VB will actually be blocked until the application is responding. You can
test this with any moderately slow-to-start app:

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command2_Click()
Shell ExeFile, vbNormalFocus
AppActivate Me.Caption
MsgBox "Are we there yet?"
End Sub

I would suppose there may be times when that doesn't happen quite as planned. To be
sure, you can use WaitForInputIdle, which is just a bit more complicated but not too
bad. Here's a simple case, using a hard-coded executable path (indented to highlight
wordwrap!):

Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA"
(ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal
lpProcessAttributes As Any, ByVal lpThreadAttributes As Any, ByVal bInheritHandles As
Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Any, ByVal
lpCurrentDriectory As Any, lpStartupInfo As STARTUPINFO, lpProcessInformation As
PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long,
ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
MsgBox "Application is now waiting for input!"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode As
VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&, vbNullString,
StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function

I can't be sure, but this little test leads me to think that perhaps VB is actually
inserting the call to WaitForInputIdle itself, following its own call to
CreateProcess. Anyone else have a conjecture on that?

Thanks... Karl
 
G

Gary Roach

i could get neither the appactivate nor the waitforinput idle methods to
work. the waitforinput idle returned with return value 0 but when i called
sendkeys in response to that the keys got sent to the calling app (in my
case the vba editor). i used word as the app to launch as i don't have corel
draw. here's the modified code i used:

Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As
Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office10\WINWORD.EXE"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
SendKeys "A"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode As
VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function
 
K

Karl E. Peterson

Hi Gary --
i could get neither the appactivate nor the waitforinput idle methods to
work. the waitforinput idle returned with return value 0 but when i called
sendkeys in response to that the keys got sent to the calling app (in my
case the vba editor). i used word as the app to launch as i don't have corel
draw. here's the modified code i used:

Hmmmm, that worked here with only two modifications. First, since this was
originally posted in a VBA group, I brought it into Excel and changed

Private Sub Command1_Click()
to
Public Sub TestShell()

Then, when I ran it, I saw that CreateProcess was failing with a LastDllError of 2
(The system cannot find the file specified.), so I looked at the ExeFile constant,
and noted the path here to Word is just slightly different:

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office\WINWORD.EXE"

Note the missing "10" in the last folder name. Could that be the issue, there?

Later... Karl
 
G

Gary Roach

I get the word app to come up no problem, but the keystrokes still go to the
VBA source window instead of to word.

Karl E. Peterson said:
Hi Gary --
i could get neither the appactivate nor the waitforinput idle methods to
work. the waitforinput idle returned with return value 0 but when i called
sendkeys in response to that the keys got sent to the calling app (in my
case the vba editor). i used word as the app to launch as i don't have corel
draw. here's the modified code i used:

Hmmmm, that worked here with only two modifications. First, since this was
originally posted in a VBA group, I brought it into Excel and changed

Private Sub Command1_Click()
to
Public Sub TestShell()

Then, when I ran it, I saw that CreateProcess was failing with a LastDllError of 2
(The system cannot find the file specified.), so I looked at the ExeFile constant,
and noted the path here to Word is just slightly different:

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office\WINWORD.EXE"

Note the missing "10" in the last folder name. Could that be the issue, there?

Later... Karl
--
[Microsoft Basic: 1976-2001, RIP]


Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As
Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office10\WINWORD.EXE"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
SendKeys "A"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode As
VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function
 
K

Karl E. Peterson

Gary Roach said:
I get the word app to come up no problem, but the keystrokes still go to the
VBA source window instead of to word.

Well, that's not much to go on. Here, Word pops up, and that keystroke ("A") goes
right into it.

<shrug>

Re-reading your report, you say the "VBA source window"??? How can that be? Your
macro is running at that point, right? That window doesn't accept input, then. I'm
pretty confused by what you're reporting, at this time. Sorry...
--
[Microsoft Basic: 1976-2001, RIP]

Karl E. Peterson said:
Hi Gary --
i could get neither the appactivate nor the waitforinput idle methods to
work. the waitforinput idle returned with return value 0 but when i called
sendkeys in response to that the keys got sent to the calling app (in my
case the vba editor). i used word as the app to launch as i don't have corel
draw. here's the modified code i used:

Hmmmm, that worked here with only two modifications. First, since this was
originally posted in a VBA group, I brought it into Excel and changed

Private Sub Command1_Click()
to
Public Sub TestShell()

Then, when I ran it, I saw that CreateProcess was failing with a
LastDllError of 2 (The system cannot find the file specified.), so I looked
at the ExeFile constant, and noted the path here to Word is just slightly
different:

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office\WINWORD.EXE"

Note the missing "10" in the last folder name. Could that be the issue,
there?

Later... Karl
--
[Microsoft Basic: 1976-2001, RIP]


Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As
Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office10\WINWORD.EXE"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
SendKeys "A"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode As
VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function




Hi Gary --

(Crossed to the API group, to solicit feedback on VB's internal operation
-- see bottom of post for the question this raises in my mind.)

i've already got the shell&wait functionality. but in this case i don't
want to wait until the app closes. i want to wait until it fully opens so
that i can send keystrokes to it. maybe the answer is to use a win32 api
like sendmessage instead of vba's sendkeys.

Usually, VB will actually be blocked until the application is responding.
You can test this with any moderately slow-to-start app:

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command2_Click()
Shell ExeFile, vbNormalFocus
AppActivate Me.Caption
MsgBox "Are we there yet?"
End Sub

I would suppose there may be times when that doesn't happen quite as
planned. To be sure, you can use WaitForInputIdle, which is just a bit
more complicated but not too bad. Here's a simple case, using a hard-coded
executable path (indented to highlight wordwrap!):

Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess
As Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
MsgBox "Application is now waiting for input!"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode
As VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function

I can't be sure, but this little test leads me to think that perhaps VB is
actually inserting the call to WaitForInputIdle itself, following its own
call to CreateProcess. Anyone else have a conjecture on that?

Thanks... Karl
 
G

Gary Roach

i'm not running a macro. i'm running the subroutine from the VBA source code
window associated with access.

Karl E. Peterson said:
Gary Roach said:
I get the word app to come up no problem, but the keystrokes still go to the
VBA source window instead of to word.

Well, that's not much to go on. Here, Word pops up, and that keystroke ("A") goes
right into it.

<shrug>

Re-reading your report, you say the "VBA source window"??? How can that be? Your
macro is running at that point, right? That window doesn't accept input, then. I'm
pretty confused by what you're reporting, at this time. Sorry...
--
[Microsoft Basic: 1976-2001, RIP]

Karl E. Peterson said:
Hi Gary --

i could get neither the appactivate nor the waitforinput idle methods to
work. the waitforinput idle returned with return value 0 but when i called
sendkeys in response to that the keys got sent to the calling app (in my
case the vba editor). i used word as the app to launch as i don't have corel
draw. here's the modified code i used:

Hmmmm, that worked here with only two modifications. First, since this was
originally posted in a VBA group, I brought it into Excel and changed

Private Sub Command1_Click()
to
Public Sub TestShell()

Then, when I ran it, I saw that CreateProcess was failing with a
LastDllError of 2 (The system cannot find the file specified.), so I looked
at the ExeFile constant, and noted the path here to Word is just slightly
different:

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office\WINWORD.EXE"

Note the missing "10" in the last folder name. Could that be the issue,
there?

Later... Karl
--
[Microsoft Basic: 1976-2001, RIP]



Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As
Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program Files\Microsoft
Office\Office10\WINWORD.EXE"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
SendKeys "A"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode As
VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function




Hi Gary --

(Crossed to the API group, to solicit feedback on VB's internal operation
-- see bottom of post for the question this raises in my mind.)

i've already got the shell&wait functionality. but in this case i don't
want to wait until the app closes. i want to wait until it fully opens so
that i can send keystrokes to it. maybe the answer is to use a win32 api
like sendmessage instead of vba's sendkeys.

Usually, VB will actually be blocked until the application is responding.
You can test this with any moderately slow-to-start app:

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command2_Click()
Shell ExeFile, vbNormalFocus
AppActivate Me.Caption
MsgBox "Are we there yet?"
End Sub

I would suppose there may be times when that doesn't happen quite as
planned. To be sure, you can use WaitForInputIdle, which is just a bit
more complicated but not too bad. Here's a simple case, using a hard-coded
executable path (indented to highlight wordwrap!):

Option Explicit

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As
String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any,
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal
lpEnvironment As Any, ByVal lpCurrentDriectory As Any, lpStartupInfo As
STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess
As Long, ByVal dwMilliseconds As Long) As Long

' Constants used with CreateProcess
Private Const STARTF_USESHOWWINDOW As Long = &H1
Private Const STARTF_FORCEONFEEDBACK As Long = &H40

' Structures used with CreateProcess
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long 'LPBYTE
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Const ExeFile As String = "C:\Program
Files\Corel\Graphics10\Programs\coreldrw.exe"

Private Sub Command1_Click()
Dim hProcess As Long
Dim nRet As Long

Const Timeout As Long = 10000 'Ten seconds
Const SYNCHRONIZE As Long = &H100000
Const WAIT_FAILED = -1& 'Error on call
Const WAIT_TIMEOUT = &H102& 'Timeout period elapsed

hProcess = hProcShell(ExeFile, vbNormalFocus).hProcess
If hProcess Then
nRet = WaitForInputIdle(hProcess, Timeout)
Select Case nRet
Case 0
MsgBox "Application is now waiting for input!"
Case WAIT_FAILED
Debug.Print nRet, Err.LastDllError, Hex$(hProcess)
MsgBox "Wait failed."
Case WAIT_TIMEOUT
MsgBox "Wait timed out."
End Select
End If
End Sub

Private Function hProcShell(ByVal JobToDo As String, Optional ExecMode
As VbAppWinStyle = vbNormalFocus) As PROCESS_INFORMATION
' Shells a new process and returns
' the main process handle.
Dim StartUp As STARTUPINFO
Dim ProcInfo As PROCESS_INFORMATION

' Set length of StartUp structure.
StartUp.cb = Len(StartUp)

' Set appropriate StartUp flags.
StartUp.dwFlags = STARTF_USESHOWWINDOW Or STARTF_FORCEONFEEDBACK

' Set StartUp ShowWindow flag.
StartUp.wShowWindow = ExecMode

' Call CreateProcess to start requested job.
If CreateProcess(JobToDo, vbNullString, 0&, 0&, False, 0&, 0&,
vbNullString, StartUp, ProcInfo) Then
hProcShell = ProcInfo
Else
Debug.Print "Can't CreateProcess: "; Err.LastDllError
End If
End Function

I can't be sure, but this little test leads me to think that perhaps VB is
actually inserting the call to WaitForInputIdle itself, following its own
call to CreateProcess. Anyone else have a conjecture on that?

Thanks... Karl
 
G

Gary Walter

Hi Gary,

I don't know how you are opening
with more than one pic, but this may
give you skeleton of code you can adjust:

Option Explicit
Private Declare Function FINDWINDOW Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Sub cmdOpen_Click()
Dim lPhotoEdHwnd As Long
Dim boolOpen As Boolean
Dim lngCounter As Long
lngCounter = 0
Shell "C:\Program Files\Common Files\Microsoft Shared\PhotoEd\photoed.exe",
vbMaximizedFocus
Do While boolOpen = False And lngCounter < 1500
lPhotoEdHwnd = FINDWINDOW("MSPhotoEditor32MainClass", vbNullString)

If lPhotoEdHwnd Then
boolOpen = True
End If
lngCounter = lngCounter + 1
Loop
If boolOpen = True Then
SendKeys "%wt", True
Else
MsgBox "PhotoEd not open."
End If
End Sub
 
A

Andrew Cushen

Gary-

See my latest reply to your earlier question for a link to
a sample that uses SendMessage to send a hotkey
combination to a window. The advantage with SendMessage is
it waits for a response. You may have to look into one of
the APIs like FindWindow to get the handle to the
PhotoEditor window. Let us know if you get it working.

-Andrew
===============================================
-----Original Message-----
Gary Roach said:
i'm not running a macro. i'm running the subroutine from the VBA source code
window associated with access.

Same thing. Honest.
--
[Microsoft Basic: 1976-2001, RIP]


.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top