Utilisation de l'api sendmessage entre deux applications vb.net

Contenu du snippet

Voici un exemple concret qui illustre l'utilisation de l'API Win32 'SendMessage' entre deux applications VB.NET :
- premièrement un programme 'client' qui envoie un message
- deuxièmement un programme 'serveur' qui écoute les messages

Le programme 'client'
=====================
Il utilise l'API SendMessage, mais également WriteProcessMemory et VirtualAllocEx.
Deux cas sont codés, mais toutes les combinaisons sont possibles :
- 1er cas : numéro de message 100255 : un entier est envoyé dans le paramètre wParam, et une chaine de caractères ANSI est envoyée dans le paramètre lParam
- 2ème cas : numéro de message 100355 : un entier est envoyé dans le paramètre wParam, et une structure est envoyée dans le paramètre lParam ; la structure contient 3 champs : 1 Double, une chaine de caractères ANSI, et un entier

La cible du SendMessage est l'ID de la fenêtre principale du programme 'serveur'. Il est obtenu en recherchant parmi les processus actifs, celui dont la propriété 'MainWindowTitle' contient le titre de la fenêtre principale du programme 'serveur'.

Le programme 'serveur'
======================
C'est une simple WinForm, dans laquelle la boucle d'évènements n'est pas celle par défaut. Elle est remplacée par la procédure 'WndProc'. Elle reçoit tous les messages qui sont envoyés à la fenêtre, y compris nos messages utilisateur 100255 et 100355.
Ainsi nos messages utilisateur 100255 et 100355 peuvent être traités particulièrement, et tous les autres messages sont transmis tels quels à la fenêtre.

Source / Exemple :


Client
======

Imports System.Runtime.InteropServices

Public Class MyWinForm

    Enum AllocationType
        Commit = &H1000
        Reserve = &H2000
        Decommit = &H4000
        Release = &H8000
        Reset = &H80000
        Physical = &H400000
        TopDown = &H100000
        WriteWatch = &H200000
        LargePages = &H20000000
    End Enum

    Enum MemoryProtection
        Execute = &H10
        ExecuteRead = &H20
        ExecuteReadWrite = &H40
        ExecuteWriteCopy = &H80
        NoAccess = &H1
        ReadOnlyFlag = &H2
        ReadWrite = &H4
        WriteCopy = &H8
        GuardModifierFlag = &H100
        NoCacheModifierFlag = &H200
        WriteCombineModifierFlag = &H400
    End Enum

    ' Win32 déclarations

    Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hWnd As IntPtr, ByVal wCmd As Integer, _
        ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

    Declare Function WriteProcessMemory Lib "kernel32.dll" _
        (ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, _
        ByVal lpBuffer As IntPtr, ByVal nSize As Integer, _
        ByVal lpNumberOfBytesWritten As IntPtr) As IntPtr

    Declare Function VirtualAllocEx Lib "kernel32.dll" _
        (ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, _
        ByVal dwSize As UInteger, ByVal flAllocationType As AllocationType, _
        ByVal flProtect As MemoryProtection) As IntPtr

    Private Structure MyStructure
        Dim ddd As Double
        Dim sss As IntPtr   ' For Ansi String
        Dim iii As Integer
    End Structure

    Private Const TARGET_WINDOW_TITLE As String = "MyWindow"    ' For example !

    Private Sub btnStructureBySendMessage_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles btnStructureBySendMessage.Click

        Try

            Dim local_processes() As Process = Process.GetProcesses()

            If local_processes.Length > 0 Then
                For Each p As Process In local_processes
                    If p.MainWindowTitle = TARGET_WINDOW_TITLE Then

                        Dim my_struct As MyStructure
                        my_struct.ddd = 100.28  ' For example !
                        my_struct.iii = 258955  ' For example !

                        Dim ansi_str As String = "hello world !"
                        Dim ansi_len As Integer = Marshal.SizeOf("c"c) * ansi_str.Length

                        Dim str_ptr As IntPtr = VirtualAllocEx(p.Handle, IntPtr.Zero, _
                            CUInt(ansi_len), _
                            AllocationType.Commit, MemoryProtection.ReadWrite)

                        my_struct.sss = str_ptr

                        Dim tmp_str_ptr As IntPtr = Marshal.StringToHGlobalAnsi(ansi_str)

                        WriteProcessMemory(p.Handle, str_ptr, tmp_str_ptr, ansi_len, IntPtr.Zero)
                        Marshal.FreeHGlobal(tmp_str_ptr)

                        Dim tmp_struct_ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(my_struct))
                        Marshal.StructureToPtr(my_struct, tmp_struct_ptr, True)

                        Dim my_struct_ptr As IntPtr = VirtualAllocEx(p.Handle, IntPtr.Zero, _
                            CUInt(Marshal.SizeOf(GetType(MyStructure))), _
                            AllocationType.Commit, MemoryProtection.ReadWrite)

                        WriteProcessMemory(p.Handle, my_struct_ptr, tmp_struct_ptr, _
                            Marshal.SizeOf(my_struct), IntPtr.Zero)
                        Marshal.FreeHGlobal(tmp_struct_ptr)

                        Dim w_param As New IntPtr(15)   ' For example !

                        Dim sm_res As IntPtr = _
                            SendMessage(p.MainWindowHandle, 100355, w_param, my_struct_ptr)

                        Exit For
                    End If
                Next
            End If

        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

    Private Sub btnSendAnsiString_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles btnSendAnsiString.Click

        Try

            Dim local_processes() As Process = Process.GetProcesses()

            If local_processes.Length > 0 Then
                For Each p As Process In local_processes
                    If p.MainWindowTitle = TARGET_WINDOW_TITLE Then

                        Dim ansi_str As String = "look at this my friends ..."  ' For example !
                        Dim ansi_len As Integer = Marshal.SizeOf("c"c) * ansi_str.Length

                        Dim str_ptr As IntPtr = VirtualAllocEx(p.Handle, IntPtr.Zero, _
                            CUInt(ansi_len), _
                            AllocationType.Commit, MemoryProtection.ReadWrite)

                        Dim tmp_str_ptr As IntPtr = Marshal.StringToHGlobalAnsi(ansi_str)

                        WriteProcessMemory(p.Handle, str_ptr, tmp_str_ptr, ansi_len, IntPtr.Zero)
                        Marshal.FreeHGlobal(tmp_str_ptr)

                        Dim w_param As New IntPtr(255)  ' For example !

                        Dim sm_res As IntPtr = _
                            SendMessage(p.MainWindowHandle, 100255, w_param, str_ptr)

                        Exit For
                    End If
                Next
            End If

        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

    End Sub
