Search code examples
vb.netvisual-studiohandlewindow-handles

Execute a application and wait for it to be loaded


I'm searching for a way to execute Steam (Steam.exe) and wait for it to be loaded (Not to exited).

I was think a good idea is to list all the child handles and search for a string in the title handles because when steam loads exist a window handle named "Friends", but this is hard to do it for me (APIs) so maybe exist a easy way to wait for a program to load...

Example of what I will do:

' First process
Process.start("Process 1.exe")

' Here will goes an efficient method to wait for application is loaded,
' not sleeping X seconds measuring the loading time or any of that.

' Second process
' Depends on first process fully loaded.
' If first process is not fully loaded (libraries and that) then this process can't run.
Process.start("Processs 2.exe")

UPDATE:

The Main question is how to wait for X process to be loaded, but about the Steam.exe I've noticed too when Steam is loaded tries to read/open some regkeys:

http://img267.imageshack.us/img267/6599/prtscrcapture4u.jpg

Maybe I can do something with code to monitor if one of that RegKeys is accesed?

Something like this?:

Dim RegKey = "HKLM\...blablabla"
Process.start("steam.exe")

While not Regkey.IsAccessed 
  ' Do nothing and wait
Loop

Process.start("Second process.exe")

UPDATE 2:

I'm trying to make a function taking by reference the solution of Amegon, I don't finished the code because I'm getting an error in the line:

Memory = hprocess.PrivateMemorySize64

enter image description here

But I only get the error when I try to launch a "big" application (application that needs much time to load like Photoshop), with "little" apps works good.

Please if someone can help me to simplify/improve the Amegon's solution to make a Generic function and to correct the memory overflow error... Thank you!

This is my code:

' This is the sub that launchs the unfinished function:
Private Sub Launch_Game()
    'Wait_For_Application_To_Load("C:\Games\Steam.exe", "-applaunch 0")
    Wait_For_Application_To_Load("C:\Program Files\Adobe Photoshop CS6 (64 Bit)\Photoshop.exe")
End Sub

Private Function Wait_For_Application_To_Load(ByVal APP_Path As String, Optional ByVal APP_Arguments As String = Nothing)
    Dim File = My.Computer.FileSystem.GetFileInfo(APP_Path)
    Process.Start(APP_Path, APP_Arguments)
    Timer_CheckCPU.Tag = File.Name.Substring(0, File.Name.Length - 4) ' Photoshop
    Timer_CheckCPU.Enabled = True
End Function

Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByVal lpBuffer As Integer, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Private WithEvents Timer_CheckCPU As New Timer

Dim Memory_Value_Changed As Boolean
Dim CPU_Changed As Boolean
Dim CPU_Time As Boolean
Dim Running_Time As Boolean
Private _desiredTime_ms As Integer = 1500

Private Sub Timer_CheckCPU_Tick(sender As Object, ev As EventArgs) Handles Timer_CheckCPU.Tick
    Timer_CheckCPU.Enabled = False
    Dim pProcess() As Process = System.Diagnostics.Process.GetProcessesByName(Timer_CheckCPU.Tag)
    Dim hprocess As Process = pProcess(0)
    If hprocess Is Nothing Then
        Running = False
        Timer_CheckCPU.Enabled = True
        Return
    End If
    Running = True


    ' Here is the error:
    Memory = hprocess.PrivateMemorySize64
    ' MsgBox(hprocess.PrivateMemorySize64.ToString)


    CPUTotal = hprocess.TotalProcessorTime.TotalMilliseconds

    If AllConditionsGood() Then
        If Not (_countdown.IsRunning) Then
            _countdown.Reset()
            _countdown.Start()
        End If
        Dim _elapsed As Integer = _countdown.ElapsedMilliseconds
        If _elapsed >= _desiredTime_ms Then
            Me.TopMost = True
            MsgBox("process loaded")
            Return
        End If
    Else
        _countdown.Reset()
    End If
    Timer_CheckCPU.Enabled = True
