6

Im trying to create a program where I can send some process id of a process (that may be firefox, ie, notepad etc) to a method that scrolls window of the process.

I have been trying with GetScrollBarInfo and SetScrollPos which I found at pinvoke without any success. Im not sure if this is the right way or not. I started playing with GetScrollBarInfo, but it doesn't seem to work.

I tried the code found at http://www.pinvoke.net/default.aspx/user32.getscrollbarinfo

[StructLayout(LayoutKind.Sequential)]
public struct SCROLLBARINFO
{
    public int cbSize;
    public Rectangle rcScrollBar;
    public int dxyLineButton;
    public int xyThumbTop;
    public int xyThumbBottom;
    public int reserved;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] rgstate;
}

private const uint OBJID_HSCROLL = 0xFFFFFFFA;
private const uint OBJID_VSCROLL = 0xFFFFFFFB;
private const uint OBJID_CLIENT = 0xFFFFFFFC;

private int Scroll(int ProcessID) 
{
    IntPtr handle = Process.GetProcessById(ProcessID).MainWindowHandle;
    SCROLLBARINFO psbi = new SCROLLBARINFO();
    psbi.cbSize = Marshal.SizeOf(psbi);
    int nResult = GetScrollBarInfo(handle, OBJID_CLIENT, ref psbi);
    if (nResult == 0)
    {
        int nLatError = Marshal.GetLastWin32Error();
    }
}

GetLastWin32Error() returns errorcode 122 which means "The data area passed to a system call is too small", according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx

Im not sure what I do wrong. How can I solve this?

Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
Kim Smith
  • 79
  • 1
  • 7

3 Answers3

6

You could send a WM_MOUSEWHEEL message to do what you want. For example, to scroll down once in a new notepad window using C++:

HWND hwnd = FindWindowEx(FindWindow(NULL, "Untitled - Notepad"), NULL, "Edit", NULL);
RECT r;
GetClientRect(hwnd, &r);
SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA * -1), MAKELPARAM(r.right / 2, r.bottom / 2));

To adapt that to C#, you could do something such as this:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, ref Point lParam);

private void ScrollWindow(IntPtr hwnd, Point p, int scrolls = -1)
{
    SendMessage(hwnd, WM_MOUSEWHEEL, (WHEEL_DELTA * scrolls) << 16, ref p);
}

Which could be used to scroll down once in a new notepad window like this:

//Imports
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
...
//Actual code
IntPtr hwnd = FindWindowEx(FindWindow(null, "Untitled - Notepad"), IntPtr.Zero, "Edit", null);
Point p = new Point(0, 0);
ScrollWindow(hwnd, p);

Some programs will require the lParam sent to be a point that's actually above the scrolled area, while others such as notepad will not.

Jammerx2
  • 794
  • 4
  • 12
  • Finding information about this seems to be kinda hard. Your example worked great by the way. Since you seems to be one of the few I have seen that knows this, where do you get this information? I would like to generalize this more, so it works on more than just notepad. Im not sure where "Edit" comes from. Whats that? How can I find equivalents for other programs? – Kim Smith Mar 13 '13 at 22:56
  • The "Edit" is the control in notepad that you need to send the scroll to. Many browsers won't require it to be sent to a control - for example, I tried with just FindWindow(null, "Current Page Name - Opera"); and scrolling worked fine. I have used WM_MOUSEWHEEL in the past, and know what is required for it due to using it in a low level mouse hook. If you want to get a control that it needs to be sent to in other windows, either look into EnumChildWindows or just use WinSpy++. For more information: http://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx – Jammerx2 Mar 14 '13 at 20:26
  • Also, if you need horizontal scrolling, the WM_MOUSEHWHEEL message should work. – Jammerx2 Mar 14 '13 at 20:27
3

If you're trying to scroll the window of another process, you need to, in effect, simulate clicks on the scroll bar or key presses. The cleanest way to do that is to use UI Automation, which has both .NET and native interfaces.

By asking for the scrollbar info, you're simply getting information about how the scrollbar is displayed. That's not going to give you a way to scroll the window content. You have to get the target application to scroll the content by making it think the user is operating the scrollbar.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
0

http://forums.codeguru.com/showthread.php?446352-How-to-scroll-active-window-SendMessage&p=1686041#post1686041

Final Code

class Program
{
        [DllImport("user32.dll")]
        static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);


        [DllImport("user32.dll")]
        public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);


        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);


        public struct GUITHREADINFO
        {
            public int cbSize;
            public int flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public System.Drawing.Rectangle rcCaret;
        }


        const Int32 WM_VSCROLL = 0x0115;
        const Int32 SB_LINERIGHT = 1;


        static void Main(string[] args)
        {
            //create process in focus
            Process.Start("notepad++", "Source.cpp");
            Thread.Sleep(1000);
            GUITHREADINFO threadInfo = new GUITHREADINFO();
            threadInfo.cbSize = Marshal.SizeOf(threadInfo);

            GetGUIThreadInfo(0, ref threadInfo);
            SendMessage(threadInfo.hwndFocus, WM_VSCROLL, SB_LINERIGHT, 0);
            //SetScrollPos not work. Change only scrollbar without scroll window
            //SetScrollPos(threadInfo.hwndFocus, System.Windows.Forms.Orientation.Vertical, 10, true);           
        }
    }