par , 03/06/2017 à 21h45 (739 Affichages)
Pour l'exemple j'ai codé une application type Windows Forms (Framework 4.0 ciblé) nommée "TestApp" dans laquelle j'ai ajouté 2 boutons (Button1 et Button2).
Lorsqu'on clique sur Button1 ça affiche une msgBox.
---> Dans un premier temps on récupère le handle du processus et on vérifie qu'il existe bien :
1 2 3 4 5 6
|
Dim processes As Process() = Process.GetProcessesByName("TestApp")
If Not processes Is Nothing AndAlso Not processes.Length = 0 Then
Dim hwnd As IntPtr = processes(0).MainWindowHandle
End If |
---> Ensuite on énumère toutes les fenêtre enfants du processus (ce qui permet de récupérer tous les handle des contrôles du processus parent) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <DllImport("user32.dll")>
Private Shared Function EnumChildWindows(window As IntPtr, callback As EnumWindowProc, i As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Delegate Function EnumWindowProc(hWnd As IntPtr, parameter As IntPtr) As Boolean
Private Shared Function ChildWindowsFromHandle(parent As IntPtr) As List(Of IntPtr)
Dim result As New List(Of IntPtr)()
Dim listHandle As GCHandle = GCHandle.Alloc(result)
Try
Dim childProc As New EnumWindowProc(AddressOf EnumWindowFromHandle)
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle))
Finally
If listHandle.IsAllocated Then
listHandle.Free()
End If
End Try
Return result
End Function
Private Shared Function EnumWindowFromHandle(handle As IntPtr, pointer As IntPtr) As Boolean
Dim gch As GCHandle = GCHandle.FromIntPtr(pointer)
Dim list As List(Of IntPtr) = TryCast(gch.Target, List(Of IntPtr))
If list Is Nothing Then
Throw New InvalidCastException("La cible de GCHandle ne peut pas être castée en type List<IntPtr>")
End If
list.Add(handle)
Return True
End Function |
Ce qui nous donne :
1 2 3 4 5 6 7
| Dim processes As Process() = Process.GetProcessesByName("TestApp")
If Not processes Is Nothing AndAlso Not processes.Length = 0 Then
Dim hwnd As IntPtr = processes(0).MainWindowHandle
For Each wind In ChildWindowsFromHandle(hwnd)
Next
End If |
---> On implémente 2 fonctions permettant de récupérer le titre d'une fenêtre et son nom de classe :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetClassName(hwnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Long) As Long
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindowTextLength(hWnd As IntPtr) As Integer
End Function
<DllImport("user32.dll")>
Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
Private Function CaptionFromHandle(hwnd As IntPtr) As String
Dim cName As String = String.Empty
Dim cText As StringBuilder = Nothing
Try
Dim cLength As Integer = GetWindowTextLength(hwnd)
cText = New StringBuilder(String.Empty, cLength + 5)
GetWindowText(hwnd, cText, cLength + 2)
If Not String.IsNullOrEmpty(cText.ToString()) AndAlso
Not String.IsNullOrWhiteSpace(cText.ToString()) Then
cName = cText.ToString()
End If
Catch ex As Exception
cName = ex.Message
Finally
cText = Nothing
End Try
Return cName
End Function
Private Function ClassNameFromHandle(hwnd As IntPtr) As String
Dim cName As String = String.Empty
Dim cText As StringBuilder = Nothing
Try
Dim cLength As Integer = 1000
cText = New StringBuilder(String.Empty, cLength + 5)
GetClassName(hwnd, cText, cLength + 2)
If Not String.IsNullOrEmpty(cText.ToString()) AndAlso
Not String.IsNullOrWhiteSpace(cText.ToString()) Then
cName = cText.ToString()
End If
Catch ex As Exception
cName = ex.Message
Finally
cText = Nothing
End Try
Return cName
End Function |
---> Si je souhaite simuler la touche ENTRER sur le Button1 je procède ainsi :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function PostMessage(hWnd As IntPtr, Msg As UInteger, wParam As IntPtr, lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Const WM_KEYDOWN As UInteger = &H100
Const WM_KEYUP As UInteger = &H101
Const VK_ENTER As Integer = &HD
Dim processes As Process() = Process.GetProcessesByName("TestApp")
If Not processes Is Nothing AndAlso Not processes.Length = 0 Then
Dim hwnd As IntPtr = processes(0).MainWindowHandle
For Each wind In ChildWindowsFromHandle(hwnd)
If CaptionFromHandle(wind) = "Button1" Then
PostMessage(wind, WM_KEYDOWN, CType(VK_ENTER, IntPtr), IntPtr.Zero)
PostMessage(wind, WM_KEYUP, CType(VK_ENTER, IntPtr), IntPtr.Zero)
End If
Next
End If |
---> Au cas ou tu souhaiterais simuler la touche TAB sur le contrôle alors c'est comme ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13
|
Const VK_TAB As Integer = &H9
Dim processes As Process() = Process.GetProcessesByName("TestApp")
If Not processes Is Nothing AndAlso Not processes.Length = 0 Then
Dim hwnd As IntPtr = processes(0).MainWindowHandle
For Each wind In ChildWindowsFromHandle(hwnd)
If CaptionFromHandle(wind) = "Button2" Then
PostMessage(wind, WM_KEYDOWN, CType(VK_TAB, IntPtr), IntPtr.Zero)
PostMessage(wind, WM_KEYUP, CType(VK_TAB, IntPtr), IntPtr.Zero)
End If
Next
End If |
PS : Dans le cas ou l'application avec laquelle tu souhaites interagir est développée en DotNet il est également possible d'utiliser "System.Reflection" pour simuler des clics etc.....