End Sub

Private Function AllConditionsGood() As Boolean
    If CPU_Time Then Return False
    If Memory_Value_Changed Then Return False
    If Running_Time Then Return False
    Return True
End Function

Private _countdown As New Stopwatch

Private _Running As Boolean = False
Public WriteOnly Property Running() As Boolean
    Set(ByVal value As Boolean)
        _Running = value
        If value Then
            Running_Time = False
        Else
            Running_Time = True
        End If
    End Set
End Property

Private _CPUTotal As Integer
Public WriteOnly Property CPUTotal() As Integer
    Set(ByVal value As Integer)
        CPU = value - _CPUTotal 'used cputime since last check
        _CPUTotal = value
    End Set
End Property


Private _CPU As Integer
Public WriteOnly Property CPU() As Integer
    Set(ByVal value As Integer)
        If value = 0 Then
            CPU_Time = False
        Else
            CPU_Time = True
        End If
        _CPU = value
    End Set
End Property

Private _Memory As Integer
Public WriteOnly Property Memory() As Integer
    Set(ByVal value As Integer)
        MemoryDiff = Math.Abs(value - _Memory)
        _Memory = value
    End Set
End Property

Private _MemoryDiff As Integer
Public WriteOnly Property MemoryDiff() As Integer
    Set(ByVal value As Integer)
        If value = _MemoryDiff Then
            Memory_Value_Changed = False
        Else
            Memory_Value_Changed = True
        End If
        _MemoryDiff = value
    End Set
End Property

