[RESOLVED] Identify enabled state of button in another app
I have the hwnd of a button in another app. I click it using PostMessage, and it works like gangbusters. The problem is that PostMessage won't care whether the button is enabled or not; it'll assume it worked even when disabled.
So I need to know how to tell if a button is enabled or not using its hwnd. Any help?
Re: Identify enabled state of button in another app
PostMessage is used when you don't want to know the answer. SendMessage will wait until the message is processed and you should get some kind of reply. So try SendMessage instead and see if the return value changes.
Other than that you could try checking for the window style of the button, WS_DISABLED and so on.
Re: Identify enabled state of button in another app
I don't really know what I'm doing when it comes to automation. Here's my automation module:
Code:
Option Explicit
Public Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer, ByVal lParam As Any) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private mstrButton As String
Private mlngButton As Long
Public Function ButtonExists(pstrWindow As String, pstrButton As String) As Boolean
Dim lngWindow As Long
Dim lngButton As Long
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
If mlngButton <> 0 Then ButtonExists = True
End If
End Function
Public Function ClickButton(pstrWindow As String, pstrButton As String) As Boolean
Const BM_CLICK As Long = &HF5&
Dim lngWindow As Long
Dim lngParam1 As Long
Dim lngParam2 As Long
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
If mlngButton <> 0 Then
PostMessage mlngButton, BM_CLICK, 0, 0&
ClickButton = True
End If
End If
End Function
Public Function CloseWindow(pstrWindow As String) As Boolean
Const WM_CLOSE = &H10
Dim lngWindow As Long
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
SendMessage lngWindow, WM_CLOSE, 0, 0&
CloseWindow = True
End If
End Function
Public Function CloseWindowIfButtonExists(pstrWindow As String, pstrButton As String, pblnExists As Boolean, Optional pblnExact As Boolean = True) As Boolean
Const WM_CLOSE = &H10
Dim lngWindow As Long
Dim lngButton As Long
Dim blnExists As Boolean
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
blnExists = (mlngButton <> 0)
If blnExists = pblnExists Then
SendMessage lngWindow, WM_CLOSE, 0, 0&
CloseWindowIfButtonExists = True
End If
End If
End Function
Public Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim strCaption As String
strCaption = Space$(GetWindowTextLength(hwnd) + 1)
GetWindowText hwnd, strCaption, Len(strCaption)
strCaption = Left$(strCaption, Len(strCaption) - 1)
If strCaption = mstrButton Then
mlngButton = hwnd
Else
EnumChildProc = 1
End If
End Function
Public Function WindowIsOpen(pstrWindow As String) As Boolean
WindowIsOpen = (FindWindow(vbNullString, pstrWindow) <> 0)
End Function
I changed the PostMessage call to SendMessage in the ClickButton() function, but the results are weird.
The button gets half-clicked; the enabled properties of the buttons around it change as if it were clicked, but nothing else happens.
Is BM_CLICK only for PostMessage? Does SendMessage need a different value to click a command button?
Re: Identify enabled state of button in another app
Quote:
Originally Posted by Ellis Dee
So I need to know how to tell if a button is enabled or not using its hwnd. Any help?
Try changing Command1.hwnd in the code below to the buttons handle and see if that works.
Code:
Option Explicit
Private Const WS_DISABLED = &H8000000
Private Const GWL_STYLE = (-16)
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Sub Command2_Click()
If (GetWindowLong(Command1.hwnd, GWL_STYLE) And WS_DISABLED) <> 0 Then
Debug.Print "Command1 is disabled."
Else
Debug.Print "Command1 is enabled."
End If
End Sub
Re: Identify enabled state of button in another app
Perfect, thanks. Here's my new and improved automation module. It now checks if a button is enabled before clicking it in the ClickButton() function. Plus I added a new function: ButtonEnabled(), which returns a simple boolean matching the enabled state of a button.
Code:
Option Explicit
Public Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Private Declare Function IsWindowEnabled Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer, ByVal lParam As Any) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private mstrButton As String
Private mlngButton As Long
Public Function ButtonEnabled(pstrWindow As String, pstrButton As String) As Boolean
Const WS_DISABLED = &H8000000
Const GWL_STYLE = (-16)
Dim lngWindow As Long
Dim lngButton As Long
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
If mlngButton <> 0 Then ButtonEnabled = (IsWindowEnabled(mlngButton) = 1)
End If
End Function
Public Function ButtonExists(pstrWindow As String, pstrButton As String) As Boolean
Dim lngWindow As Long
Dim lngButton As Long
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
If mlngButton <> 0 Then ButtonExists = True
End If
End Function
Public Function ClickButton(pstrWindow As String, pstrButton As String) As Boolean
Const BM_CLICK As Long = &HF5&
Const WS_DISABLED = &H8000000
Const GWL_STYLE = (-16)
Dim lngWindow As Long
Dim lngParam1 As Long
Dim lngParam2 As Long
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
If mlngButton <> 0 Then
If IsWindowEnabled(mlngButton) = 1 Then
PostMessage mlngButton, BM_CLICK, 0, 0&
ClickButton = True
End If
End If
End If
End Function
Public Function CloseWindow(pstrWindow As String) As Boolean
Const WM_CLOSE = &H10
Dim lngWindow As Long
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
SendMessage lngWindow, WM_CLOSE, 0, 0&
CloseWindow = True
End If
End Function
Public Function CloseWindowIfButtonExists(pstrWindow As String, pstrButton As String, pblnExists As Boolean) As Boolean
Const WM_CLOSE = &H10
Dim lngWindow As Long
Dim lngButton As Long
Dim blnExists As Boolean
mstrButton = pstrButton
mlngButton = 0
lngWindow = FindWindow(vbNullString, pstrWindow)
If lngWindow <> 0 Then
EnumChildWindows lngWindow, AddressOf EnumChildProc, ByVal 0&
blnExists = (mlngButton <> 0)
If blnExists = pblnExists Then
SendMessage lngWindow, WM_CLOSE, 0, 0&
CloseWindowIfButtonExists = True
End If
End If
End Function
Public Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim strCaption As String
strCaption = Space$(GetWindowTextLength(hwnd) + 1)
GetWindowText hwnd, strCaption, Len(strCaption)
strCaption = Left$(strCaption, Len(strCaption) - 1)
If strCaption = mstrButton Then
mlngButton = hwnd
Else
EnumChildProc = 1
End If
End Function
Public Function WindowIsOpen(pstrWindow As String) As Boolean
WindowIsOpen = (FindWindow(vbNullString, pstrWindow) <> 0)
End Function
Re: [RESOLVED] Identify enabled state of button in another app
And for the record:
You must spread some Reputation around before giving it to Edgemeal again.
Re: [RESOLVED] Identify enabled state of button in another app
To check if button is enabled you can probably just use the simpler, IsWindowEnabled API call.
Re: [RESOLVED] Identify enabled state of button in another app
Quote:
Originally Posted by Edgemeal
To check if button is enabled you can probably just use the simpler, IsWindowEnabled API call.
Yeah, probably, but it's not like ANDing a state with a mask is involved. It's identifying a window by its caption that takes all the code.
EDIT: I went ahead and made that change, as it is definitely more elegant. I also edited the code above to reflect it for posterity.
Re: Identify enabled state of button in another app
Quote:
Originally Posted by Ellis Dee
The button gets half-clicked; the enabled properties of the buttons around it change as if it were clicked, but nothing else happens.
I was trying to use this same sort of code and seems if the button you want to click is the default button(/already has focus) bm_click doesn't work on the first try but does on the second try, was searching around the web and found a working solution so thought I'd post it in case you ran into the same problem. Before sending the BM_CLICK send a WM_ACTIVATE.
Code:
SendMessage ButnHandle, WM_ACTIVATE, MA_ACTIVATE, 0
SendMessage ButnHandle, BM_CLICK, 0, 0
EDIT
Actually that second parameter should be WA_ACTIVE, although it still has the same value (1) as MA_ACTIVATE, that constant is really used with the mouse messages.
Code:
SendMessage ButnHandle, WM_ACTIVATE, WA_ACTIVE, 0
SendMessage ButnHandle, BM_CLICK, 0, 0
HTH,
Ed