End Class

serveur
=======

Public Class frmAccueil

    Private Structure MyStructure
        Dim ddd As Double
        Dim sss As IntPtr   ' For Ansi String
        Dim iii As Integer
    End Structure

    Protected Overrides Sub WndProc(ByRef m As Message)

        Select Case (m.Msg)
            Case 100255 ' A String should be sent

                ' First thing : we get the integer value stored in wParam
                Dim i As Integer = m.WParam.ToInt32

                Console.WriteLine("wParam is : '" + i.ToString + "'")

                ' Second thing : lParam pointer must be converted to ansi String
                Dim ansi_str As String = Marshal.PtrToStringAnsi(m.LParam)

                Console.WriteLine("lParam is : '" + ansi_str + "'")

            Case 100355 ' A Structure should be sent

                ' First thing : we get the integer value stored in wParam
                Dim i As Integer = m.WParam.ToInt32

                Console.WriteLine("wParam is : " + i.ToString)

                ' Second thing : lParam pointer must be converted to structure
                Dim o As Object = Marshal.PtrToStructure(m.LParam, GetType(MyStructure))
                Dim my_struct As MyStructure = CType(o, MyStructure)

                ' Third thing : 'sss' field of structure must be converted to ansi String
                Dim ansi_sss As String = Marshal.PtrToStringAnsi(my_struct.sss)

                Console.WriteLine("lParam is : ddd='" + my_struct.ddd.ToString + "'" _
                    + " sss='" + ansi_sss.ToString + "'" _
                    + " iii='" + my_struct.iii.ToString + "'")

        End Select

        MyBase.WndProc(m)   ' very important !

    End Sub
End Class

Conclusion :


Illustration de la cohabitation entre du code managé et non managé, et du Marshaling.

Amélioration : tester tous les codes retour des API.

Bon courage !

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.