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 !
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.