I'm trying to send mouse events to a window in windows through the SendMessage(..) method.
The problem I'm facing is that the messages don't seem to be delivered to the window I'm sending them to, even though SendMessage returns 0, which(according to the documentation) means the message was delivered successfully.
I'm using the following piece of code:
(Let p be a Point struct and selectedWindow.Handle a valid handle to a window)
int position = ((p.X & 0xFFFF) << 16) | (p.Y & 0xFFFF);
SendMessage(selectedWindow.Handle, 0x0201, new IntPtr(), new IntPtr(position));
SendMessage(selectedWindow.Handle, 0x0202, new IntPtr(), new IntPtr(position));
0x0201 and 0x0202 are WM_LBUTTONDOWN and WM_LBUTTONUP.
Could someone enlighten me why this isn't working?
(Edit: I am using the ScreenToClient() method to convert a screen position to a position within the window)
Mouse messages are normally retrieved from the message queue, which means you should use PostMessage(). But it isn't that likely that this is the real problem, very few programs do mouse handling in the message loop. UAC is an obvious failure scenario, you cannot send messages to a window owned by an elevated program. You send the wrong WParam value, that could have an effect. And of course you could have the wrong window handle.
But the much more likely cause is the code we cannot see. You seem to go through some trouble to generate the X- and Y-coordinates of the message. No such effort is necessary, it doesn't matter where you click the button. You might as well click it at (1, 1):
PostMessage(selectedWindow.Handle, 0x0201, new IntPtr(1), new IntPtr(0x10001));
PostMessage(selectedWindow.Handle, 0x0202, new IntPtr(0), new IntPtr(0x10001));
Or in other words, mouse coordinates are relative from the window's upper left corner. Additional hacks are generating the BM_CLICK message and whatever WM_COMMAND message the button generates.
Use the Spy++ tool to observe the message processing.
I have three suggestions:
First, make sure that the coordinates are relative to the origin of the window's client area. If you're using screen coordinates, you must first use the ScreenToClient function to convert them.
Second, try using PostMessage instead of SendMessage. This more closely emulates the actual behavior of Windows.
Finally, unless you are absolutely opposed to moving the actual mouse pointer, you may find it easier to use the mouse_event or SendInput function.
Related
I'm making a little tool for drawing onto the screen with the mouse after a 'pen' button is toggled in a floating sidebar.
I have done this (please don't laugh) by having a top-most windows form with its background as its transparency key cover the whole screen.
I need to make the mouse not click through the form onto the stuff below when I"m in drawing mode. I tried following this:
Windows form with a transparent background that cannot be clicked through
How to disable click through on transparent control?
which successfully stops the mouse but also un-maximises the form and drags it around with the mouse (using HTCAPTION IntPtr(2) this is) I tried using some of the other values listed on MSDN, but with no luck.
I'm way out of my depth, any help greatly appreciated (newbie friendly please!)
PS I'm using this right now..
//code for allowing clicking through of menus
protected override void WndProc(ref Message m)
{
if (penMode && m.Msg == 0x84)
{
m.Result = new IntPtr(2);
}
else
base.WndProc(ref m);
}
UPDATE: Now solved the problem by approaching it in another way entirely. It doesn't look like WndProc will work so I simply created a blank form over the whole screen the showed my main form (form.Show(this)) from within that. Then adjust the opacity of the blank form which sits underneath from 0% to 1% to allow/ prevent clicking through. Works!
Thanks to all answers, taught me a lot.
Actually, no need to laugh—it sounds to me like you're doing this the correct way already. Since you don't own the desktop, you shouldn't draw directly on it. Instead, you need to simulate it by overlaying a transparent form that you do own, and then drawing on that. Because you own the transparent overlay form, it's no problem to draw on it.
But beyond that, it sounds like you're just trying values randomly without a clear understanding of what they actually do. That's like throwing darts with your eyes closed. You won't have a very high hit count.
Let's start by understanding what your code does. The magic value 0x84 corresponds to the WM_NCHITTEST message, which is sent by Windows to a window to determine how mouse clicks on that window should be handled. In response to that message, you reply with one of the HT* values, given in the linked documentation. Each of those values has a particular meaning, also explained in the documentation. For example:
HTCAPTION (which has a value of 2) means that the clicked portion of the window should be treated as the window's caption/title bar. You know from using Windows that you can drag windows around on the screen using the title bar, so it makes sense that returning HTCAPTION in response to mouse clicks would allow your window to be draggable. You'll see this used on borderless forms (i.e., those with no title bar) to allow them to be movable.
HTTRANSPARENT (which has a value of -1) is another available value. This one's pretty simple. It just makes your window look transparent. It's like saying "don't mind me, there's no window here!" Mouse clicks are simply passed on to the window that lies below yours in the Z order as if you weren't there.
HTCLIENT (a value of 1) is the default result when the click occurs anywhere on the window's client area. You would return this (or simply call the default window procedure) when you want everything to work normally. Click events that return this value would go on to be processed normally by the framework, raising either the form's Click event, or getting passed on to child controls located on the form.
So, when you're not drawing, you probably want to return HTTRANSPARENT. When you are drawing, you probably want to return HTCLIENT so that your drawing code can see the mouse events and draw the result.
Fixing your code, then:
// Code for allowing clicking through of the form
protected override void WndProc(ref Message m)
{
const uint WM_NCHITTEST = 0x84;
const int HTTRANSPARENT = -1;
const int HTCLIENT = 1;
const int HTCAPTION = 2;
// ... or define an enum with all the values
if (m.Msg == WM_NCHITTEST)
{
// If it's the message we want, handle it.
if (penMode)
{
// If we're drawing, we want to see mouse events like normal.
m.Result = new IntPtr(HTCLIENT);
}
else
{
// Otherwise, we want to pass mouse events on to the desktop,
// as if we were not even here.
m.Result = new IntPtr(HTTRANSPARENT);
}
return; // bail out because we've handled the message
}
// Otherwise, call the base class implementation for default processing.
base.WndProc(ref m);
}
You might just want to set the visibility of your window to like 5% or so and leave the transparent key deactivated.
you basically won't notice it and jet it's there :D
hope this helps
I'm working on a 'drag' action in game'ss window. I want to mouse_down in a one place and then mouse_up another one.
Here's the c# code:
uint Iparm = makeDWord((ushort)x, (ushort)y);
IntPtr xd = new IntPtr(Iparm);
uint Iparm2 = makeDWord((ushort)x2, (ushort)y2);
IntPtr xd2 = new IntPtr(Iparm2);
SendMessage(p, (uint)0x201, (IntPtr)0x1, xd); // down button (start x,y)
SendMessage(p, (uint)0x202, (IntPtr)0x0, xd2); // up button (final x,y)
I have connected that code with a button (lets say button named START) in a c# form. When I push START and keep on moving my mouse, it works perfectly. But when I just push START and wait for the action (without moving the cursor on the screen), nothing happens. I've checked game's window with spy++ while pushing button in my form, and there's no difference between two ways of pushing that button (moving/not moving cursor).
I've forgotten to add that while moving cursor, action takes place even though window is minimized (what is very cool)
What's causing my problem ? : P
edit:
WM_MOUSEDOWN = 0x201;
WM_MOUSEUP = 0x202;
That code is placed in a function, p is windows handle, button START executes that function.
As I said before, the code works just fine while I'm moving cursor
Try calling SendMessage with a WM_MOUSEMOVE message in between the down and up.
Use SendInput instead.
Im trying to move my cursor according to my hand point in kinect, I can get the real coordinates I mean I can move an image on screen but I want real cursor to be settled according to my hand coordinates. I tried Console.SetCursor(x,y) but it gives exception I also tried to download windows forms dll but I cant find the version 4.00 . Is there any simple way to set cursor in a desired position? (which is working by the way and as I said Console.SetcursorPosition is not wodking?)
You didn't provide very much information about you app but I suspect that you just need to assign to Cursor.Position from System.Windows.Forms. You may need to add a reference to System.Windows.Forms in order to gain access to this, depending on exactly what type of project you have.
If you want to keep it lightweight and avoid taking a reference to WinForms then you could just pinvoke to SetCursorPos.
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
Just use
Cursor.Position = new Point();
You can find more information's here
Thank you for question and answer.
I found strange and unobvious behavior.
Let you use multi-monitor (two or three monitor) configuration, and use two windows in your application.
One window is active, and you set cursor position for second windows.
And this two windows located on different monitors.
Then, you need call SetCursorPos TWICE!
TWICE, who would have thought?
Firs call move cursor to virtual borderline between monitors.
And only second call move cursor to required position second window!
Example of code (after 6 hours of experiments):
SetForegroundWindow(this.Handle); // set second window active
SendMessage(this.Handle, 0x20, this.Handle, (IntPtr)1); // Send WM_SETCURSOR
SetCursorPos(400, 600);
Thread.Sleep(50);
SetCursorPos(400, 600);
I'm writing a simple Windows Forms utility to screenshot either the entire screen or an active window and save the file. The winform has a button called 'Capture' that, when clicked, takes the screenshot.
I have the code entirely working for the entire screen but can't figure it out for the active window.
By my current code, if I select 'Active Window', it ends up Print Screening the application itself which isn't what I want.
Here's my code (under the Capture_Click method):
try
{
this.Hide();
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
//bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
if (AltPrint.Checked)
{
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
}
else
{
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
number++;
}
In your button_Click method, to capture a window other than the one which is doing the capturing, the only possible way I can see is to make use of the user32 Windows API.
You will first need to platform invoke some methods like this:
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, GetWindowLongNIndex nIndex);
I recommend www.pinvoke.net/ for more info about this.
Here is my own Window class (work in progress, but useable) which you are welcome to use as an example: http://pastebin.com/fj2tEaPY
Some useful properties in there for you are:
get AllWindows
get TaskbarWindows
set ForegroundWindow
You'll need to get the handle of your current window (use get ForegroundWindow, then the Handle property), then find the window which you want to capture the image of, perhaps using code similar to that in TaskbarWindows. Once you have the window, you will need to send the keypresses Alt + PrtScn (again maybe using user32 API, but I won't go into it.) Then use the static ForegroundWindow property to set the orignal window as the forground using the handle which you saved at the start.
Good luck!
As far as I can see your code uses the bounds of its own form to capture the "active" window. That is probably not what you intend.
If you want to use the mouse to "print" a window (and not hotkey which probably is easier to implement) you need to be able to from your own form let the user point to another window to capture. Here is an idea describing how to do that:
When the user presses the "Capture" button capture the mouse and initiate a drag operation.
Let the user drag the mouse (perhaps now having a "bullseye" or a "camera" cursor) to the window to capture.
When the user releases the mouse determine the window it was on top and use the location and size of this window to capture the correct pixels.
You will probably need to use P/Invoke to get information about all top-level windows on the desktop.
Note that "mouse capture" is very different from "screen capture". When an application "captures the mouse" it takes ownership of the mouse cursor and doesn't the ability to track the mouse even when it is moved outside the bounds of the window of the application.
Your comments seem to understand that once your app gets the focus (when you click on it) the other app has lost it and is no longer 'active'.
A possible alternative: your application gets a WM_SETFOCUS message when it gets focus. The wParam of that message is a handle to whichever window has just given up the focus -- and that window belongs to the app you want to capture.
Of course, that window will only be a subwindow or control, but you can figure out which app owns that button.
I ended up taking a different route.
I allow the user to resize my form window to cover exactly the area they need to screenshot, and then when they click the button, the form hides itself, takes a screenshot of the area it was covering and appears again. Contrary to my initial plan, I believe this will be more beneficial, as my targeted users (friends at an overclocking forum I'm part of) seldom take screenshots of just one window. They mostly need a couple of windows covered (temp monitoring, clockspeed monitoring, stability test etc.).
Thanks for all the answers guys. They definitely got me thinking, and made me realize what my options were.
Should anyone else need it, this is the code I use for the button that captures only part of the screen:
try
{
this.Hide();
Thread.Sleep(250);
bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Screenshot saved at " + SaveLocation);
numSuffix++;
}
catch (Exception ex)
{
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Unable to take screenshot. Exception: " + ex.ToString());
}
finally
{
this.Show();
}
You need to borrow the design of commercially available screen capturing apps, such as Psp. I think you need to use a 'hot' key to trigger the capturing of the active window. Instead of doing it on Capture_Click event. This way, you can hide your application's window so that it does not become the active window. Then when the user pushes the 'hot' key, you can capture it.
What about SendKeys?
Windows.Forms.SendKeys.Send("%{PRTSC}");
It will copy currently selected window in to Clipboard object.
Than calling
BitmapSource image = Clipboard.GetImage();
you can get your image back
In this question How to detect mouse wheel tilt an answer is posted and accepted that shows the code needed.
I've implemented that code in my application's existing WndProc method (which is working for other messages I need to trap) but it's not working. I've checked and WndProc doesn't appear to be getting any messages at all let alone ones with a value of 0x020E when I tilt the mouse wheel.
I'm using a Microsoft Wireless Laser 5000 on Windows XP SP3 (fully patched) with .NET 3.5 SP1 installed.
I've updated my Intellipoint drivers to version 7.0.258.0 dated 08/05/2009.
Other applications (e.g. Visual Studio, Word, paint.NET) are getting and acting upon the mouse wheel being tilted so it must be my application, but I can't see what I'm doing wrong.
Just for completeness here's my code:
protected override void WndProc(ref Message m)
{
Trace.WriteLine(string.Format("0x{0:X4}", m.Msg));
switch(m.Msg)
{
case WM_EXITSIZEMOVE:
Opacity = 1.0;
break;
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MINIMIZE && this.minimizeToTray)
{
MinimizeToTray();
}
break;
case WM_MOUSEHWHEEL:
// Handle tilting here
break;
}
base.WndProc(ref m);
}
The Trace.WriteLine call is an attempt to check if the tilt messages are getting through. The other messages WM_EXITSIZEMOVE and WM_SYSCOMMAND are being received. The messages are defined as:
private const int WM_EXITSIZEMOVE = 0x0232;
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xF020;
private const int WM_MOUSEHWHEEL = 0x020E;
NOTE I removed the [hardware] tag, as I'm 99% sure it's not the hardware that's at fault as other applications are receiving the messages.
UPDATE
I've added a multi-line textbox with scrollbars to my application and that receives and acts upon the mouse wheel tilt messages. So all I need to do is find the code for that ;)
UPDATE
This question on SuperUser may have some bearing on this - I'll keep an eye on answers there.
Use Spy++ to check what messages you are receiving.
EDIT: You can also call m.ToString() in you WndProc method to get the name (!) of the message you've received. (This is done by a giant switch statement in Syetm.Windows.Forms.MessageDecoder.MsgToString)
Note that the messages might be sent only to whatever control has focus and not to the form itself; if that is the case, you might want to use a message filter.
Also, note that different mice send different mousewheel messages. I have a Logitech mouse that sends 0x20E with a WParam that is negative for left scroll and positive for right scroll.
EDIT (in reponse to comments)
Remember that horizontal scrolling was added long after vertical scrolling and is not supported by older programs. Therefore, the mouse driver could well be looking for horizontal scrollbars and scrolling them explicitly. Try adding a horizontal scrollbar to your form, positioned negatively so the user won't see it, and see if that changes anything.