How to scroll active window? SendMessage?
Hi all. First post here for me.
Just a quick note: I'm not very good in VC++. In fact, I only started using it a few days ago. I have done some C++ coding in the past, but it was quite a while ago. I guess all that qualifies me as a newbie. I searched for similar issues to mine but nothing I found seemed to fit my problem.
On to the point: I'm trying to write a program that will scroll the current window of any* program in response to certain keyboard inputs. Essentially, I want certain keys on the keyboard to have the same** effect as clicking on the scroll bar arrows.
* Actually, it doesn't have to be absolutely any program. Just the standard windows stuff will be enough - IE, Word, Excel, etc.
** It doesn't have to behave exactly like a scroll bar arrows; the number of lines can be different. The important thing is I don't want the cursor (if any) to move, I just want the window to scroll.
I figured I could use SendMessage, but it doesn't seem to work. This is what I'm trying:
Code:
SendMessage(GetForegroundWindow(), WM_HSCROLL, SB_LINERIGHT, 0);
myFile << "Sent message to " << (int)GetForegroundWindow() << " msg = " << WM_HSCROLL << " wParam = " << SB_LINERIGHT << " lParam = " << 0 << "\r\n";
The second line is just outputting to a logfile so I can be sure the code is getting to that point, which it is.
Here's what I'm seeing in the logfile:
Quote:
Sent message to 12059976 msg = 276 wParam = 1 lParam = 0
The window handle changes as the current window changes, so it seems to be picking up the current window handle OK. The problem is, every program I've tried this with does nothing. Except MS Publisher, which does something very weird: It doesn't scroll the window contents, but forces the scrollbar all the way to the right where it gets "stuck", and doesn't move in response to clicking the arrows, although the window contents scroll.
This seems to tell me that Publisher is getting some kind of message related to the scroll bars, but is not interpreting it properly. Maybe the other programs are seeing some kind of invalid message as well, but simply ignore it completely?
In the end I have two questions:
What am I doing wrong?
Is SendMessage the best way to scroll windows?
Re: How to scroll active window? SendMessage?
WM_HSCROLL is a notification message! From MSDN:
Quote:
The WM_HSCROLL message is sent to a window when a scroll event occurs in the window's standard horizontal scroll bar.
You could try to use SetScrollInfo API (I am not sure though, it will work with the windows belonging to another process).
Note also that a lot of windows don't have scroll bars at all and some windows draw scrollbars themselves, so SetScrollInfo won't work for them.
Re: How to scroll active window? SendMessage?
Thanks. What led me to believe that this would work is this :
http://www.thescripts.com/forum/thread280461.html
Although I think Steve is trying to communicate within a process and I'm trying to communicate across processes.
I will try using SetScrollInfo.
Re: How to scroll active window? SendMessage?
Well, I am a stubborn SOB. I was convinced that SendMessage is indeed a feasible way to scroll windows.
It was an adventure, but after days of experimenting...
Turns out I was right. The problem lies in getting the correct window handle. GetForegroundWindow() only returns the top-level window's handle, but what I need is the handle for the child window that has keyboard focus and owns the scrollbar. The top-level window doesn't own the scrollbars so it doesn't do anything upon receiving the scroll message.
The way to get the child window with focus is GetFocus() but the problem with that is that it doesn't work if the active window is owned by a different thread (Good Lord, that is stupid behavior), unless I attach my thread's message queue to that thread.
So in the end, I have to attach to the thread, get the handle of the active child window, detach from the thread (since I expect the active window to change), then finally send the message to the correct window. Here's my code, which scrolls to the right:
Code:
AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(GetForegroundWindow(), NULL), TRUE); //attach this thread's input to the active window's thread
HWND WindowToScroll = GetFocus(); //get the handle of the child window with focus
AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(GetForegroundWindow(), NULL), FALSE); //detach from the active window's thread
SendMessage(WindowToScroll, WM_HSCROLL, SB_LINERIGHT, (LPARAM)0); //send the scroll message
I'm wondering if there is any less kludgy way to get the handle to the child window which has the keyboard focus. While my code works, I'd really like a neater solution. Anyone?
Re: How to scroll active window? SendMessage?
Feels a bit like I'm talking to myself.
I think I found a better way of getting the window handle I need. The function to use is GetGUIThreadInfo().
Code:
GUITHREADINFO gti;
gti.cbSize = sizeof(GUITHREADINFO);
GetGUIThreadInfo(NULL, >i);
WindowToScroll = gti.hwndFocus;
The declaration and size setting only need to be done once in the program execution, so only the last two lines need to be called every time the handle needs to be used. Calling GetGUIThreadInfo with NULL as the first parameter obviates the need for a call to GetForegroundWindow(), and all that thread attaching crap is completely gone.
Re: How to scroll active window? SendMessage?
GUITHREADINFO gti;
gti.cbSize = sizeof(GUITHREADINFO);
GetGUIThreadInfo(NULL, >i);
WindowToScroll = gti.hwndFocus;