Question Getting handle using win32 api

matthugh1

New member
Joined
Jan 19, 2012
Messages
3
Programming Experience
Beginner
I am new to the forum so I hope I am doing things correctly. I am also new to VB.net development. I am hoping someone can help me.

I am trying to create a small utility that tracks desktop usage in detail, to the level of individual text boxes etc.... in other applications. However, it is proving quite tricky. I thought I had it nailed using the Windows api. The below code is just a snipet of the code but essentially it provides me the handle of the control that the mouse is over. I thought from here, it would be easy to to the same thing but to provide the handle of the control that the cursor was actually clicked on, for example, if someone is filling in a text box in an application, they would not necessarily move the mouse to get to the text box, they may tab to it. Does anyone know if there is a way to get the handle of the control that is in use?

VB.NET:
Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick


        Dim ptx As Integer = Cursor.Position.X
        Dim pty As Integer = Cursor.Position.Y
        Dim OnhookWindow As IntPtr = WindowFromPointXY(ptx, pty)
        Dim intClassName As New StringBuilder("", 256)
   
        If Not OnhookWindow = Me.Handle Then
            Label1.Text = OnhookWindow 
            
       End If
        
    End Sub
 
GetForegroundWindow and GetFocus functions is a start.

A C# sample is posted here Copy Selected Text from any window
You can pretty much run that code through a VB.Net-C# online code translator and it will work out of the box.
 
Thanks JohnH, but unless I am doing something really wrong, GetForegroundWindow only ever returns the handle for active window within my project, if focus is on any other window, the GetForegroundWindow shows as 0. I converted the code to VB, thanks for that.

VB.NET:
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
    <DllImport("user32.dll")> _
    Public Shared Function GetCursorPos(ByRef pt As Point) As Boolean
    End Function
    <DllImport("user32.dll", EntryPoint:="WindowFromPoint", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
    Public Shared Function WindowFromPoint(pt As Point) As IntPtr
    End Function
    <DllImport("user32.dll", EntryPoint:="SendMessageW")> _
    Public Shared Function SendMessageW(<InAttribute()> hWnd As System.IntPtr, Msg As Integer, wParam As Integer, lParam As IntPtr) As Integer
    End Function
    Public Const WM_GETTEXT As Integer = 13
    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
    Friend Shared Function GetForegroundWindow() As IntPtr
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
    Friend Shared Function GetFocus() As IntPtr
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Friend Shared Function GetWindowThreadProcessId(handle As Integer, ByRef processId As Integer) As Integer
    End Function
    Friend Shared Function AttachThreadInput(idAttach As Integer, idAttachTo As Integer, fAttach As Boolean) As Integer
    End Function
    <DllImport("kernel32.dll")> _
    Friend Shared Function GetCurrentThreadId() As Integer
    End Function
    <DllImport("user32", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Friend Shared Function GetWindowText(hWnd As IntPtr, <Out(), MarshalAs(UnmanagedType.LPTStr)> lpString As StringBuilder, nMaxCount As Integer) As Integer
    End Function
    Private _timer As New System.Windows.Forms.Timer()
    Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        'Initialize a timer to update the control text.
        _timer.Interval = 1000
        AddHandler _timer.Tick, New EventHandler(AddressOf _timer_Tick)
        _timer.Start()
    End Sub
    Private Sub _timer_Tick(sender As Object, e As EventArgs)
        Try
            TextBox1.Text = GetTextFromFocusedControl()
        Catch exp As Exception
            TextBox1.Text += exp.Message
        End Try
    End Sub
    'Start to monitor and show the text of the related control.
    Private Sub Button1_Click1(sender As Object, e As System.EventArgs) Handles Button1.Click
        _timer.Start()
    End Sub
    'Get the text of the focused control
    Private Function GetTextFromFocusedControl() As String
        Try
            Dim activeWinPtr As Integer = GetForegroundWindow().ToInt32()
            Dim activeThreadId As Integer = 0, processId As Integer
            activeThreadId = GetWindowThreadProcessId(activeWinPtr, processId)
            Dim currentThreadId As Integer = GetCurrentThreadId()
            If activeThreadId <> currentThreadId Then
                AttachThreadInput(activeThreadId, currentThreadId, True)
            End If
            Dim activeCtrlId As IntPtr = GetFocus()
            TextBox1.Text = activeCtrlId
            Return activeCtrlId
            'GetText(activeCtrlId)
        Catch exp As Exception
            Return exp.Message
        End Try
    End Function
    'Get the text of the control at the mouse position
    Private Function GetTextFromControlAtMousePosition() As String
        Try
            Dim p As Point
            If GetCursorPos(p) Then
                Dim ptr As IntPtr = WindowFromPoint(p)
                If ptr <> IntPtr.Zero Then
                    Return GetText(ptr)
                End If
            End If
            Return ""
        Catch exp As Exception
            Return exp.Message
        End Try
    End Function
    'Get the text of a control with its handle
    Private Function GetText(handle As IntPtr) As String
        Dim maxLength As Integer = 100
        Dim buffer As IntPtr = Marshal.AllocHGlobal((maxLength + 1) * 2)
        SendMessageW(handle, WM_GETTEXT, maxLength, buffer)
        Dim w As String = Marshal.PtrToStringUni(buffer)
        Marshal.FreeHGlobal(buffer)
        Return w
    End Function
 
End Class
 
GetForegroundWindow only ever returns the handle for active window within my project
Sure about that? It may be possible, but that is not what your code is returning, so you should clarify. I can confirm again the code I converted from article works as expected.

You should also add some error handling to the code, for example both GetWindowThreadProcessId and AttachThreadInput may also return 0, in which case there is no point in doing following code. If any of those problems happen you can narrow down exactly where the problem is. If AttachThreadInput is returning 0 you can call Err.LastDllError if you have Vista or newer.

One more thing, UAC may prevent you from working with handles and threads in other processes, so I'd include this in debugging and run as elevated admin to verify things are working first, then narrow scope if necessary/possible.
 
Thanks for all you rhelp, I did manage to work out a solution.


On Error Resume Next


'lasthwnd = cwnd


SendKeys.Flush()

cwnd = apiGetFocus


If cwnd <> 0 Then Return cwnd

hwnd = apiGetForegroundWindow()


If apiAttachThreadInput(apiGetWindowThreadProcessId(hwnd, 0), apiGetCurrentThreadId, 1) <> 0 Then

cwnd = apiGetFocus

apiAttachThreadInput(apiGetWindowThreadProcessId(hwnd, 0), apiGetCurrentThreadId, 0)


End If
 
Back
Top