using vb6 is it possible to click buttons and forms on another running process programmatically ?
Look at the SendMessage() API call. This is what Windows itself uses to notify a button it has been clicked.
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
E.g.
retval = SendMessage(hwndButton, BM_CLICK, ByVal CLng(0), ByVal CLng(0))
Tricky bit is getting the window handle of the button (hwndButton). FindWindow() and EnumChildWindows() APIs will do this. FindWindow() will return the handle of the top-level windows (e.g. Notepad). Then EnumChildWindows can be used to iterate the controls until the correct button is found.
Related
What I am doing is by using SetWindowLong I change an address of an original window procedure of a window(a form in my case) to an address of my own WindowProc, then I process one of system messages and finally I pass the intercepted message to the original window procedure.
I tested intercepting messages subclassing:
A window of MS Access instance
A window of a form in an overlapping interface
Both of the tests go well only if I run the main Access window without opening an IDE window before or during the test. The trouble happens if at the time of testing not only MS Access main window is open but a window of IDE too.
The problem doesn’t arise in a first case (intercepting messages of MS Access window). While as the second case is a pain in the neck indeed (intercepting messages of a form in an overlapping interface). What happens is MS Access stops responding to user’s input. It looks like it gets looping and looping endlessly. I’d like to stress once more that such behavior takes place only when MS Access window and window of IDE are open at the same time.
I made a test file so that you can see it for yourself. I uploaded it here:
https://www.dropbox.com/s/8lnqfgb1hn3zqy8/SetWindowLong.accdb?dl=0
So, the first case(when there is no problem):
Open the file (DO NOT open IDE window till the end of the test)
Open Form1(on opening the system messages to MS Access window will start being intercepted)
Close Form1(on closing intercepting will be stopped)
Open Form 2(on opening the system messages to Form2 window will get being intercepted)
Close Form2(on closing intercepting will be stopped)
So far so good.
Now the second case (the pain in the neck):
Open the file
Open IDE window (ALT+F11)
Open Form1(on opening the system messages to MS Access window will get being intercepted)
Close Form1(on closing intercepting will be stopped)
Open Form 2(on opening the system messages to Form2 window are intended to get being intercepted, but you will witness an endless looping and no response from Access)
And now are my questions:
How come an open window of IDE can interfere in my subclassing of Form2 and does not affect the subclassing of MS Access window.
What can be done so that I was able to subclass Form2 while the window of IDE stayed open
I will be very grateful for your advice.
Edited on 2017/08/25:
Sorry for not providing my code at the beginning.
In a standard module of MS Access:
Option Compare Database
Option Explicit
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public lngPrevWndProc As Long
Public Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WndProc = CallWindowProc(lngPrevWndProc, hWnd, uMsg, wParam, lParam)
End Function
In a code module of Form2:
Option Compare Database
Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_WNDPROC = -4
Private Sub Form_Open(Cancel As Integer)
lngPrevWndProc = SetWindowLong(Me.hWnd, GWL_WNDPROC, AddressOf WndProc)
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetWindowLong Me.hWnd, GWL_WNDPROC, lngPrevWndProc
End Sub
This sample intended to work on a 32bit Windows and in 32bit MS Access. I tested it on:
Windows XP Pro + MS Access 2000 (the file was converted to mdb); Windows 7 32bit + MS Access 2010 32bit (accdb); indows 10 32bit + MS Access 2016 32bit (accdb)
The result was the same as I described before.
Sergiy Vakshul
I've got a VB6 program. I'm using the Mainfest to apply "XP Themes" and give it the modern (as of 8 years ago!) look.
However, for graphical style Command buttons, I have to use some special code to redraw the button. Therein lies the problem.
When I click on one of this Graphical buttons it gets the proper "highlighting" of the background, but when another button gets the focus or mouseover, etc. that former button keeps the background highlighting.
If I move another window in front of it, the form redraws itself and this "residual" background color disappears.
I'm trying to figure out how to force that to happen.
What I've tried:
button.refresh
form.refresh
Doevents
Here is a video demo of the problem
I don't have anything like your setup to try this in, but you can try using the API call InvalidateRect. I've shown the declarations and created a Sub that uses it. It should be a simple copy and paste to try.
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Sub RefreshMe()
Dim udtRect As RECT
Call GetClientRect(Me.hwnd, udtRect)
InvalidateRect Me.hwnd, udtRect, 1
DoEvents 'give windows a chance to handle the event
End Sub
I'm wondering how I would be able to run an invisible form in the foreground (meaning it runs wherever the heck you might be in your computer). I've tried playing around with formless applications using modules but unfortunately nothing happens. I've also tried doing
Form1.Visible = False
Form1.WindowState = 2 (Maximized)
But it WILL NOT run in the foreground (It will if it's focused on) and also the Windows "warning error" sort of sound (the "ehh." sort of sound in monotone) keeps playing when you hit a key. The coding I tried out is this in case you want to know:
Private Sub Form1_KeyPress(KeyAscii As Integer, Shift As Integer)
If KeyAscii <> 0 Then PlaySound App.Path & "\sounds\ding.wav", ByVal 0&, SND_FILENAME Or SND_ASYNC
End Sub
And of course using the Windows APIs for playing wav files (winnm.dll or something I believe)
My main question would be (which means THIS is what I want answered) is how I could possibly make it run ANYWHERE on the computer and not make "ehh." monotone sounds. Because That is obviously not what I wanted to achieve.
Oh, and quick update, I've made it play the proper sound (HOORAY!). Just disregard the latter part of the question.
Please tell us why you want to do this?
What are you trying to accomplish?
Have a look at the following API:
'api for keeping form on top
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
For example call it as follows:
'put form always on top
With Screen
SetWindowPos Me.hwnd, -1, 0, 0, .Width, .Height, 3
End With 'screen
I am doing some web scraping using excel vba.
At one point in my program my internet explorer instance opens an OpenFileDialog. I need to access this dialog and provide it with a filename. Is there a way to do this in vba?
My idea was to get the window handle (I've already done that) and then somehow get the object using the handle, but i cannot find a way to use the handle to access the window.
if you have the hWind of the dialag box, then the function
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
will allow you to send keys to the application with
Debug.Print PostMessage(hWind, WM_KEYDOWN, vbKeyA, 0)
more info here
In the "Control Panel > Ease of Access Centre > Make the keyboard easier to use" is an option to "Underline keyboard shortcuts and access keys."
Is there a way of programmatically switching this on and off?
I'm using Visual Basic Scripts, but can use .NET.
Run Registry Editor and go to HKEY_CURRENT_USER\Control Panel\Accessibility\Keyboard Preference
Now create or modify a String Value (REG_SZ) called On and set its value to 1
Information is comming from:
http://www.windowsvalley.com/get-underlined-keyboard-shortcuts-and-access-keys-permanently/
AFAIK, there's no way to toggle this option programmatically except for automating the approproate GUI actions (opening the Control Panel, switching the option on/off and applying the changes). In this case, I'd recommend using AutoIt to automate the option switching.
It turns out you CAN programatically change the “underline keyboard shortcuts” option in your own application. You need to send the WM_UPDATEUISTATE message to your main form according to the documentation found at: https://learn.microsoft.com/en-us/windows/win32/menurc/wm-updateuistate
Since you mentioned Visual Basic, here's how to do it:
Private Const WM_UPDATEUISTATE = &H128
Private Const UIS_CLEAR = &H2
Private Const UISF_HIDEACCEL = &H2
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
Then in the "Form_Load" event send the message and it will activate keyboard shortcuts underlining for all controls and menus present on that form:
Private Sub Form_Load()
PostMessage Me.hWnd, WM_UPDATEUISTATE, UIS_CLEAR + UISF_HIDEACCEL * 65536, 0
End Sub