How to scroll the scrollbar in external application via WinAPI? - winapi

Description
I'm trying to test application coded in Delphi (VCL components by DevEx) with TestComplete. Application is built without debug info.
I need to scroll TcxTreeList component. The problem is when I set Position property for this component's scrollbars content is not scrolled but scroll bar position changes. I tried a lot of approaches and suppose that WinAPI can help me.
The Question:
How to scroll the scrollbar in external application via WinAPI?
I found PostMessage function, but I do not know how to synthesize WM_SCROLL message...

Scroll one line down (you can see other constants at the page JustBoo mentions);
PostMessage(HWnd, WM_VSCROLL, SB_LINEDOWN, 0)
Scroll to a specific position;
PostMessage(HWnd, WM_VSCROLL, MakeWParam(SB_THUMBPOSITION, 30), 0)
But if you refer to this page on the Devex forums, it is mentioned that
"ScrollBar in the cxTreeList it is another control, not standard windows scrollbar".
So it might not work. In this case you might want to try ScrollWindowEx coupled with setting the position as you already do.

These links should show how to setup the parameters to a PostMessage call for scrolling.
WM_HSCROLL Message
WM_VSCROLL Message

Related

How to get correct background and control colors in property pages?

I'm trying to handle background color properly in a dynamically generated property sheet in dynamically generated property pages in win32 api using MFC (though I expect my question is general, and not restricted to MFC, but since my code and examples use it, it's germane to my question anyway).
So we have a:
CPropertySheet
containing multiple
CPropertyPage
I generate the contents of any given page dynamically - from file resources using a custom dialog definition language - all irrelevant other than to say - a list of controls and their coordinates is created within a given page, and the page is resized to accommodate them. This logic is working beautifully.
However, what doesn't work is that the controls and background of each page draws using the dialog default color/brush.
I've tried a number of ways to attempt to force it to draw using the white color/brush that a hard-coded property sheet / page would.
There are two important pieces to this:
Page Background
Control (on the page) background
For #1, I've tried:
acquire the background brush from parent window class (it's dialog bkgrd) (same is true if I do this and ask the tab control)
change the property page to use WS_EX_TRANSPARENT (PreCreateWindow is not called by the framework when generating a page viz PropertySheet::AddPage)
For #2, I've tried:
overriding OnWndMsg / WM_CTLCOLORSTATIC to forward that request to (A) the parent (sheet), and (B) to the tab control (which is what wants the white in the first place).
However, anytime I use any of the above "ask for the background / forward the request" up the chain to either the sheet or the tab control - I get the dialog background color, never the white I'd expect.
Using Spy64, I can see that for a fully hardcoded property sheet / page - that the only discernable differences I can see is that the dialog window created in AddPage (or its moral equivalent) has WS_CHILD instead of mine which has WS_POPUP (the rest of the styles appear to be the same, such as WS_VISIBLE|DS_3DLOOK|DS_FIXEDSYS|DS_SETFONT|DS_CONTROL and WS_EX_CONTROLPARENT.
So, other than the WS_CHILD, I see no significant differences from what I'm creating and from another property sheet that works properly from a standard resource (i.e. hard coded).
I'm also flummoxed as to how this works normally anyway - since forwarding things like the ctrlcolor message doesn't respond correctly - and asking for the windows background colors similar doesn't - then how is it in a standard case the background colors of controls and pages comes out as white, and not dialog background?
Any ideas or help would be appreciated - I'm kind of running out of ideas...
When Visual Styles were added in Windows XP they really wanted to show off this new feature so they made the tab background a gradient (really a stretched image) instead of a single color and this caused problems in old applications that did custom drawing with the dialog brush as the background.
Because of this, only applications with a comctl32 v6 manifest got the new look but there was a problem; old propertysheet shell extensions would load in new applications (including Explorer) and things would look wrong.
To work around this they also require you (or your UI framework) to call EnableThemeDialogTexture(.., ETDT_ENABLETAB) to get the correct tab page look.
As if things are not tricky enough, there is a undocumented requirement that you also need a button or a static control on the page!
If you have custom controls they have to call DrawThemeParentBackground when you draw if they are partially transparent.
Turns out my old code had defined an ON_WM_ERASEBKGND handler - and removing that (and all of my above attempts) makes it work.
So simply doing NOTHING is the correct answer. D'oh!!!
I'm leaving my shame here in case someone else trips on this! [Whoops!]
(Still interested if anyone has deeper insight into how this mechanism works under the hood)

Auto resize a dialog control when parent window is resized WinApi

I am trying to show a web page in a dialog control. All is working fine till I maximize the parent window, the inner control with webpage retains its size and so a blank area is left at the side of window. I want to know is there any WS_* message or something I can use to auto resize control when we resize the main window. I am using resource hacker so may be there can be some trick I should know.
Any help is appreciated.
Thanks.
The application must perform the resize. And it does so when it receives a WM_SIZE message for the parent control.
You are not going to be able change this by modifying the resources in a pre-existing binary. You are going to need to write some code to respond to that message.
The way I'd try seeing as I've never tried to hack something like this in.
Would be to see if the web control had an anchors property.
Its should be being passed a wm-size message, it's just not doing anything with it.
If you anchor all four corners of the webcontrol it should resize relative to it's parent.
The other way this is done is through explicit code that handles a resize event, don't think you could hack that in very easily though.

Tooltip only shows when running from source

I have a hierarchical flexgrid control with the ToolTipText property set, and when I run from source the tooltip displays as it should. But when I compile it and run that way, the tooltip doesn't display.
I've tried to remove anything listening to MouseMove in the hopes that that would fix it, and when I add some code to put the tooltip text into a message box, it appears to be set correctly. Can anyone think of why this would be happening?
Update: It appears that the problem arises when I host the grid inside another user control. E.g.: make container.ctl, which is just a blank control but with ControlContainer = True. Then make gridholder.ctl, which is a mshfg inside of a container.ctl. Lastly, embed gridholder.ctl into some form. Tooltips on the flexgrid don't appear to show up.
I'm interested to see how reproducible this is...
I haven't found a workaround for this issue yet, but I have a better idea of why it's happening after some testing and stepping through some of the VB6 runtime code in WinDBG.
The first interesting thing is that VB6 doesn't use the standard tooltip display mechanisms provided by Windows. For example, it doesn't use WM_NOTIFY messages to show/hide tooltips, or any of the other "standard" tooltip support described in the documentation explaining how tooltips work in Windows.
Instead, the VB6 runtime has its own way of managing and displaying tooltips. In principle, it's similar to in some ways to the standard Windows way of dealing with tooltips, but it's also different in a quite a few areas.
A breakdown of how VB6 does tooltips:
When a VB6 program starts, the runtime uses SetWindowsHookEx to install a mouse hook for the program's main thread.
The mouse hook intercepts all mouse messages sent to the program, in particular all WM_MOUSEMOUSE messages
Whenever the mouse hook runs, it calls an internal method in the VB6 runtime to get the object pointer (HCTL) of the control that the mouse is currently over top of. Note that this is an actual COM interface pointer, not a window handle.
It translates the HCTL to the corresponding window handle (HWND).
It checks to see if the mouse position is within that window's rectangle.
If so, it retrieves the ToolTipText property for the control. If this is not empty, it creates a tooltip window and displays the tooltip after a 700ms delay.
The problem with the MSHFlexGrid (and I imagine other controls that are not standard VB6 controls) is that this code doesn't retrieve the correct HCTL when you hover over the control and it's inside a custom container.
In that case, the code retrieves the HCTL of the custom container, not the HCTL of the MSHFlexGrid itself. Therefore, it retrieves the container's ToolTipText property (which is empty) and not the grid's ToolTipText, and therefore won't display a tooltip.
I'm not sure exactly why it does this, because as noted in the comments on your question, all of this works correctly if you use a PictureBox as your container.
I suspect the PictureBox has code to handle this correctly that is not included when you create your own container.
I'll update this answer with an actual workaround if I can find one. The only thing I can think right now is to somehow "sync" your container's ToolTipText property with the grid's ToolTipText property, so that when VB6 requests the container's ToolTipText, it will return the value of the grid's ToolTextTip property instead.
This is easier said than done, however, because ToolTipText is an extender property, and extender properties take precedence over properties that you write yourself that have the same name.
After a bit of research, I found what I think is the underlying problem. Your user control is not implementing any method for the controls to interact with. User Controls that are Container Controls need to implement the Extender functionality. These two links are the best I've found on the subject so far.
http://www.justvb.net/obook/ch7.htm#UsingtheExtenderObject
http://msdn.microsoft.com/en-us/library/aa733622(v=vs.60).aspx

How to Get the Active ChildWindow of an application?

I have this problem. I have an handler to the mainWindow of a certain application, and I want to simulate a keypress on that application...
I'm using sendMessage/postMessage api calls to do this. The reason why I don't use the .Net SendKeys function or the keybd_event of the win32 api, is that they simulate the keypress at a global level. In my case, I may have the application minimized and still want the keypress to be simulated.
The problem with sendMessage and postMessage is that you must pass the handler of the exact childwindow where you want the key to be pressed. For example, in notepad, if I send the key to the mainWindow, nothing happens, I have to send the key to the child window that basically consists of the white canvas where you can write.
With msPaint for example, if a user creates a new document, and opens a textbox in that drawing, and I want to simulate a keypress there, I have to get the childwindow of the childwindow of the mainwindow for it to works.
So I found a way that seemed to work for every situation, basically, I used getWindow with the parameter GW_CHILD, to get the child-window with the highest z-value. Then I do it again for the child window and continue doing it until a certain childWindow has no more childWindows..
And it seemed to work and I was very happy!
However... I found cases where this does not work. Firefox is one of them. Firefox has the mainWindow, and then has a childWindow that's pretty much the same as the mainWindow and then it has another childWindow which is the website area, ie, the area under the address bar and menus. If I am on www.google.com for example, and I want to simulate a keypress in the focused search box, it works, cause getting the child-window of the child-window gives me the correct childWindow. However, if the user clicks on the address bar for example, nothing changes in the way the getWindow works. It will still eventually get the childwindow that's under the address bar, doing nothing, instead of simulating the keypress on the address bar.
The thing is that I haven't found a way of getting the active child window of a certain application. I can only use the GetWindow method to get the child window of a certain window and do it until I find a child window with no childs. However, as you've seen in the firefox case, the active window is actually the parent of the child window that I get in the end.
I've tried other api calls like getTopWindow but I had no luck..
Anyone can put some light on this issue?
Thanks!
If the application violates the windowing rules of windows, you'll need an exception.
In Mozilla, it's like this (IIRC):
There's this 'god' window of the class MozillaUIWindowClass and with the "- Mozilla Firefox" string in its window text.
If you know the position of the address bar you can use the following function:
And provide it with the HWND of the 'god' window and the position of the address bar.
HWND ChildWindowFromPoint(HWND, POINT);
There is probably a better solution, I came up with this since I needed to automate mouse, which is position based.
For more information you might need to consult the sources of particular software, or spend whole day in Spy+. :>
You can use GetGUIThreadInfo to get info about the UI of a particular process.
If you have the main window you can call GetWindowThreadProcessId to obtain the process thread id. Then you can call GetGUIThreadInfo to get info about the active/focused windows, etc.
I also have to point that some applications only have one window and all its controls are windowsless (like Windows Live Messenger).

How can I make a systray (notification area) icon receive WM_MOUSEWHEEL messages?

I want to extend an existing application I made to make it set mixer volume by wheel-scrolling over it's notification area icon.
As far as I know, the notification area doesn't receive any WM_MOUSEWHEEL messages, but still I found an application that does exactly what I want to achieve (http://www.actualsolution.com/power_mixer).
Using WinspectorSpy I've noticed some strange messages the application's form receives: 0x000003d0 and 0x000003d1, but I found no references about them.
Does anyone have any idea on how I could achieve the desired functionality?
If you want to capture mouse/keyboard events outside of your application you will need Low-level Hooks.
A nice beginners article about installing a mouse hook in Delphi is How to Hook the Mouse to Catch Events Outside of your application on About.com written by Zarko Gajic.
The user which starts your application will need administrative rights to install a hook.
After you capture the message you should determine if it's above your icon in the notification bar (which can be difficult because there is no exact api to get your position on the bar) and than process the scroll event.
I explained about the mouse hooking, and mentioned it could be difficult to locate your exact icon. I did found the following article about how to locate a tray icon.
CTrayIconPosition - where is my tray icon? by Irek Zielinski. I think if you try to understand how it works you can turn it around and use it to check if your mouse is currently positioned above your icon.
But you should first check if the mouse is even in the tray area. I found some old code of mine (2005) which locates the correct region.
var
hwndTaskBar, hwndTrayWnd, hwndTrayToolBar : HWND;
rTrayToolBar : tRect;
begin
hwndTaskBar := FindWindowEx (0, 0, 'Shell_TrayWnd', nil);
hwndTrayWnd := FindWindowEx (hwndTaskBar , 0, 'TrayNotifyWnd',nil);
hwndTrayToolBar := FindWindowEx(hwndTrayWnd, 0, 'ToolbarWindow32',nil);
Windows.GetClientRect(hwndTrayToolBar, rTrayToolBar);
end
Using this piece of code and the knowledge from the mentioned article I think you could implement the functionality that you wanted.
Not sure if this will solve the problem but it might be worth a try as a starting point. You could create a top level transparent window that you then position over the top of the taskbar icon. That top level window will receive mouse notifications when the mouse is over it. You can then process them as required. How to find the screen location of the taskbar icon is something I do not know and so that might be an issue.
Id don't know if this will help you, but in Delphi the standard Message library states:
WM_MOUSEWHEEL = $020A;
It would also be helpful if you let as know which language you are using.
Just thought I'd point out that Power Mixer is capturing scroll wheel events on the whole taskbar, while the mouse middle click operates just on the sys tray icon.

Resources