[RESOLVED] Trouble extracting Checkbox State from external application
I am trying to determine the state of a checkbox in an external program from a VB.Net WinForm application. I used Spy++ to get the handle of the checkbox. I convert the handle from hex to decimal, and enter it into a textbox (I automatically extract the handle in my full application). I can toggle the checkbox from my program, so I know i have the correct handle. However I can not seem to GETSTATE or GETCHECK using SendMessage. I was not sure which I should be using, but they both always return 0. Here is my module code:
Code:
Imports System.Runtime.InteropServices
Module Module1
Public Const BM_CLICK = &HF5
Public Const BM_GETCHECK = &HF0
Public Const BM_GETSTATE = &HF2
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Sub toggle(hwnd As IntPtr)
SendMessage(hwnd, BM_CLICK, 0, 0)
End Sub
Function GetCheck(hwnd As IntPtr) As Integer
Return SendMessage(hwnd, BM_GETCHECK, 0, 0)
End Function
Function GetState(hwnd As IntPtr) As Integer
Return SendMessage(hwnd, BM_GETSTATE, 0, 0)
End Function
End Module
and my form code
Code:
Public Class Form1
Private Sub btnToggle_Click(sender As Object, e As EventArgs) Handles btnToggle.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
toggle(hwnd)
End Sub
Private Sub btnGetStatus_Click(sender As Object, e As EventArgs) Handles btnGetStatus.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblState.Text = GetState(hwnd).ToString
End Sub
Private Sub btnGetChk_Click(sender As Object, e As EventArgs) Handles btnGetChk.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblCheck.Text = GetCheck(hwnd).ToString
End Sub
End Class
Re: Trouble extracting Checkbox State from external application
Originally Posted by John_SC
I am trying to determine the state of a checkbox in an external program from a VB.Net WinForm application. I used Spy++ to get the handle of the checkbox. I convert the handle from hex to decimal, and enter it into a textbox (I automatically extract the handle in my full application). I can toggle the checkbox from my program, so I know i have the correct handle. However I can not seem to GETSTATE or GETCHECK using SendMessage. I was not sure which I should be using, but they both always return 0. Here is my module code:
Code:
Imports System.Runtime.InteropServices
Module Module1
Public Const BM_CLICK = &HF5
Public Const BM_GETCHECK = &HF0
Public Const BM_GETSTATE = &HF2
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Sub toggle(hwnd As IntPtr)
SendMessage(hwnd, BM_CLICK, 0, 0)
End Sub
Function GetCheck(hwnd As IntPtr) As Integer
Return SendMessage(hwnd, BM_GETCHECK, 0, 0)
End Function
Function GetState(hwnd As IntPtr) As Integer
Return SendMessage(hwnd, BM_GETSTATE, 0, 0)
End Function
End Module
and my form code
Code:
Public Class Form1
Private Sub btnToggle_Click(sender As Object, e As EventArgs) Handles btnToggle.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
toggle(hwnd)
End Sub
Private Sub btnGetStatus_Click(sender As Object, e As EventArgs) Handles btnGetStatus.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblState.Text = GetState(hwnd).ToString
End Sub
Private Sub btnGetChk_Click(sender As Object, e As EventArgs) Handles btnGetChk.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblCheck.Text = GetCheck(hwnd).ToString
End Sub
End Class
Any help would be appreciated
Not sure whether this is useful in your case, but I generally always check for errors after calling an API function. Try calling the System.Runtime.InteropServices.Marshal.GetLastWin32Error() function immediately after calling the PostMessage API function to get the error code of any error that might have occurred. You can get a description for the error code as follows:
Code:
Dim Message As String = New System.ComponentModel.Win32Exception(error code).Message
Hope this helps.
Last edited by Peter Swinkels; Jan 3rd, 2017 at 09:38 AM.
Re: Trouble extracting Checkbox State from external application
Thanks for the suggestion. When I checked for any errors, it came back saying it completed successfully, but still had values of zero in all cases.
So I went looking for another checkbox to test this on, and found one in the Windows File Manager. When I ran the same code on that GetChecked came back with 1 for checked and 0 for unchecked. GetState came back with 9 for checked and 8 for unchecked. So the code works.
Note, the target application was created in visual Basic specifically to test this functionality. Apparently, GetChecked and GetState don't work with the checkbox in VS 2013 v 4 with .Net Framework 4.6. This is odd since BM_Click does work.
According to https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx, BM_Check will only work with "button created with the BS_AUTOCHECKBOX, BS_AUTORADIOBUTTON, BS_AUTO3STATE, BS_CHECKBOX, BS_RADIOBUTTON, or BS_3STATE style. ... If the button has a style other than those listed, the return value is zero." So I am guessing the checkbox I am trying to automate is not one of these.
Any suggestions as to how I can determine the check state? I have seen a number of posts about Windows UI Automation, but I have not invested the time to figure that out yet.
Re: Trouble extracting Checkbox State from external application
I got something to work, but it is a bit odd.
* As written in VB, I was unable to toggle either a checkbox or a radio button in my test application.
* I went in a set the button style to one of the button styles that are supported. I specified that the control should be redrawn, but nothing obvious changed when I did this.
* When I changed a checkbox style to BS_CHECKBOX, I still was unable to retrieve its value.
* When I changed either the radio button or the check box to BS_RADIOBUTTON, I was able to extract the check state.
* The check box still looked and worked like a checkbox (Very ODD)
* The radio button still looked and worked like a radio button.
Here is the module code I used:
Code:
Imports System.Runtime.InteropServices
Module Module1
Public Const BM_GETCHECK = &HF0
Public Const BM_SETCHECK = &HF1
Public Const BM_GETSTATE = &HF2
Public Const BM_SETSTATE = &HF3
Public Const BM_SETSTYLE = &HF4
Public Const BM_CLICK = &HF5
Private Const BST_UNCHECKED = &H0&
Private Const BST_CHECKED = &H1&
Private Const BST_INDETERMINATE = &H2&
Private Const BST_PUSHED = &H4&
Private Const BST_FOCUS = &H8&
Private Const BS_CHECKBOX = &H2
Private Const BS_RADIOBUTTON = &H4
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Sub toggle(hwnd As IntPtr)
SendMessage(hwnd, BM_CLICK, 0, 0)
End Sub
Function GetCheck(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_GETCHECK, 0, 0)
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
Function GetState(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_GETSTATE, 0, 0)
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
Function Setstate(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_SETSTATE, BST_UNCHECKED, 0)
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
Function Setcheck(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_SETCHECK, BST_UNCHECKED, 0)
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
Function MakeCheckbox(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_SETSTYLE, BS_CHECKBOX, CInt(True))
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
Function MakeRadiobutton(hwnd As IntPtr) As Integer
Dim x, y As Integer
x = SendMessage(hwnd, BM_SETSTYLE, BS_RADIOBUTTON, CInt(True))
y = System.Runtime.InteropServices.Marshal.GetLastWin32Error()
Dim Message As String = New System.ComponentModel.Win32Exception(y).Message
Return x
End Function
End Module
And here is the Class Code
Code:
Public Class Form1
Private Sub btnToggle_Click(sender As Object, e As EventArgs) Handles btnToggle.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
toggle(hwnd)
End Sub
Private Sub btnGetStatus_Click(sender As Object, e As EventArgs) Handles btnGetStatus.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblState.Text = GetState(hwnd).ToString
End Sub
Private Sub btnGetChk_Click(sender As Object, e As EventArgs) Handles btnGetChk.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblCheck.Text = GetCheck(hwnd).ToString
End Sub
Private Sub Load_Click(sender As Object, e As EventArgs) Handles Load.Click
TextBox1.Text = Val("&H" & TextBox2.Text.Trim).ToString
End Sub
Private Sub btnSet_Click(sender As Object, e As EventArgs) Handles btnSet.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
lblset.Text = SetCheck(hwnd).ToString
End Sub
Private Sub btnMakeCBX_Click(sender As Object, e As EventArgs) Handles btnMakeCBX.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
Dim s As String = MakeCheckbox(hwnd).ToString
End Sub
Private Sub btnMakeRBN_Click(sender As Object, e As EventArgs) Handles btnMakeRBN.Click
Dim hwnd As IntPtr = CType(TextBox1.Text, IntPtr)
Dim s As String = MakeRadiobutton(hwnd).ToString
End Sub
End Class
Re: [RESOLVED] Trouble extracting Checkbox State from external application
I thought I would make it easier for anyone interested and uploaded the application. See the attached zip file.
I added some instructions on the form and cleaned up the code a little, so it is slightly different that what I posted earlier (added ability to SetCheck either on or off, and changed the name of the Load button to eliminate a warning).
I hope you find this useful.