I am looking to send key and mouse events to notepad.exe, however not necessarily have the window be active. In some cases the key events are actual typing operations and in other cases the key input represents shortcut key combinations.
Using SendKeys works, but obviously needs focus. I would prefer to have that operate in the background so am investivating SendMessage from user32.dll. I can see it has different signatures based on what the message code is, and this may be the origin of the problem.
Starting with
Dim notepad() As Process = Process.GetProcessesByName("notepad")
Dim actualWindow As IntPtr = FindWindowEx(notepad(0).MainWindowHandle, IntPtr.Zero, "Edit", Nothing)
and using
<System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SendMessageA")>
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As IntPtr
End Function
if I send
SendMessage(actualWindow, KeyEventType.WM_SETTEXT, 0, "Text")
then the editor text is replaced with "Text".
If however I use
<System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SendMessageA")>
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As IntPtr
End Function
and send
SendMessage(actualWindow, KeyEventType.WM_KEYDOWN, &H4C, 0)
SendMessage(actualWindow, KeyEventType.WM_KEYUP, &H4C, 0)
or even
SendMessage(notepad(0).MainWindowHandle, KeyEventType.WM_KEYDOWN, &H4C, 0)
SendMessage(notepad(0).MainWindowHandle, KeyEventType.WM_KEYUP, &H4C, 0)
where &H4C corresponds to the 'L' key (1.), no letter 'l' appears in the editor.
I am moderately convinced that the function signatures are correct for both WM_KEYDOWN/WM_KEYUP (2.) and WM_SETTEXT (3.). I think the problem might be one of 'focus' but am not sure how to tackle that if I want the operations to occur in the background. How can keystrokes be sent to the text editor?
The enumerations are defined as:
Public Enum KeyEventType As Int32
WM_SETTEXT = &HC
WM_KEYDOWN = &H100
WM_KEYUP = &H101
End Enum
Related
From VB .Net, I'm trying to send a string to a textbox of another application but I cannot make it work. I'm able to get the handle and even set the focus to the textbox but my SendMessage function doesn't seem to be correct as I get the error message "SendMessage' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature."
Here is my code:
Module Module1
Private Const WM_SETTEXT As Int32 = &HC
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32" (ByVal hwndParent As IntPtr, ByVal hwndChildAfter As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
Private Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As IntPtr, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As IntPtr
Sub Main()
Dim AppHwnd As IntPtr = FindWindow(vbNullString, "Test Application"
Dim WinHwnd1 As IntPtr = FindWindowEx(AppHwnd, 0&, "SWT_Window0", vbNullString)
Dim WinHwnd2 As IntPtr = FindWindowEx(WinHwnd1, 0&, "SWT_Window0", vbNullString)
Dim WinHwnd3 As IntPtr = FindWindowEx(WinHwnd2, 0&, "SWT_Window0", vbNullString)
Dim TextBoxHwnd1 As IntPtr = FindWindowEx(WinHwnd3, 0&, "Edit", vbNullString)
Dim TextBoxHwnd2 As IntPtr = FindWindowEx(WinHwnd3, TextBoxHwnd1, "Edit", vbNullString)
MsgBox(TextBoxHwnd2)
SetForegroundWindow(TextBoxHwnd2)
SendMessage(TextBoxHwnd2, WM_SETTEXT, 0&, "text")
End Sub
End Module
The line "MsgBox(TextBoxHwnd2)" returns the handle number I found using Window Detective so I'm assuming the code is correct up to this point. Also, I tested "SetForegroundWindow(TextBoxHwnd1)" and the cursor is on the first textbox whereas "SetForegroundWindow(TextBoxHwnd1) sets the cursor on the second textbox.
Your declaration looks like it might have been migrated from legacy VB code, where Long was a 32-bit integer and Integer was only 16-bit (dating to legacy VB's origination in 16-bit Windows). The length of the data types changed in .NET which was created after the shift to universal 32-bit Windows, so Integer is 32-bit and Long is 64-bit.
Moreover, the Windows API itself needed to be updated for 64-bit Windows. SendMessage is one of the functions that has some parameters that are longer in 64-bit because they are expected to hold pointers.
I checked on the native header file declaration of SendMessage and found that:
The parameter Msg is declared as UINT which would correspond to Integer in VB. This needs to be changed regardless of whether you have a 32-bit or 64-bit build.
The parameter wParam is declared as UINT_PTR so it should in fact be Long for a 64-bit build, but Integer for a 32-bit build; if it's possible to declare it as IntPtr and then pass 0 to it, that would probably be best, because it should automatically adjust the length depending on whether you're building for 32-bit or 64-bit.
I want to obtain the text from a chat room. And to do that I use the Marshal class to get the string pointer and convert back to a string using Marshal.PtrToStringUni. My target string is written in Vietnamese (UTF-8, codepage Windows-1258). And I could not get it displayed correctly (result shows weird Chinese and symbols). What should I change in the code below to get it right?. Thank you ~
'API Declaration
Declare Auto Function SendMessage Lib "user32.dll"(ByVal hWnd As IntPtr, ByVal msg As Integer, _
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
'Sub to get chat room text
' I already got the handle of the chat text area (RoomText)
Private Sub GetText()
'get text length
length = SendMessage(RoomText, WM_GETTEXTLENGTH, 0, 0) + 1
'Alloc memory for the buffer that receives the text
Dim Handle As IntPtr = Marshal.AllocHGlobal(length)
'send WM_GETTEXT message to the chat window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, length, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a textbox
TextBox1.AppendText(Text)
End Sub
Here is the result of the above code:
P/S: In other efforts, I tried the SendMessageW, and SendMessageA functions, and only SendMessageA results the string with mix of English and question mark (something like ng?y de?p...). SendMessageW returns weird characters.
'API Declaration
Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW" (ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
'Sub to get chat room text
' I already got the handle of the chat text area (RoomText)
Private Sub GetText()
'get text length
length = SendMessage(RoomText, WM_GETTEXTLENGTH, 0, 0) + 1
'Alloc memory for the buffer that receives the text
'Be noted that Length is the string character count, but Marshal.AllocHGlobal need byte count.
'in VB.net string, a character use 2 byte so I put *2
Dim Handle As IntPtr = Marshal.AllocHGlobal(length*2)
'send WM_GETTEXT message to the chat window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, length, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a textbox
TextBox1.AppendText(Text)
End Sub
I'm working on a VB.Net project where in part of it I catch a pop up message box when it's displayed and handle it in some way.
My problem is that I have to know what buttons are included in this pop up window (their captions mainly).
Is this possible? Can someone please tell me how this can be done? A sample would be much appreciated.
Thanks.
Update:
Since I got a down vote, and Ed Cottrell told me that this is because of not adding any code related to my question. Now I have the answer to my question so I'm adding some code:
My app catches a pop-up window displayed by another windows application, I used EnumWindows API to know when a new pop-up window is displayed.
Public Declare Function EnumWindows Lib "User32.dll" (ByVal WNDENUMPROC As EnumWindowDelegate, ByVal lparam As IntPtr) As Boolean
Delegate Function EnumWindowDelegate(ByVal hWnd As IntPtr, ByVal Lparam As IntPtr) As Boolean
When I catch this window, I use its handle that I got from EnumWindows result and get its child windows using EnumChildWindows (which are the controls I'm looking for , since controls are kind of windows too):
APIs I used
<DllImport("User32.dll")> _
Public Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
' Get window text length signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
' Get window text signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As StringBuilder) As Int32
Structure ApiWindow
Public Structure ApiWindow
Public MainWindowTitle As String
Public ClassName As String
Public hWnd As Int32
End Structure
Functions
Public Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ChildrenList As New List(Of IntPtr)
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
Public Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = GCHandle.FromIntPtr(Parameter).Target
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Now when I have the handle of pop-up window (parentHandle), I can get its children windows:
Dim hndls() As IntPtr = GetChildWindows(parentHandle)
Dim window As ApiWindow
For Each hnd In hndls
window = GetWindowIdentification(hnd)
'Add Code Here
Next
Where GetWindowIdentification is:
''' <summary>
''' Build the ApiWindow object to hold information about the Window object.
''' </summary>
Public Function GetWindowIdentification(ByVal hwnd As Integer) As ApiWindow
Const WM_GETTEXT As Int32 = &HD
Const WM_GETTEXTLENGTH As Int32 = &HE
Dim window As New ApiWindow()
Dim title As New StringBuilder()
' Get the size of the string required to hold the window title.
Dim size As Int32 = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)
' If the return is 0, there is no title.
If size > 0 Then
title = New StringBuilder(size + 1)
SendMessage(hwnd, WM_GETTEXT, title.Capacity, title)
End If
' Set the properties for the ApiWindow object.
window.MainWindowTitle = title.ToString()
window.hWnd = hwnd
Return window
End Function
I found this answer on the net on another website, I thought I should share it in case someone sees this question in the future:
Buttons are nothing but more windows. You just look for more child
windows of the message box window you found. You can NOT just get
the handle from a window and then try to cast it to a Form object and
expect all the properties to work. It just doesn't work that way.
I have tried automating a few external window's Combo box controls using the API functions, SendMessage and PostMessage and using the Window Messages WM_SETTEXT WM_GETTEXTLENGTH and WM_GETTEXT.
I can get this to work when I run through the code for example pressing F11 through each line or putting a breakpoint in and running it at a certain point, but when I run it as normal, the WM_SETTEXT message doesn't update the Combo box.
It seems to be an issue with the speed that the code is running so my first solution was to put a piece of validation that ensures the text is in the combo box, but for some reason the WM_GETTEXT retrieves the text that should be in the text box even though its not visible.
The code below is a snippet of the kind of validation I've tried, my question here would be is there any sort of wait until method I could use to ensure that the text actually gets entered, I know about the Sleep API but I would ideally like something that actually makes sure that text is there and doesn't load f x seconds and hopes that the text is there.
Any ideas are appriciated.
Many Thanks
VB.net Code Example:
Private Enum WindowMessages
WM_SETTEXT = &HC
WM_GETTEXT = &HD
WM_GETTEXTLENGTH = &HE
End Enum
Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Int32, ByVal wMsg As Int32, ByVal _
wParam As Int32, lParam As String) As Int32
Public Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hWnd
As Int32, _
ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As Int32
Private Sub TestExample(ByVal hWndTextBox as Int32, ByVal TextString as String)
'####Using the hWnd of a Combo box control and a string to enter in the combo box.####
Dim TextLength as Int32
Dim ItemText as string
Do until ItemText = TextString
SendMessage(hWndTextBox, WM_SETTEXT, 0, TextString) > 0
TextLength = SendMessage(hWndTextBox, WM_GETTEXTLENGTH, 0, 0)
ItemText = Space(TextLength)
SendMessage(hWndTextBox, WM_GETTEXT, TextLen + 1, ItemText)
Loop
End Sub
I'm trying to make a program with vb.net (VS2008) for studio shooting with my Nikon D600.
I'm using a program called ControlMyNikon for tethered shooting and it's working perfectly.
It has this external control-feature with following instructions: http://i.jjj.fi/a9dAQ7z.png
Could someone give me hint what does 'send string to 'ControlMyNikon v4.1' window with windows messages' actually mean?
I tried with SendMessages.
I was able to change window title with WM_SETTEXT but that's all.
I'm able to get the window handle but don't know how to send any string to it.
Help? :)
Ugh, Nikon is pretty infamous for writing truly crappy shovelware. This does not disappoint. Here are some declarations that ought to work. Try the Unicode version first. If that produces Chinese text then use the Ansi version:
Imports System.Runtime.InteropServices
Class NativeMethods
Friend Const WM_SETTEXT As Integer = 12
<DllImport("user32.dll", EntryPoint:="SendMessageW", CharSet:=CharSet.Unicode)> _
Friend Shared Function SetWindowTextUnicode(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SendMessageA", CharSet:=CharSet.Ansi)> _
Friend Shared Function SetWindowTextAnsi(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function
End Class
Usage:
NativeMethods.SetWindowTextUnicode(handleYouFound, WM_SETTTEXT, IntPtr.Zero, "shoot")
If neither works then you are probably using the wrong window handle. Use Spy++ to double check that you located the window properly.