I'm using Excel(2003) VBA. I have "UserForm1" with a command button, "bHide". I'm using FindWindow to get the hwnd for UserForm1. With that I'm successfully changing the window style (removing the form border). I also want to modify "bHide" but I need to get its handle.
What's the best way to get the handle for "bHide"? Thanks.
Related
I have custom print property sheets/pages that have been added to the dialog displayed by PrintDlgEx. These property sheets are, of course, used to change additional options. The issue is that there does not appear to be any documented way to activate the Apply button from the property sheet's dialog function, or anywhere for that matter. This seems to be a huge omission on Microsoft's part.
Is there any "official" way to change the Apply button's state? If not, are there any possible workarounds?
Is there any "official" way to change the Apply button's state? If not, are there any possible workarounds?
Not directly, no. You would have to retrieve the button's HWND manually an then manipulate it as needed.
use SetWindowHookEx() to install a local WH_CBT hook for the thread that is calling PrintDlgEx(). The dialog's HWND will be available as a parameter of the callback function when it receives a HCBT_ACTIVATE notification. Then you can locate the Apply button's HWND within the dialog (use Spy++ or similar tool to get details about the button, then have your code use GetDlgItem() or FindWindowEx() to get the button's HWND). Be sure to call UnhookWindowsHookEx() after PrintDlgEx() exits (or at least after you are done using the button HWND).
use SetWinEventHook() to register for EVENT_OBJECT_CREATE, EVENT_OBJECT_SHOW, and/or EVENT_SYSTEM_DIALOGSTART notification(s) for the thread that is calling PrintDlgEx(). The dialog and button HWNDs will be available as a parameter of the callback function. Be sure to call UnhookWinEvent() after PrintDlgEx() exits (or at least after you are done using the button HWND).
Once you have the button's HWND, you can do whatever you want with it. It is a standard button control, so any standard button message/function can be used with it.
A more close to "official" way is to call PropSheet_Changed().
The way I get the property sheet dialog is to look at the source of PSN_ notifications sent to IPrintDialogCallback::HandleMessage(). Or you can use GetParent(GetParent(generalDialog)).
Once you call PropSheet_Changed() the Apply button will activate.
You're right, it seems to be a huge omission on Microsoft's part, as it's not a simple thing to code, but it's something that most people adding property sheets would need.
I can put some code up if anyone needs it.
Let's say I have a compiled binary program that runs and exposes some GUI on the screen.
I need from another program in Win32 to access its toolbar, find a button and click on it.
I know how to find Hwnd of the toolbar, but I don't really know how to enumerate the buttons on it.
Any ideas how to do it in Win32 calls?
Is there any tool like Spy++ that is capable of showing button handles under toolbar?
Spy++ doesn't do it.
Thanks
If it is a standard Win32 Toolbar control, then you send the toolbar a TB_BUTTONCOUNT message to determine how many buttons are on the toolbar, then send a TB_GETBUTTON message to retrieve information about a button at a given index.
The tricky part is that the TBBUTTON structure that receives the button info needs to be allocated in the same process that owns the toolbar, so you will have to:
call GetWindowThreadProcessId() to retrieve the toolbar's process ID
call OpenProcess() to get a HANDLE to that process
call VirtualAllocEx() to allocate the structure inside of that process
send the TB_GETBUTTON message(s) to the toolbar, specifying the pointer returned by VirtualAllocEx()
call ReadProcessMemory() to copy the structure data back into your own process so you can process it as needed
call VirtualFreeEx() to free the allocated memory.
When the button is clicked it sends a WM_COMMAND message to the main window. Simulating a click on a toolbar button is not very practical. A better approach is to use Spy++ to find the WM_COMMAND message and its parameters. Then in your program send this same WM_COMMAND message.
AFAIR you can get the HWND of a toolbar button by calling GetDlgItem. The first parameter is the HWND of the toolbar, the second parameter is the ID of the button (the one you set in its TBBUTTON structure). You need to have the button ID to use this approach.
=== EDIT ===
In addition to EnumChildWindows, suggested by #graham.reeds, you can try SendInput. Move the target window to the foreground, calculate the screen coordinates of the upper-left corner of the toolbar (using its HWND), add the X-Y offset of the middle of the target button, and send a mouse click to that position. (I used this approach successfully to click on Flash and Silverlight objects rendered inside the IE window.)
Off the top of my head:
Use EnumChildWindows to find the child controls of the toolbar.
Then use GetWindowText to see if it is a button.
If it is PostMessage to it to invoke it's operation.
Im trying to scroll other program (PowerPoint 2013) by sending WM_HSCROLL,
and it will work only if I offer the correct LPARAM (not NULL) to SendMessage.
The value of LPARAM is dynamic, it will change if you close and open a new program.
Now I can only get its value by hooking WM_HSCROLL and clicking scroll bar manually.
// This is my code:
LPARAM lParam = 0x0a616c38; // Found by hooking WM_HSCROLL
SendMessage(hTarget, WM_HSCROLL, SB_LINERIGHT, lParam);
So is it possible to get the correct LPARAM programatically?
Many thanks!
p.s. The scroll bar is not a real window in PowerPoint 2013.
p.s. It returns 1 if I use GetScrollInfo(hTarget, SB_CTL, &scrollinfo), but all values inside scrollinfo are zero.
p.s. Office Home and Student 2013 Official Site
Did you try to call GetLastError?
GetScrollInfo will probably not work across process boundaries, so I would say that's why you're not getting valid values back.
The lParam value of a WM_HSCROLL message is either NULL for the standard window scroll bar, or the handle of the scroll control. The handle of the scroll control will obviously change every time the program is run, so you need to find that out yourself before you can reliably simulate scroll input.
To do this, you can use the FindWindowEx function to search the parent window (hTarget in your example) for child windows of class "SCROLLBAR". As you will probably find more than one scrollbar child window you'll need some way to tell them apart - most probably, via the window's GWL_ID value as this will probably not change from run to run.
I am using spy++ and see that the control I have has the decimal that matches the hex(after conversion of course) in spy++ and I see the parent window matches as well so I have the IntPtr for a Label and IntPtr for the form/window but my SendMessage is not working to change the text in the target application.
Another approach may be may be to do something like this post but what is the control id and how do I get that
SetText of textbox in external app. Win32 API
I assume the hWnd here needs to be the controls hWnd, correct?
SendMessageCall(hWnd, WM_SETTEXT, (IntPtr)value.Length, value);
I notice that getting the text IS WORKING
SendMessageCall(hWnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
and I notice that I get the test, see the correct value, set the text yet it doesn't change and then get the text again using SendMessage AND it is the new value but the application still shows the wrong value....hmmm, do I need to send a repaint message maybe and if so, what is the code for that?
thanks,
Dean
You don't send a window message to force repaint, instead you call InvalidateRect(hWnd, NULL, TRUE).
Ho do I get hWnd of the current window/form in VB6?
If you're on the form: Me.hWnd. If you don't know which form is the current form: Screen.ActiveForm.hWnd
Using Windows API, GetForegroundWindow() will get the handle of the topmost window regardless of which application it is from, and GetActiveWindow() will get the handle of your application's active window. The Declare statements you will need:
Declare Function GetForegroundWindow Lib "user32.dll" () As Long
Declare Function GetActiveWindow Lib "user32.dll" () As Long
Calling either function will return a window handle as described above.
It's been a long time since I used VB6, but this is what I remember:
You'll want to open the API Viewer, which should be in the Start Menu around the VB6 entry. When you open it, you want to select win32api.txt, and you'll get a list of all of the Win32 API functions. This is the easiest way to not mess up the function signatures. Copy and paste the function declaration into one of your VB6 modules.
I always "cheated" and just looked for my window by caption name, instead of looping over all of the available windows with GetWindow. If you're okay with this, you want to use FindWindow and pass the caption name as the second parameter.