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?
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.
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.
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);
}
}
Related
The goal
I developed a keyboard in Unity3D (C#) and want it to pop up when the users click on "EDIT" type control such as a address bar or an input field. Therefore, I need to detect when an "EDIT" control is clicked.
What I've tried
Currently I use SetWinEventHook and listen to event EVENT_OBJECT_FOCUS to get the handle of the object which gets the focus. After that, I use GetClassName to see if the focused object is an "EDIT" control which displays a flashing caret when clicked. However, take Google Chrome as an example, I always get Chrome_WidgetWin_1 whether I click the address bar or the plain text of the page. After doing some googling I found this blog post What makes RealGetWindowClass so much more real than GetClassName? saying that RealGetWindowClass can get the base class which I think it will be something like "EDIT" or "COMBOBOX" listed here. Things were not going so well. I tried using RealGetWindowClass and still get the same result Chrome_WidgetWin_1.
The problem
Why do GetClassName and RealGetWindowClass return the same value? How should I make RealGetWindowClass return the base class?
The code
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll", SetLastError = true)]
private static extern int UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, uint cchType);
private delegate void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private const int WINEVENT_SKIPOWNPROCESS = 2;
private IntPtr windowEventHook;
private const int EVENT_OBJECT_FOCUS = 0x8005;
private void Start()
{
if (windowEventHook == IntPtr.Zero)
{
windowEventHook = SetWinEventHook(
EVENT_OBJECT_FOCUS,
EVENT_OBJECT_FOCUS,
IntPtr.Zero,
WindowEventCallback,
0,
0,
WINEVENT_SKIPOWNPROCESS);
if (windowEventHook == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
private void OnDestroy()
{
UnhookWinEvent(windowEventHook);
}
private void WindowEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
UnityEngine.Debug.Log($"[EventType]: {(EventEnum)eventType} [Class Name]: {GetClassName(hWnd)} [Real Class]: {RealGetWindowClassM(hWnd)}");
// Will print out the same log whether I click an address bar or plain text.
// [EventType]: EVENT_OBJECT_FOCUS [Class Name]: Chrome_WidgetWin_1 [Real Class]: Chrome_WidgetWin_1
}
private string GetClassName(IntPtr hWnd)
{
StringBuilder className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
return className.ToString();
}
private string RealGetWindowClassM(IntPtr hWnd)
{
StringBuilder className = new StringBuilder(256);
RealGetWindowClass(hWnd, className, (UInt32)className.Capacity);
return className.ToString();
}
Hi I looked into this guy question and got some of my answer but I would like to instead of resizing explorer I would like to resize discord or firefox for instance
private void windowsPos(string name, Rect pos){
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows()){
if (Path.GetFileNameWithoutExtension(window.FullName).ToLowerInvariant() == name){
window.Left = pos.Left;
window.Top = pos.Top;
window.Width = pos.Width;
window.Height = pos.Height;
}
}
}
But this works only for explorer I tried other exe but it works only for explorer ... can someone help me adapt this code to my requirements ?
Thank you #zezba9000 & #NetMage your answsers were the ones that worked ... I went on PInvoke - EnumWindows to get the general idea of how it works and the used Reed's answer to get to this code :
public class SearchData{
public string Title;
public IntPtr hWnd;
}
private delegate bool EnumWindowsProc(IntPtr hWnd, ref SearchData data);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, ref SearchData data);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
private void test(){
IntPtr hwnd = SearchForWindow("Discord");
SetWindowPos(hwnd, 0, 50, 175, 1000, 750, 0x0040);
}
public IntPtr SearchForWindow(string title){
SearchData sd = new SearchData { Title = title };
EnumWindows(new EnumWindowsProc(EnumProc), ref sd);
return sd.hWnd;
}
public bool EnumProc(IntPtr hWnd, ref SearchData data){
StringBuilder sb = new StringBuilder(1024);
GetWindowText(hWnd, sb, sb.Capacity);
if (sb.ToString().Contains(data.Title)){
data.hWnd = hWnd;
return false;
}
else return true;
}
Also #Jimi I tried your approach but I could not work with it because it could not find Automation in the System.Windows namespace
I'm trying to make a Unity3D Application that can control the volume of another application. So far I've gotten it to work to change the volume of ALL applications, but I'm having trouble to get it to change just one, e.g. Spotify. This is on Windows 8, but I'd like it to work for Windows 7 as well.
private const int APPCOMMAND_VOLUME_UP = 0xA0000;
private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
public static extern IntPtr GetActiveWindow();
public void DecreaseVolume()
{
IntPtr hWnd = FindWindow("SpotifyMainWindow", "Spotify");
if (hWnd == IntPtr.Zero)
return;
uint pID;
GetWindowThreadProcessId(hWnd, out pID);
Debug.Log(pID);
SendMessage(hWnd, WM_APPCOMMAND, hWnd, (IntPtr)APPCOMMAND_VOLUME_DOWN);
}
public void IncreaseVolume()
{
// same as above, but with APPCOMMAND_VOLUME_UP
}
I'm pretty sure I'm getting the correct handle (hWnd) from FindWindow() because the pID printed out matches the PID of Spotify in my Task Manager, however all this does is change the volume of everything instead of just Spotify. This is the same result as if I didn't specify a specific handle e.g.
SendMessage(IntPtr.Zero, WM_APPCOMMAND, IntPtr.Zero, (IntPtr)APPCOMMAND_VOLUME_DOWN);
or if I use GetActiveWindow().
I've never messed around with these windows api stuff until today, so any help would be very appreciated!
How to hide Start button while openning camera using CameraCaptureDialog in windows mobile
Do you just want to hide the Start button, or the whole taskbar?
You can hide and show the complete taskbar by using this code:
[DllImport("coredll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(
[In] string lpClassName,
[In] string lpWindowName);
[DllImport("coredll", EntryPoint = "ShowWindow")]
public static extern bool ShowWindow(
[In] IntPtr hWnd,
[In] int nCmdShow);
[DllImport("coredll", EntryPoint = "EnableWindow")]
public static extern bool EnableWindow(
[In] IntPtr hWnd,
[In] bool bEnable);
public const int SW_HIDE = 0x0000;
public const int SW_SHOW = 0x0001;
public static void HideTaskBar()
{
IntPtr hWnd = FindWindow("HHTaskBar", null);
EnableWindow(hWnd, false);
ShowWindow(hWnd, Win32.SW_HIDE);
}
public static void ShowTaskBar()
{
IntPtr hWnd = FindWindow("HHTaskBar", null);
EnableWindow(hWnd, true);
ShowWindow(hWnd, Win32.SW_SHOW);
}
This is with reference to this question:
How to read output and give input to a program from c program?
I found the answer to the above question very useful. In the same way, can I do it for windows applications? Like can I give input into a Windows form (like .net, C# applications) application's text box programmatically? Can I do the action of button click from a program?
If so, can I do it from a C program (with Tiny C compiler or any other on windows platform)? If not, how and in which language can I do it? Please don't take it as a silly question. Any example codes will be very much appreciated.
Edit: Can I do the same with web applications and web pages?
If your application mainly just needs to manipulate other windows, by sending keystrokes etc. i would use AutoIt. It's mainly function is exactly this job and it does it very well.
So maybe you should give it a try, cause not every problem is a nail and can be solved with a hammer (C#) ;-))
You can enumerate all the windows of the system.
You can send windows messages to any window.
E.g. to set the text of a textbox you have to send a WM_SETTEXT message.
FYI: Winspector is a very interesting tool which makes heavy use of this as well (also to debug or otherwise first inspect the windows you're trying to programmatically access).
You might also be interested in this:
AutoIt v3 is a freeware BASIC-like scripting language designed for automating the Windows GUI and general scripting. It uses a combination of simulated keystrokes, mouse movement and window/control manipulation in order to automate tasks in a way not possible or reliable with other languages (e.g. VBScript and SendKeys). AutoIt is also very small, self-contained and will run on all versions of Windows out-of-the-box with no annoying "runtimes" required!
As fretje pointed out, you can certainly do it.
It's much more challenging for windows apps. Because individual controls also count as windows, the total window count will be much higher than you might expect; and finding the specific control you want to send input to can take a lot of work.
But you can programmatically move, resize, check, fill, maximize or otherwise affect an app's windows, once you know which one is your target.
I wrote code to do the discovery process a few years ago. It found all the windows of a target app, then using their size & location data, produced a translucent overlay of each child window, along with the handle number. So I could visually tell which control went with which handle.
EDIT: Added some code. This is some basic C# interop code that will let you make easy calls into user32.dll, which holds the fns to which fretje referred. This just gives you the basic calls for discovery and manipulation; you'll still have to do the hard work of enumerating and examining what you find. If you can find a 3rd-party package that does the job for you, save yourself the trouble; I only did it as a learning experience, and it was pretty laborious.
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;
namespace WinAPI
{
[Flags] public enum WindowStyleFlags : uint
{
WS_OVERLAPPED = 0x00000000,
WS_POPUP = 0x80000000,
// more...
}
[Flags] public enum ExtendedWindowStyleFlags: int
{
WS_EX_DLGMODALFRAME = 0x00000001,
WS_EX_NOPARENTNOTIFY = 0x00000004,
// more...
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct POINT
{
public int Left;
public int Top;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct FLASHWINFO
{
public int cbSize;
public IntPtr hwnd;
public int dwFlags;
public int uCount;
public int dwTimeout;
}
public delegate int EnumWindowsCallback( IntPtr hwnd, int lParam );
public class User32Dll
{
// Constants & fields
public const int FLASHW_STOP = 0;
public const int FLASHW_CAPTION = 0x00000001;
// lots, lots more, web search for them...
// Self-added, don't know if correct
[DllImport("user32")]
public extern static bool CloseWindow( IntPtr hWnd );
[DllImport("user32")]
public extern static IntPtr GetDesktopWindow();
[DllImport("user32")]
public extern static IntPtr GetForegroundWindow();
[DllImport("user32")]
public extern static int GetDlgItem( IntPtr hWnd, int wMsg );
[DllImport("user32")]
public extern static int GetListBoxInfo( IntPtr hWnd );
[DllImport("user32")]
public extern static bool MoveWindow( IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint );
[DllImport( "user32" )]
public static extern int SendMessage( IntPtr hWnd, int uMsg, IntPtr wParam, StringBuilder lpString );
[DllImport("user32")]
public static extern bool SetWindowPos( IntPtr hWnd, IntPtr afterWnd, int X, int Y, int cX, int cY, uint uFlags );
[DllImport("user32")]
public extern static int BringWindowToTop (IntPtr hWnd);
[DllImport("user32")]
public extern static int EnumWindows( EnumWindowsCallback lpEnumFunc, int lParam );
[DllImport("user32")]
public extern static int EnumChildWindows( IntPtr hWndParent, EnumWindowsCallback lpEnumFunc, int lParam );
[DllImport( "user32.dll" )]
public static extern int EnumThreadWindows( IntPtr hWndParent, EnumWindowsCallback callback, int lParam );
[DllImport( "user32.dll" )]
public static extern int FindWindow( string lpClassName, string WindowName );
[DllImport( "user32.dll" )]
public static extern int FindWindowEx( IntPtr hWnd, IntPtr hWnd2, string lpsz, string lpsz2 );
[DllImport("user32")]
public extern static int FlashWindow ( IntPtr hWnd, ref FLASHWINFO pwfi);
[DllImport("user32")]
public extern static IntPtr GetAncestor( IntPtr hWnd, uint gaFlags );
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetClassName ( IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static uint GetWindowLong( IntPtr hwnd, int nIndex);
[DllImport("user32")]
public extern static int GetClientRect( IntPtr hWnd, ref RECT lpRect);
[DllImport("user32")]
public extern static int GetWindowRect( IntPtr hWnd, ref RECT lpRect);
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetWindowText( IntPtr hWnd, StringBuilder lpString, int cch );
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetWindowTextLength( IntPtr hWnd );
[DllImport("user32")]
public extern static int IsIconic(IntPtr hWnd);
[DllImport("user32")]
public extern static int IsWindowVisible( IntPtr hWnd );
[DllImport("user32")]
public extern static int IsZoomed(IntPtr hwnd);
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int PostMessage( IntPtr hWnd, int wMsg, int wParam, int lParam);
[DllImport( "user32.dll" )]
public static extern int RealGetWindowClass( IntPtr hWnd, StringBuilder pszType, uint bufferSize );
[DllImport("user32")]
public extern static int ScreenToClient( IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int SendMessage( IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public extern static int SetForegroundWindow (IntPtr hWnd);
[DllImport( "user32.dll" )]
public static extern int SetWindowText( IntPtr hWnd, string lpsz );
}
}
Check out the following SO question. There are numerous vendor and open-source tools to do this for you, so you don't have to code at to the Win32 API.
automated-testing-of-windows-forms