Solution

  • Update I have build an example program to show this memory and cpu observing

    To run it create a new project, windows form application, then open the code part and insert the following code. -no need to add any controls, I added them in code manually. I as able to successfully run the code with VS 2012 ultimate on win8 prof x64- :

    Option Strict On
    Imports System.Runtime.InteropServices
    
    Public Class Form1
        Private WithEvents btnStart As New Button
        Private WithEvents tmrCheckCPU As New Timer
    
        Private tbRunning As New TextBox
        Private tbCPU As New TextBox
        Private tbMemory As New TextBox
        Private tbTime As New TextBox
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.Size = New Size(400, 400)
            With btnStart
                '.Text = "Start DxDiag"
                .Text = "Start Photoshop"
                Me.Controls.Add(btnStart)
                .Location = New Point(50, 50)
                .Size = New Size(200, 50)
            End With
            With tmrCheckCPU
                .Interval = 100
                .Enabled = False
            End With
            With tbRunning
                Me.Controls.Add(tbRunning)
                .Location = New Point(50, 110)
                .Size = New Size(100, 40)
            End With
            With tbCPU
                Me.Controls.Add(tbCPU)
                .Location = New Point(50, 150)
                .Size = New Size(100, 40)
            End With
            With tbMemory
                Me.Controls.Add(tbMemory)
                .Location = New Point(50, 200)
                .Size = New Size(100, 40)
            End With
            With tbTime
                Me.Controls.Add(tbTime)
                .Location = New Point(50, 250)
                .Size = New Size(100, 40)
            End With
        End Sub
    
        Private Sub btnStart_Clicked(sender As Object, ev As EventArgs) Handles btnStart.Click
            'start Process
            'Process.Start("dxdiag.exe", "/whql:on")
            Process.Start("C:\Program Files\Adobe\Adobe Photoshop CS6 (64 Bit)\Photoshop.exe")
            'start watching Timer
            tmrCheckCPU.Enabled = True
        End Sub
    
        Private Function FindProcess(MainWindowTitle As String) As System.Diagnostics.Process
            For Each ele As System.Diagnostics.Process In Process.GetProcesses
                If ele.MainWindowTitle = MainWindowTitle Then Return ele
            Next
            Return Nothing
        End Function
    
        Private Sub tmrCheckCPU_Tick(sender As Object, ev As EventArgs) Handles tmrCheckCPU.Tick
            tmrCheckCPU.Enabled = False
            'Dim name As String = "DirectX Diagnostic Tool"
            Dim name As String = "Adobe Photoshop CS6 Extended"
            Dim hprocess As Process = FindProcess(name)
            If hprocess Is Nothing Then
                Running = False
                tmrCheckCPU.Enabled = True
                Return
            End If
            Running = True
            Memory = hprocess.PrivateMemorySize64
            CPUTotal = hprocess.TotalProcessorTime.TotalMilliseconds
    
            If AllConditionsGood() Then
                If Not (_countdown.IsRunning) Then
                    _countdown.Reset()
                    _countdown.Start()
                End If
                Dim _elapsed As Long = _countdown.ElapsedMilliseconds
                If _elapsed >= _desiredTime_ms Then
                    tbTime.Text = _desiredTime_ms.ToString
                    tbTime.BackColor = Color.LightGreen
                    Me.TopMost = True
                    Me.Focus()
                    MsgBox("process loaded")
                    Return
                Else
                    tbTime.Text = _elapsed.ToString
                    tbTime.BackColor = Color.LightGreen
                End If
            Else
                _countdown.Reset()
            End If
            tmrCheckCPU.Enabled = True
        End Sub
    
        Private _desiredTime_ms As Integer = 1500
    
        Private Function AllConditionsGood() As Boolean
            If tbCPU.BackColor <> Color.LightGreen Then Return False
            If tbMemory.BackColor <> Color.LightGreen Then Return False
            If tbRunning.BackColor <> Color.LightGreen Then Return False
            Return True
        End Function
    
        Private _countdown As New Stopwatch
    
        Private _Running As Boolean = False
        Public WriteOnly Property Running() As Boolean
            Set(ByVal value As Boolean)
                _Running = value
                If value Then
                    tbRunning.Text = "Running"
                    tbRunning.BackColor = Color.LightGreen
                Else
                    tbRunning.Text = "Not Running"
                    tbRunning.BackColor = Color.LightPink
                End If
            End Set
        End Property
    
        Private _CPUTotal As Double
        Public WriteOnly Property CPUTotal() As Double
            Set(ByVal value As Double)
                CPU = value - _CPUTotal 'used cputime since last check
                _CPUTotal = value
            End Set
        End Property
    
    
        Private _CPU As Double
        Public WriteOnly Property CPU() As Double
            Set(ByVal value As Double)
                If value = 0 Then
                    tbCPU.BackColor = Color.LightGreen
                Else
                    tbCPU.BackColor = Color.LightPink
                End If
                _CPU = value
                tbCPU.Text = value.ToString
            End Set
        End Property
    
        Private _Memory As Long
        Public WriteOnly Property Memory() As Long
            Set(ByVal value As Long)
                MemoryDiff = Math.Abs(value - _Memory)
                _Memory = value
            End Set
        End Property
    
        Private _MemoryDiff As Long
        Public WriteOnly Property MemoryDiff() As Long
            Set(ByVal value As Long)
                If value = _MemoryDiff Then
                    tbMemory.BackColor = Color.LightGreen
                Else
                    tbMemory.BackColor = Color.LightPink
                End If
                _MemoryDiff = value
                tbMemory.Text = value.ToString
            End Set
        End Property
    
    End Class
    

    If you click on the button, it will start dxdiag and shows the change of memory and the difference of total cpu time (compared with last time) in textboxes. If all conditions are met (running, no cpu total time change, no memory change) then a stopwatch will count to 1500 and then a messagebox will say 'finished'

    It is dirty written, but I guess you understand the main core (how to find the correct process without any dll declare method), how to read some information of that process after finding it, and in what way to apply the conditions.

    The waiting time is meant to assure that a second of pure hard drive loading time will not create the wrong impression that loading is done (no memory or cpu change maybe).

    The risk is that.. if a page will automatically appear and show content/advertisement, then maybe the cpu is constantly used. so you may only check for memory change or any other value tht you can access from the Process.

    Update corrected code (fixed the wrong data types). Now Option strict is not complaining about my code :)