Why isn't my app getting mouse wheel tilt messages? - c#

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.

Related

Global cursor icon changing

I have a button on a (c#) WinForm, and when it is pressed (mouse down then up) I would like to change the mouse cursor to a custom icon. I would like that custom cursor icon to remain regardless of mouse position on the screen area (over source app, other apps, desktop, etc.) until the mouse is clicked (mouse down then up). After this second click I want the cursor to revert back to its default behavior.
I'm currently using the global mouse hook method outlined by Dan Silk (with adjustment from Hans Passant) to capture global mouse move and click events.
I think I need to intercept (and subsequently stop) WM_SETCURSOR messages (which according to Hans follow any mouse move). However I'm not sure how to do this for things beyond the source app, which Reza Aghaei outlined as follows:
const int WM_SETCURSOR = 0x0020;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
Cursor.Current = myCustomCursor;
else
base.WndProc(ref m);
}
When I tried to use the above WndProc method for adjusting the cursor for just the source app, I still got cursor flickering. Is there a proper way to stop the WM_SETCURSOR message sending/posting?
Any help or suggestions would be most appreciated!
UPDATE
I decided to go at my problem from a different angle to avoid fighting WM_SETCURSOR messages entirely. What I have now works fine, however if there is an answer floating out there you are welcome to post it for posterity.
Couple of these events works for me globally no matter which application is mouse over:
private static Cursor _customCursor = new Cursor(#"C:\path\Hand.cur");
private void button1_MouseDown(object sender, MouseEventArgs e)
{
Cursor = _customCursor;
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
Cursor = Cursors.Default;
}
Does it cover your needs or I have missed something important here?

Handle Double Click Anywhere in the Form?

In my WinForm application I have drawn a rectangle (System.Drawing.Rectangle) on a form.
I need to handle double click anywhere on the form
I could attach MouseDoubleClick event handler to the form. It works only when double click was made outside the rectangular shape.
How do I achieve this?
EDIT:
I have drawn a rectangle which is center aligned and covers only 40% of the whole winform area. When user double clicks on the rectangle I need to expand rectangle size to occupy full screen. That's all!
EDIT 2:
My friends who have down voted, write a comment please so that I could improve, please!
The best way I know to do what you are looking for is to use the "WndProc" method. This allows you to collect "messages" (events) from the message queue before they are sent to the form. You then have the option of either responding to those events or allowing them to continue through the normal message process. For more information, take a look at the MSDN page here.
A brief example of how you might use this:
protected override void WndProc(ref Message m)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh454920(v=vs.85).aspx
// 0x210 is WM_PARENTNOTIFY
// 513 is WM_LBUTTONCLICK
if (m.Msg == 0x210 && m.WParam.ToInt32() == 513)
{
var x = (int)(m.LParam.ToInt32() & 0xFFFF);
var y = (int)(m.LParam.ToInt32() >> 16);
var childControl = this.GetChildAtPoint(new Point(x, y));
if (childControl == cancelButton)
{
// ...
}
}
base.WndProc(ref m);
}
Credit for this example goes to: this stack question.
Remember too, that "double click" is just two single clicks, so you are going to have to monitor and find the time between clicks yourself to decide what actually represents a double click.
Unfortunately Rectangle does not contain any fire any events (at all) as System.Drawing merely provides "grahpics" for a form.
There are options (such as provided by #drew_w) however such workarounds that require interaction with COM objects is usually indicate it's time to re-evaluate the requirements.

Preventing mouse clicks through a transparent Windows Form

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

Prevent mouse from leaving my form

I've attached some MouseMove and MouseClick events to my program and next up is one of these:
Get "global" mouse movement, so that I can read the mouse location even outside the form.
Prevent my mouse from leaving the form in the first place.
My project is a game so it'd be awesome to prevent the mouse leaving my form like most other games do (ofc. you can move it out if you switch focus with alt+tab fe.) and taking a look at answers to other questions asking for global mosue movement, they seem pretty messy for my needs.
Is there an easy way to prevent my mouse from going outside my form's borders? Or actually to prevent it from going OVER the borders in the first place, I want the mouse to stay inside the client area.
Additional info about the game:
The game is a short, 5-30 seconds long survival game (it gets too hard after 30 seconds for you to stay alive) where you have to dodge bullets with your mouse. It's really annoying when you move your mouse out of the form and then the player (System.Windows.Forms.Panel attached to mouse) stops moving and instantly gets hit by a bullet. This is why preventing mouse from leaving the area would be good.
Late answer but might come in handy. You could subscribe the form to MouseLeave and MouseMove events and handle them like this :
private int X = 0;
private int Y = 0;
private void Form1_MouseLeave(object sender, EventArgs e)
{
Cursor.Position = new Point(X, Y);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (Cursor.Position.X < this.Bounds.X + 50 )
X = Cursor.Position.X + 20;
else
X = Cursor.Position.X - 20;
if (Cursor.Position.Y < this.Bounds.Y + 50)
Y = Cursor.Position.Y + 20;
else
Y = Cursor.Position.Y - 20;
}
The above will make sure the mouse cursor never leaves the bounds of the form. Make sure you unsubscribe the events when the game is finished.
Edit :
Hans Passants's answer makes more sense than my answer. Use Cursor.Clip on MouseEnter :
private void Form1_MouseEnter(object sender, EventArgs e)
{
Cursor.Clip = this.Bounds;
}
You could free the cursor in case of any error/crash (I'm sure you could catch'em) :
Cursor.Clip = Rectangle.Empty;
You cannot trap the mouse, that would prevent the user from, say, operating the Start menu. Closest you can get is assigning the Cursor.Clip property. But it is easily defeated by the user pressing Ctrl+Esc for example, there is no notification for this.
Best thing to do is to subscribe the form's Deactivated event, it reliably tells you that the user switched to another program. The Activated event tells you when the user moved back. Of course the user will have few reasons to actually do this when the game score depends on keeping a game object moving. So don't forget to give the user an easy way to pause the game with, say, the Escape key.
I don't know a solution for your exact problem, but I have a completely different idea for you. I don't know how your game works, but based on what you told me, why not make it a step harder: Add borders to the game-area, for example 4 pixels wide rectangles, which you are not allowed to touch. If you touch them, you die and the mouse gets released.
You can use the Cursor class. For example:
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
As for preventing the user to move the mouse outside the form, the best approach would probably be if you had someway to know what is the coordinates of your form on the screen and attach a MouseMove event, and check if the mouse is inside the form rectangle.
To know the form position on the screen take a look at this question.
I wouldn't recommend the global mouse movement control for two reasons.
It's bad design, you should respect the bounds of the operating system. Make the application full screen if you want this kind of behaviour. The only applications that should perform these kind of operations are "kiosk" mode applications which lock down the entire OS (to prevent operator abuse).
Global key hooks are messy, aren't guaranteed to work and are dangerous because they affect a key part of the operating system (all controls). A bug in your code could result in requiring a reboot on the machine.
That said, last time I checked (a while ago, on Vista) SetWindowsHookEx still works (but its not officially supported IIRC), it's an unmanaged call so you'll have to pinvoke but with it you can refuse to pass on messages that would move the mouse outside of the bounds of your application. I'm not 100% sure if the OS will let you beat it to the cursor control (I've only blocked keyboards before on desktop boxes) but its probably your best shot.

Why do my faked button presses fail with SendMessage?

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.

Categories

Resources