Voir le flux RSS

wallace1

Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke)

Noter ce billet
par , 03/06/2017 à 21h45 (156 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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
 <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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
 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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
50
 <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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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.....

Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Viadeo Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Twitter Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Google Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Facebook Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Digg Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Delicious Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog MySpace Envoyer le billet « Simuler la touche ENTRER ou TAB sur une application exécutée puis réduite (Pinvoke) » dans le blog Yahoo

Catégories
DotNET , C# , VB.NET

Commentaires