3

I have the following script to launch an application:

add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
$args = "arguments"
$proc = Start-Process -PassThru "path" -ArgumentList $args
start-sleep -Seconds 5
[Microsoft.VisualBasic.Interaction]::AppActivate($proc.Id)
[System.Windows.Forms.SendKeys]::SendWait("~")

I use this script to launch several windows forms applications that need user interaction to start (i.e. push a start button). So far I've been sleeping the script 5 seconds to allow the application to launch. I tried to run this script in a variety of computers with different processing capabilities, but did not work in all computers, some launch the application slower than others, when the app took more than 5 seconds to launch the app the script fails, because the AppActivate could not find the PID. I don't want to try different sleeping times for each computer, because I have to run this script in more than 100 computers at boot time.

I would like to know if there is a way to wait for the application to launch in a event driven way.

UPDATE:

The WaitForInputIdle does not work in all applications I tried to start. It returns immediately (successfully because it's returning true) and the AppActive method throws an exception.

9
  • does this help you....start-process -wait ( wait for the process to finish) Commented Feb 1, 2016 at 3:03
  • No, because that waits for the process to complete before accepting more input. I have to sleep the script for 5 seconds otherwise the AppActivate function throws an exception because it did not found the process ID. Commented Feb 1, 2016 at 3:10
  • Would it be because instead of <code> -Seconds </code> it should be <code> -s </code> PS. idk why the code tag is not working o_O Commented Feb 1, 2016 at 4:16
  • How many processes did u start? and did they open? Commented Feb 1, 2016 at 4:30
  • The sleep cmdlet is working fine, I used this script to start several applications, the number of process varies in every machine. Commented Feb 1, 2016 at 4:40

4 Answers 4

7

I suggest you to avoid using "Sleep" to synchronize system objects (it's never a good solution). As far as you are starting Windows Forms application I suggest you to use Process.WaitForInputIdle Method.

 $p = [diagnostics.process]::start("notepad.exe", "D:\temp\1mbfile.txt")
 $p.WaitForInputIdle(5000);

using it in a loop you can test alternatively test if the input is idle (application is started and waiting) or if the application is stopped ($p.HasExited -eq $true).

do
 {
   if ($p.HasExited -eq $true)
   {
     break
   }
 } while ($p.WaitForInputIdle(5000) -ne $true)
Sign up to request clarification or add additional context in comments.

6 Comments

How can I know with this loop approach, when the process finish creating its main window and is idle (app started and waiting for user input)?
I edit the code, normaly after the loop the windows is idle. You should test $p.HasExited to verify that the process has not exited.
Yes I tested like that, but WaitForInputIdle does not work all the times, with some applications returns intermediately and AppActivate gives me an error (Process {0} was not found). The applications are .net apps.
Actually I tested it like this: do{ if ($proc.HasExited -eq $true) { break } } while ($proc.WaitForInputIdle() -eq $false) [Microsoft.VisualBasic.Interaction]::AppActivate($proc.ID)
The fact that you are using .NET applictions is not the point, it's supposed to work everytime an application use a message loop (application interface). You should activate your application before using this.
|
2

So, I finally wrote this workaround since WaitForInputIdle does not work with all my applications and I was not able to figure out why.

do{
    if ($proc.MainWindowHandle -ne 0)
   {
        break
   }
   $proc.Refresh() 
} while ($true)

I am not sure if this is the best approach; anyway, I wanted to share this.

Comments

2

Neither of the other answers waited long enough for my script, so I came up with this, which waits until the process stops using the cpu.

do{
$ProcCpu = $Process.CPU
Start-Sleep -Milliseconds 100
} until($ProcCpu -eq $Process.CPU)

2 Comments

can you post the full code so we can see how you call your arguments
Well, I haven't used that script or even opened powershell in about 3 years, so ... ¯\_(ツ)_/¯
0

I finally have a working method that waits for the application to launch and then proceeds to run other commands to that application without using sleep commands

also allows you to run your program in Realtime something we thought was impossible to do.

allows you to pass arguments and environment variables too.

It fully waits until your slow computer or fast computer launches the program before proceeding past the powershell command.

this is a combination of bat and powershell. (run through .bat)

@echo off

QPROCESS "testprogram.exe">NUL
IF %ERRORLEVEL% EQU 1 Start "" /Realtime "c:\testprogram.exe" %mycustomenvironmentvariables% %* >nul

powershell Do{if ($ugraf = Get-Process -Name "testprogram"){$testprogram.MainWindowTitle}else {schtasks /run /TN "testprogram"}}Until($testprogram.MainWindowTitle)

:TRY
QPROCESS "testprogram.exe">NUL
IF %ERRORLEVEL% EQU 0 Start "my awesome test program" /Realtime "c:\testprogram.exe" %*
QPROCESS "testprogram.exe">NUL
IF %ERRORLEVEL% EQU 0 goto :SUCCESS
goto :TRY
:SUCCESS

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.