(MVC) MVC (2018)

How to SendText to specific field in another application by Win32 API.

I.

First of all you must understand the name of application, the window on which you want to send message and name of input fields in that window. There are some application to help you obtain needed information.

There are two main tools to navigate between opened windows in windows desktop. First of it is Microsoft Spy++, which installed by default with Visual Studio.



And second main tools is WinSpy.



This tools help you recognize input fields name.


Programmatically the same operations start from class System.Diagnostics.Process.


  72:      Sub Main()
  73:          Dim Wnd_name As String
  74:          Dim nWnd2 As IntPtr
  75:          Dim Processes As System.Diagnostics.Process() = Process.GetProcesses()
  76:          Dim PokerProcess = (From Y In Processes Select Y Where Y.ProcessName.Contains("Poker")).ToList
  77:          For Each one In PokerProcess
  78:              If one.MainWindowHandle <> IntPtr.Zero Then
  79:                  Wnd_name = one.MainWindowTitle
  80:                  Console.WriteLine(Wnd_name)
  81:                  ObjectDumper.Write(one, 1)
  82:                  nWnd2 = one.MainWindowHandle
  83:              End If
  84:          Next

For visual understanding information of process I create simple reflection object dumper.



Next step is to search all daughter windows from top windows and send the message to needed field.


  85:          Dim allChildWindows2 = New WindowHandleInfo(nWnd2).GetAllChildHandles()
  86:   
  87:          For Each One As IntPtr In allChildWindows
  88:              Dim Name As String = GetClassNameOfWindow(One)
  89:              Console.WriteLine(Name)
  90:              If Name = "PokerStarsChatEditorClass" Then
  91:                  Dim Res As IntPtr = SendMessage(One, WM_SETTEXT, New IntPtr(0), Wnd_name)
  92:              End If
  93:          Next
  94:   
  95:      Private Function GetClassNameOfWindow(ByVal hwnd As IntPtr) As String
  96:          Dim className As String = ""
  97:          Dim classText As Text.StringBuilder = Nothing
  98:   
  99:          Try
 100:              Dim cls_max_length As Integer = 1000
 101:              classText = New Text.StringBuilder("", cls_max_length + 5)
 102:              GetClassName(hwnd, classText, cls_max_length + 2)
 103:              If Not String.IsNullOrEmpty(classText.ToString()) AndAlso Not String.IsNullOrWhiteSpace(classText.ToString()) Then className = classText.ToString()
 104:          Catch ex As Exception
 105:              className = ex.Message
 106:          Finally
 107:              classText = Nothing
 108:          End Try
 109:   
 110:          Return className
 111:      End Function

