I have written a program that is to find a box in another program and set focus to it. Once this is done it will sendkeys and save to this box.
I am using Findwindow and FindwindowEx to locate the box, but I have a bit of an issue.
if you notice the windows are the same all the way down to the first TPanel. Now after that there are 3Tpanel Classes.
After 3Tpanel Classes there are multiple TttgEdit Classes.
How do I teach the program which Classes I want to select?
Here is my code thus far.
Delcare
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
Private Declare Auto Function FindWindow Lib "user32.dll" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32.dll" ( _
ByVal hwndParent As IntPtr, _
ByVal hwndChildAfter As IntPtr, _
ByVal lpszClass As String, _
ByVal lpszWindow As String _
) As IntPtr
Source
Dim hWnd As IntPtr = FindWindow("TRunprgForm", Nothing)
If hWnd.Equals(IntPtr.Zero) Then
Return
End If
cb1.Checked = True
'--------------------instert here
Dim hWndChild1 As IntPtr = _
FindWindowEx(hWnd, IntPtr.Zero, "TmisinvForm", Nothing)
If hWndChild1.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild2 As IntPtr = _
FindWindowEx(hWndChild1, IntPtr.Zero, "TScrollBox", Nothing)
If hWndChild2.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild3 As IntPtr = _
FindWindowEx(hWndChild2, IntPtr.Zero, "TPageControl", Nothing)
If hWndChild3.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild4 As IntPtr = _
FindWindowEx(hWndChild3, IntPtr.Zero, "TTabSheet", Nothing)
If hWndChild4.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild5 As IntPtr = _
FindWindowEx(hWndChild4, IntPtr.Zero, "TttgCenterPanel", Nothing)
If hWndChild5.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild6 As IntPtr = _
FindWindowEx(hWndChild5, IntPtr.Zero, "TPanel", Nothing)
If hWndChild6.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndEdit As IntPtr = _
FindWindowEx(hWndChild6, IntPtr.Zero, "TttgDBEdit", Nothing)
If hWndEdit.Equals(IntPtr.Zero) Then
Return
End If
SetForegroundWindow(hWndEdit)
The numbers on the left hand side, hWnd, They change every time the screen is closed and opened, so I cant use them as a static number. Any help would be awesome.
It looks like you want the second TPanel under the TttgCenterPanel.
In order to do that, you can find the first TPanel (you already did this), and after that, find the TPanel that is a descendant of TttgCenterPanel, and comes after the first TPanel. You need to pass hwndChild5 into the hwndChildAfter of FindWindowEx`.
' .... all the stuff you did before
Dim hWndChild5 As IntPtr = _
FindWindowEx(hWndChild4, IntPtr.Zero, "TttgCenterPanel", Nothing)
If hWndChild5.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild6 As IntPtr = _
FindWindowEx(hWndChild5, IntPtr.Zero, "TPanel", Nothing)
If hWndChild6.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndChild6Second As IntPtr = _
FindWindowEx(hWndChild5, hWndChild6, "TPanel", Nothing)
If hWndChild6Second.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndEdit As IntPtr = _
FindWindowEx(hWndChild6Second, IntPtr.Zero, "TttgDBEdit", Nothing)
If hWndEdit.Equals(IntPtr.Zero) Then
Return
End If
SetForegroundWindow(hWndEdit)
From the MSDN documentation of FindWindowEx:
hwndChildAfter [in, optional]
Type: HWND
A handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.
This approach will work if you are trying to find the second TPanel. If they are in random order each time, this will fail.
Related
This is code to save untitled notepad to desktop or given path. when we run this code visual studio host stopping some times and some times marshal object throwing error, when we have more than one notepad opened some files are saving .. after that program stopping automatically.. we are passing path as Environment.SpecialFolder.Desktop
Private Const WM_GETTEXT As Integer = &HD
Private Const WM_GETTEXTLENGTH As Integer = &HE
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
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
ByVal childAfter As IntPtr, _
ByVal lclassName As String, _
ByVal windowTitle As String) As IntPtr
End Function
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Sub saveNotepad(pat As String)
Try
Dim processes() As Process
Dim procName As String = "notepad"
processes = Process.GetProcessesByName(procName)
If processes IsNot Nothing Then
For Each proc In processes
If Not proc.MainWindowTitle = "" Then
Dim Hwnd As IntPtr = FindWindow(Nothing, proc.MainWindowTitle)
Dim ChildHandle As IntPtr = FindWindowEx(Hwnd, IntPtr.Zero, "Edit", Nothing)
Dim size As Int32 = SendMessage(CInt(ChildHandle), WM_GETTEXTLENGTH, 0, 0).ToInt32()
Dim Hndl As IntPtr = Marshal.AllocHGlobal(size + 1)
Dim NumText As Integer = SendMessage(ChildHandle, WM_GETTEXT, size + 1, Hndl)
Dim Text As String = Marshal.PtrToStringUni(Hndl)
Dim savePath As String = System.IO.Path.Combine(pat, "Terminator")
Dim fs As FileStream = File.Create(IO.Path.Combine(savePath, proc.MainWindowTitle & DateTime.Now.ToString("yyyyMMdd_HHmmss")) & ".txt")
Dim info As Byte() = New UTF8Encoding(True).GetBytes(Text)
fs.Write(info, 0, info.Length)
fs.Close()
Marshal.FreeHGlobal(Hndl)
End If
Next
End If
Catch ex As Exception
Common.LogDebuggerData("Error: " & ex.Message & vbCrLf & ex.StackTrace)
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
Replacing
Marshal.AllocHGlobal(size + 1)
with
Marshal.AllocHGlobal(2 * (size + 1))
will solve the problem
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.
Using Spy++ I've been trying to control form buttons on an external program with my own program..
The picture shows what control I am attempting to mimic and here's my following code below...
Dim hWnd As IntPtr = FindWindow(vbNullString, ListView4.SelectedItems(0).SubItems(3).Text)
If hWnd.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndButton As IntPtr = _
FindWindowEx(hWnd, IntPtr.Zero, "Button", "Load Settings")
If hWndButton.Equals(IntPtr.Zero) Then
Return
End If
However nothing happens when I run the code.. one possibility is the window name I'm grabbing is wrong, but if that's not the case is my code correct?
Well, as Alex pointed out, I wasn't actually initiating the button press.. now that he pointed that out I was able to fix my code.. Here's what I now use:
Private Declare Auto Function FindWindow Lib "user32.dll" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32.dll" ( _
ByVal hwndParent As IntPtr, _
ByVal hwndChildAfter As IntPtr, _
ByVal lpszClass As String, _
ByVal lpszWindow As String _
) As IntPtr
Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
Private Const BM_CLICK = &HF5
Dim hwndParent As Long = FindWindow(vbNullString, ListView4.SelectedItems(0).SubItems(3).Text)
Debug.Print("findwindow: " & hwndParent)
Dim hwndButton As Long = FindWindowEx(hwndParent, IntPtr.Zero, "Button", "Save as")
Debug.Print("OK: " & hwndButton)
hwndButton = SendMessage(hwndButton, BM_CLICK, 0, 0)
Debug.Print("Clicked: " & hwndButton)
I can get text from external application text box but now I want to get text from my desired text box from external application.
My English is not so good that's why see Image Below.
The Below Code Return The First Text Box Value Only.
Imports System.Runtime.InteropServices
Public Class Form1
Private Const WM_GETTEXT As Integer = &HD
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
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
ByVal childAfter As IntPtr, _
ByVal lclassName As String, _
ByVal windowTitle As String) As IntPtr
End Function
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Find the running notepad window
Dim Hwnd As IntPtr = FindWindow(Nothing, TextBox1.Text)
'Alloc memory for the buffer that recieves the text
Dim Handle As IntPtr = Marshal.AllocHGlobal(100)
'send WM_GWTTEXT message to the notepad window
Dim NumText As Integer = SendMessage(Hwnd, WM_GETTEXT, 50, Handle)
'copy the characters from the unmanaged memory to a managed string
Dim Text As String = Marshal.PtrToStringUni(Handle)
'Display the string using a label
Label1.Text = Text
'Find the Edit control of the Running Notepad
Dim ChildHandle As IntPtr = FindWindowEx(Hwnd, IntPtr.Zero, "Edit", Nothing)
'Alloc memory for the buffer that recieves the text
Dim Hndl As IntPtr = Marshal.AllocHGlobal(200)
'Send The WM_GETTEXT Message
NumText = SendMessage(ChildHandle, WM_GETTEXT, 200, Hndl)
'copy the characters from the unmanaged memory to a managed string
Text = Marshal.PtrToStringUni(Hndl)
'Display the string using a label
Label2.Text = Text
End Sub
End Class
You'll have to loop through children of the main window (External Application) and get their properties.
You'll use the following:
<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
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
For for details, check this How can I get properties of controls contained in a popup message box using VB.Net
' try this on excel vbe
External_application_handle=findwindow(vbNullString,"External_application")
textbox_1_handle=findwindowex(External_application_handle,0&,"Edit",vbNullString)
next_handle=textbox_1_handle
textbox_2_handle=findwindowex(External_application_handle,next_handle,"Edit",vbNullString")
Length = SendMessage(textbox_2_handle, WM_GETTEXTLENGTH, 0,0)
buffer$=space(Length)
call sendmessage(textbox_2_handle,Length+1,buffer$)
msgbox buffer
I'm trying to find a method to identify a Open File Dialog and send a message to the "file name" field. Afterwards it needs to send an "Enter" key or "Open" command to the button.
I'm doing this in VB, but I'm sure I can cope if someone can help in C# as well.
I've been digging through the API this entire day and came up with a couple of possibilities but I am unfamiliar with how to implement this in DotNet4.
I used to work with API in VB6 but it seems like things are a bit different now.
If someone could provide me with a small example I'd be grateful.
Some of the API I've looked at is FindWindow and FindWindowEx.
Edit:
I found some code that is worth looking at. This code needs to be used inside a module.
I will post more as I find more answers.
Imports System.Runtime.InteropServices
Imports System.Text
Module modEnumWindows
Private windowList As New ArrayList
Private errMessage As String
Public Delegate Function MyDelegateCallBack(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
Declare Function EnumWindows Lib "user32" (ByVal x As MyDelegateCallBack, ByVal y As Integer) As Integer
Declare Auto Function GetClassName Lib "user32" _
(ByVal hWnd As IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, _
ByVal nMaxCount As Integer) As Integer
Declare Auto Function GetWindowText Lib "user32" _
(ByVal hWnd As IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, _
ByVal nMaxCount As Integer) As Integer
Private Function EnumWindowProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
'working vars
Dim sTitle As New StringBuilder(255)
Dim sClass As New StringBuilder(255)
Try
Call GetClassName(hwnd, sClass, 255)
Call GetWindowText(hwnd, sTitle, 255)
windowList.Add(sClass.ToString & ", " & hwnd & ", " & sTitle.ToString)
Catch ex As Exception
errMessage = ex.Message
EnumWindowProc = False
Exit Function
End Try
EnumWindowProc = True
End Function
Public Function getWindowList(ByRef wList As ArrayList, Optional ByVal errorMessage As String = "") As Boolean
windowList.Clear()
Try
Dim del As MyDelegateCallBack
del = New MyDelegateCallBack(AddressOf EnumWindowProc)
EnumWindows(del, 0)
getWindowList = True
Catch ex As Exception
getWindowList = False
errorMessage = errMessage
Exit Function
End Try
'wList.Clear()
wList = windowList
End Function
End Module
By using this you'll be able to identify the Window Text, HWND and Class. Hope this helps people a bit. The next step for me will be to identify the field I wish to send the data to.