今天,我要为大家讲解,如何在vb.net中使用SendMessage API,来发送信息,并Overrides那个WndProc函数来接受信息
首先请在新建一个Class,代码:
Imports System.Runtime.InteropServices
Public Class Form1
Public Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As IntPtr
Public lpData As IntPtr
End Structure
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As IntPtr, ByRef lParam As COPYDATASTRUCT) As IntPtr
Public Const WM_COPYDATA As Integer = &H4A
End Class
—— 发送信息 篇 ——
首先,我们要得到一个 句柄 IntPtr,变量名为hWnd(Me.Handle就是一个例子)
过后我们New一个COPYDATASTRUCT,变量名为CDS
CDS.dwData不是很清楚是什么来的,听说是自定义资料,个人觉得放intptr.zero就好了
代码:CDS.dwData = IntPtr.Zero
CDS.lpData 是我们要发生的信息,在这里我们需要用Marshal.StringToHGlobalAuto来转换Managed String成Unmanaged memory
代码:CDS.lpData = Marshal.StringToHGlobalAuto("message here")
CDS.cbData 则是指定memory大小
代码:CDS.cbData = New IntPtr(Marshal.SystemDefaultCharSize * Msg.Length)
然后就是用API来发送信息
代码:SendMessage(hWnd, WM_COPYDATA, 0, CDS)
—— 接受信息 篇 ——
如果你还记得我们SendMessage的代码,SendMessage(..., ..., ..., lParam as COPYDATASTRUCT),
你应该会猜到,其他程序发送给我们的CDS其实就在m.LParam。
可是,它是CDS,是一个结构体(Structure),不能直接使用,我们需要“还原”那个CDS出来。
我们先New一个COPYDATASTRUCT,变量名为CDS。
然后,我们使用m里面的一个函数,叫GetLParam,它可以让我们转换LParam到另一个 类型。
转换到我们要的COPYDATASTRUCT类型后,就可以给CDS赋值了。
代码:CDS = m.GetLParam(CDS.GetType)
但毕竟这样得到是Object,如果要更正式点,可以加个CType
代码:CDS = CType(m.GetLParam(CDS.GetType), COPYDATASTRUCT)
好了,得到了CDS了,现在我们可以通过CDS的lpData,来得到 信息。
信息 发过来时是由String变成Unmanaged Memory发来的,所以我们现在要转回去。
又要麻烦下Marshal了~ 这次我们用的是PtrToStringAuto,得提供两个参数,一个是Unmanaged Memory,一个是Memory Size
代码:Dim Str as string = Marshal.PtrToStringAuto(cds.lpData, cds.cbData.ToInt32 \ Marshal.SystemDefaultCharSize)
现在那 信息 就在 Str 变量里面了~
如果你要指定返回值,可以给m.Result赋值~
代码:m.Result = New IntPtr(777)
示例代码:
Imports System.Runtime.InteropServices
Public Class Form1
<StructLayout(LayoutKind.Sequential)> _
Public Structure COPYDATASTRUCT
Dim dwData As IntPtr
Dim cbData As IntPtr
Dim lpData As IntPtr
End Structure
Public Declare Auto Function SendMessage Lib "user32.dll" Alias "SendMessage" (ByVal hWnd As IntPtr, ByVal wMsg As Int32, _
ByVal wParam As IntPtr, ByRef lParam As COPYDATASTRUCT) As IntPtr
Public Const WM_COPYDATA As Integer = &H4A
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
txtMeHandle.Text = Me.Handle
End Sub
Private Sub btnNewInstance_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNewInstance.Click
Shell(Application.ExecutablePath, AppWinStyle.NormalFocus)
End Sub
Private Sub btnSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSend.Click
Dim hWnd As IntPtr 'for the target handle
'First we need to know the environment is 32bit or 64bit
If IntPtr.Size = 4 Then 'is 32 bit
Dim hWndInt As Integer 'for storing the parsed integer
If Integer.TryParse(txtTargetHandle.Text, hWndInt) Then
hWnd = New IntPtr(hWndInt)
Else
MsgBox("The handle you entered was unable to parse, please make sure it's number-only.")
Return
End If
ElseIf IntPtr.Size = 8 Then 'is 64 bit
Dim hWndLong As Long ' for storing the parsed long
If Long.TryParse(txtTargetHandle.Text, hWndLong) Then
hWnd = New IntPtr(hWndLong)
Else
MsgBox("The handle you entered was unable to parse, please make sure it's number-only.")
Return
End If
End If
'Then, we need to have a COPYDATASTRUCT, for storing the data we wanted to send
Dim CDS As New COPYDATASTRUCT
CDS.dwData = IntPtr.Zero
'CDS.lpData <- it's actually unmanaged memory, so if we want to send string, we must use a function in Marshal
'to convert our string to unmanaged memory.
CDS.lpData = Marshal.StringToHGlobalAuto(txtMsgToSend.Text)
'CDS.cbData <- it's represent the memory size. Any type of data, even like string, also required a certain memory size.
'So we should make some math, tell the API, how many memory we want for our string
CDS.cbData = Marshal.SystemDefaultCharSize * txtMsgToSend.Text.Length
'OK, now it's time for us to use the API, as we prepared all of the data that API required
' If you just wanna send message, then:
' SendMessage(hWnd, WM_COPYDATA, 0, CDS)
' If you wanna get the "response" from the target window too, then:
' Dim Result as IntPtr = SendMessage(hWnd, WM_COPYDATA, 0, CDS)
Dim Result As IntPtr = SendMessage(hWnd, WM_COPYDATA, 0, CDS)
MsgBox("Target window give back this IntPtr: " & Integer.Parse(Result))
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
If m.Msg = WM_COPYDATA Then
Dim CDS As New COPYDATASTRUCT
CDS = CType(m.GetLParam(CDS.GetType), COPYDATASTRUCT)
Dim Msg As String = Marshal.PtrToStringAuto(CDS.lpData, CDS.cbData.ToInt32 \ Marshal.SystemDefaultCharSize)
Dim Ans As String = InputBox("Received a message, message content:" & vbCrLf & Msg & vbCrLf & vbCrLf & "Please enter the result (number-only): ", "I got a message!")
Dim mResult As Integer
If Integer.TryParse(Ans, mResult) = False Then
MsgBox("You entered a non-number-only value, the result will be 0")
Else
m.Result = New IntPtr(mResult)
End If
End If
End Sub
End Class
看到了吧?其实 接受信息 和 发送信息 的方法是一样的,只不过倒转过来罢了~
可是对于新手,尤其是曾经的我,在没有相关资料辅助下是很难明白的,故写此贴。
由于本人水平有限,可能有些地方写得不对,本人的建议是,先读了此贴,有个概念,再去读其他高手的帖,希望可以让你更明白~
No comments:
Post a Comment
你对此有何看法?