This code use some definition of Win32 API (full definition available in https://pinvoke.net/


  15:      Public Const WM_CHAR = &H102
  16:      Public Const WM_SETTEXT = &HC
  17:      Public Const WM_KEYDOWN = &H100
  18:      Public Const WM_KEYUP = &H101
  19:      Public Const WM_LBUTTONDOWN = &H201
  20:      Public Const WM_LBUTTONUP = &H202
  21:      Public Const WM_CLOSE = &H10
  22:      Public Const WM_COMMAND = &H111
  23:      Public Const WM_CLEAR = &H303
  24:      Public Const WM_DESTROY = &H2
  25:      Public Const WM_GETTEXT = &HD
  26:      Public Const WM_GETTEXTLENGTH = &HE
  27:      Public Const WM_LBUTTONDBLCLK = &H203
  28:   
  29:   
  30:      <DllImport("User32.dll")>
  31:      Private Function ShowWindow(hWnd As Integer, nCmdShow As Integer) As Boolean
  32:      End Function
  33:   
  34:      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  35:      Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As Text.StringBuilder) As IntPtr
  36:      End Function
  37:   
  38:      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  39:      Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As IntPtr
  40:      End Function
  41:   
  42:      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  43:      Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As IntPtr
  44:      End Function
  45:   
  46:   
  47:      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  48:      Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As IntPtr
  49:      End Function
    :   
  67:      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  68:      Private Function GetClassName(ByVal hWnd As IntPtr, ByVal lpClassName As Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
  69:      End Function

And this is simple additional wrapper over Win32 EnumChildWindows function.


   1:  Imports System.Runtime.InteropServices
   2:   
   3:  Public Class WindowHandleInfo
   4:      Private Delegate Function EnumWindowProc(ByVal hwnd As IntPtr, ByVal lParam As IntPtr) As Boolean
   5:   
   6:      <DllImport("user32")>
   7:      Private Shared Function EnumChildWindows(ByVal window As IntPtr, ByVal callback As EnumWindowProc, ByVal lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
   8:      End Function
   9:   
  10:      Private _MainHandle As IntPtr
  11:   
  12:      Public Sub New(ByVal handle As IntPtr)
  13:          Me._MainHandle = handle
  14:      End Sub
  15:   
  16:      Public Function GetAllChildHandles() As List(Of IntPtr)
  17:          Dim childHandles As List(Of IntPtr) = New List(Of IntPtr)()
  18:          Dim gcChildhandlesList As GCHandle = GCHandle.Alloc(childHandles)
  19:          Dim pointerChildHandlesList As IntPtr = GCHandle.ToIntPtr(gcChildhandlesList)
  20:   
  21:          Try
  22:              Dim childProc As EnumWindowProc = New EnumWindowProc(AddressOf EnumWindow)
  23:              EnumChildWindows(Me._MainHandle, childProc, pointerChildHandlesList)
  24:          Finally
  25:              gcChildhandlesList.Free()
  26:          End Try
  27:   
  28:          Return childHandles
  29:      End Function
  30:   
  31:      Private Function EnumWindow(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Boolean
  32:          Dim gcChildhandlesList As GCHandle = GCHandle.FromIntPtr(lParam)
  33:   
  34:          Dim childHandles As List(Of IntPtr) = TryCast(gcChildhandlesList.Target, List(Of IntPtr))
  35:          childHandles.Add(hWnd)
  36:          Return True
  37:      End Function
  38:  End Class

And this is a result of this test.



II.

But not all windows program has controls derived from Windows Control class, therefore some program is show only main window.



In this case we can use UI Automation. Below you can see a small test to use this framework with firefox.


   1:  Imports System.Windows.Automation
   2:   
   3:  Module Module1
   4:   
   5:      Sub Main()
   6:          Dim Firefox As Process() = Process.GetProcessesByName("firefox")
   7:          If Firefox.Length > 0 Then
   8:              Dim Parent = Firefox(0).Parent
   9:              Dim RootElement As AutomationElement = AutomationElement.FromHandle(Parent.MainWindowHandle)
  10:              Dim Controls As Condition = New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document)
  11:              Console.WriteLine(RootElement.FindFirst(TreeScope.Descendants, Controls))
  12:              Dim DocElement As AutomationElement = RootElement.FindFirst(TreeScope.Descendants, Controls)
  13:              For Each pattern As AutomationPattern In DocElement.GetSupportedPatterns()
  14:                  Console.WriteLine(DocElement.GetSupportedPatterns(0).GetType.FullName)
  15:                  If TypeOf DocElement.GetCurrentPattern(pattern) Is ValuePattern Then
  16:                      Console.WriteLine((TryCast(DocElement.GetCurrentPattern(pattern), ValuePattern)).Current.Value.ToString())
  17:                  End If
  18:              Next
  19:              'freeze
  20:              Dim DocElements As AutomationElementCollection = RootElement.FindAll(TreeScope.Descendants, Controls)
  21:              For Each One As AutomationElement In DocElements
  22:                  For Each pattern As AutomationPattern In One.GetSupportedPatterns()
  23:                      If TypeOf One.GetCurrentPattern(pattern) Is ValuePattern Then Console.WriteLine((TryCast(One.GetCurrentPattern(pattern), ValuePattern)).Current.Value.ToString() + Environment.NewLine)
  24:                  Next
  25:              Next
  26:              Console.ReadLine()
  27:          End If
  28:      End Sub
  29:   
  30:  End Module



Pay attention than first part of code (FindFirst URL) working quick and fine but second part of this test (FindAll) is working extremely slow. Little bit later, after some experiments, I found more faster solution How to Read URLs from Firefox tabs by UI Automation.



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19
Link to this page: http://www.vb-net.com/SendMessage_user32/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <MAIL ME>  <ABOUT ME>  < THANKS ME>