Odd enum values in Windows.Forms.MouseButtons - c#

I found this gem (IMO) in System.Windows.Forms namespace. I'm struggling to figure out why is it set like this.
[Flags]
public enum MouseButtons
{
None = 0,
Left = 1048576,
Right = 2097152,
Middle = 4194304,
XButton1 = 8388608,
XButton2 = 16777216,
}
Can somebody explain why it uses these values (power of 2^20 to 2^24) instead of this:
public enum MouseButtons
{
None = 0,
Left = 1, // 2^0
Right = 2, // 2^1
Middle = 4, // 2^2
XButton1 = 8, // 2^3
XButton2 = 16, // 2^4
}
The first value is 100000000000000000000 in binary, which leaves space for another 20 bits! Why do we need such space and why is it preserved like this?

Enum values used in Winforms do tend to match corresponding bits in the winapi but that's not the case at all for mouse buttons. Explaining this one requires a pretty wild guess.
I do have one, the way you retrieve the state of the mouse buttons without relying on a Windows message is very strange. You call GetAsyncKeyState(), passing VK_LBUTTON through VK_XBUTTON2. Fake virtual keys that actually represent mouse keys and not keyboard keys. This happened way too long ago for me to guess why they did it this way instead of providing a proper GetMouseButtonState() winapi function.
The Keys enumeration has those values as well, like Keys.LButton etcetera. Something else that's special about Keys is that it can also encode the state of a modifier key. There are for example Keys.Control and Keys.ControlKey. And Keys.Shift vs Keys.ShiftKey, etcetera. The first one indicates the state of the key, the second one indicates the actual key. Which permits friendly code like keydata == (Keys.Control | Keys.F) to check if Ctrl+F was pressed.
The significance of these MouseButtons enum values is then that they fit in a Keys enum value to indicate the state of the mouse buttons. Leaving 20 bits available for the bits that encode the key.
Sounds good, doesn't it? The only hiccup is that it is never combined that way within the Winforms object model. But could be in your own code to define a shortcut that also uses the mouse state.

My guess is that it has to do with how the underlying Windows API is passing mouse information to .NET.
Windows packages up which mouse buttons are clicked as well as pointer position in a block of information (think of the old MOUSE_EVENT structure). The enums in .NET are setup the way they are to be efficient, so they probably line up nicely with how the underlying Windows message.
So, instead of getting the lower-level message and having to convert it into a new set of values, .NET just targets the bits in the lower-level message that it is interested in - no conversion, no math, just efficiency.

Related

Translating Windows.forms.Keys to real local keyboard value

I use the namespace Windows.Forms.Keys
I would like to be able to catch and use some special like characters like é,è,à,ç, but when the program fire the event KeyDown, the KeyEventArg just return me the value "D1" to "D9".
What could I do to get the real char associated to these keys ?
Short answer: use KeyPress instead of KeyDown.
KeyDown is really designed to work with the physical layout of the keyboard (well, the logical layout of the physica... forget it :D). This is very different from the character that given physical key represents.
On the other hand KeyPress is all about characters being input from the keyboard, rather than keys being pressed, really. Note how KeyPress supports features like AltGr + someKey and char repetition etc.
If you really need to use KeyDown/KeyUp, you'll have to emulate the way windows keyboard system works to determine the char to output (for example, if you're making a keyboard mapping screen for a game or something like that). You can use the ToAscii WinAPI method (https://msdn.microsoft.com/en-us/library/ms646316.aspx).
Apart from that, you still have to understand the meaning of the key combinations - for example, on my keyboard, if I press 1, I get +. If I press Shift+1, I get 1. If I press AltGr + 1, I get !. Which of those do you care about? Maybe Shift + 1 should be interpreted as 1 (what KeyPress does). Maybe it should be interpreted as Shift + 1 (the easiest, you already have that). And maybe it should be interpreted as Shift + +, the way it's usually used for hotkey bindings or keyboard mappings in games.
It should be pretty obvious by now that this is actually far from trivial. You need some mechanism to interpret the "raw" input data - and different interpretations make different sense for different initial conditions. You're basically asking for a mixed approach between the two obvious options - you're mixing virtual keys and "real" characters.

Application.AddMessageFilter - how to read exactly what keys were pushed?

I'm experimenting with Application.AddMessageFilter, using some code written originally by Somebody Else, so I don't necessarily understand everything that's happening here.
Here's what the code looks like. In Main():
Application.AddMessageFilter(new KeyDownMessageFilter());
In KeyDownMessageFilter:
internal class KeyDownMessageFilter : IMessageFilter {
private const int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
var k = (Keys)m.WParam;
var c = (char)k;
// and some other stuff
}
return false;
}
}
I can see that by casting m.WParam to a variable of type System.Windows.Forms.Keys and then casting that to a char, I can tell which key on the keyboard was pushed. So far, so good.
BUT - I cannot work out how to tell the difference between a Shifted key and a key push without a Shift - e.g. pushing % yields the character '5'. Even more weirdly, looking at the value of the k, it appears as "LButton | MButton | ShiftKey | Space" (no matter whether I push 5 or %).
The MSDN documentation on this subject is rather thin. Can anyone please explain how to tell exactly what character was pushed, and for bonus marks, explain what this Message object is supposed to be for, and why it uses such nondescript properties such as LParam and WParam to carry useful information?
e.g. pushing "%" yields the character '5'
By design, the WM_KEYDOWN message passes a virtual key code in wparam. The virtual key code for the key that produces '%' and '5' is the same, it is the same key on your keyboard. It doesn't get turned into an actual typing key until Windows processes the WM_KEYDOWN message and turns it into a WM_CHAR message. Which will check the state of the Shift, Ctrl and Alt keys and change the generated character accordingly. The actual character that is produced depends on the active keyboard layout.
looking at the value of the k, it appears as "LButton | MButton | ShiftKey | Space"
That's a side-effect of the [Flags] attribute on the Keys type. The default Enum.ToString() method checks that attribute and will combine enum values if that attribute is present. The integer value of Keys.D5 is 0x35, a combination of 0x01 + 0x04 + 0x10 + 0x20. Respectively Keys.LButton, MButton, ShiftKey and Space. Clearly this is unhelpful in your case, cast to (int) in the watch expression.
Well, that explains what is going on. The one thing you really want to avoid is trying to translate the virtual key to a typing key yourself. Have a look at the ToUnicodeEx() Windows api function, the one you have to use to do it properly. You can use the Control.ModifierKeys property to detect the difference between a plain Keys.D5 and one that's pressed with the Shift key down.
Btw: you should also trap WM_SYSKEYDOWN, the message that's generated when the Alt key is down. Message 0x104.
Trap WM_CHAR, it'll already be translated with the shift and you should receive the %.

DragDropEffects.Scroll: why -2147483648 not 8?

A standard enumeration in System.Windows.Forms:
[Flags]
public enum DragDropEffects
{
Scroll = -2147483648,
//
// Summary:
// The combination of the System.Windows.DragDropEffects.Copy, System.Windows.Forms.DragDropEffects.Link,
// System.Windows.Forms.DragDropEffects.Move, and System.Windows.Forms.DragDropEffects.Scroll
// effects.
All = -2147483645,
None = 0,
Copy = 1,
Move = 2,
Link = 4,
}
Quite a strange value for Scroll, don't you think?
As I understand these values all come from "the old times" of COM\OLE DROPEFFECT... But why were they chosen so in the first place? Did author try to reserve the interval between 8 and 0x80000000 for something? Is it usefule somehow or is there an interesting story behind it or it's just another long-lived illustration of the YAGNI principle?
It is a status flag, separate from the principal drop effects (Copy/Move/Link). Short from leaving room for future drop effects, picking the high bit allows a trick like checking if the value is negative. Same kind of idea as an HRESULT or the GetAsyncKeyState return value.
Yes, it looks like an "interesting" hack of some sort. Common sense would suggest using 8, but maybe there's some Windows version related reason why 8 couldn't be used, and so the author used -2147483645 (-0x80000000) instead. It's not that unusual a number - whoever wrote it is just starting with a binary '1' from the high significant end rather than the low significant end.
Perhaps scrolling was regarded in some other group of drag/drop effects to copy/move/link, and so the author wanted to place it at the other end of the word, along with any other future similarly different effects.
Maybe there's some awful piece of logic somewhere to test to see if a DragDropEffects variable is greater than zero (intending to mean "anything that isn't none"), and Scroll should not fall in that range?
Bit of a mystery. At the very least you'd think they would put the constant in as hex, to show it's not just some totally random number.
This allows a quick check for > 0 in order to know whether Copy, Move, or Link are being invoked. It excludes None as well as Scroll.

How to send more than one CopyPixelOperation through CopyFromScreen?

I am using the managed Graphics.CopyFromScreen method to take a Bitmap screenshot of a region on the screen.
Everything is working using the CopyPixelOperation.SourceCopy enum as the flag to the CopyFromScreen method... but unfortunately I need to capture layered / transparent windows that are in the region I'm trying to capture... and with only the SourceCopy enum these do not get picked up in the resulting image. This can be solved using the CopyPixelOperation.CaptureBlt... but I can't find a way to do this:
Graphics.CopyFromScreen(left, top, 0, 0, size, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); // note the binary OR operator
... as this results in a "Bitwise Or is not possible on type enum" style error from Resharper - upon which I researched the error discovering that bitwise operations on enums happens on the numeric representation of the enum values and you will end up with another one of the available enum values (opposed to two of them).
Is there a way to pass two enums to this function or a similar function? I have a preference in staying managed if possible.
MSDN Documentation
CopyFromScreen
CopyPixelOperation
As per MSDN:
copyPixelOperation
Type: System.Drawing..::.CopyPixelOperation
One of the CopyPixelOperation values.
You can't combine those flags, you probably want to use CopyPixelOperation.CaptureBlt alone anyway.

Way to turn on keyboard's caps-lock light without actually turning on caps-lock

I'm writing a program that uses Caps Lock as a toggle switch. It would be nice to set the LED of the key to show that my program is on or off, like the Caps Lock key does naturally.
I know that I could just SendInput('Capslock'); or whatever to actually turn caps-lock on and off. But my application is a typing program, and I don't want to have to deal with translating the all-caps keys that turning it on would give me into their lower/upper cases. I might go that route eventually, but not for this version.
I would however be interested in just turning on the LED light without actually turning on Caps Lock. Is there a way to do that?
There is a plugin for Miranda IM named "Keyboard Notify Ext." which contains in its source code a C implementation of controlling LEDs. See file keyboard.c in the source code. Probably you can port it to C#.
Here are most interesting highlights from source code:
mir_snprintf(aux1, sizeof(aux1), "Kbd%d", i);
mir_snprintf(aux2, sizeof(aux2), "\\Device\\KeyboardClass%d", i);
DefineDosDevice(DDD_RAW_TARGET_PATH, aux1, aux2);
mir_snprintf(aux1, sizeof(aux1), "\\\\.\\Kbd%d", i);
hKbdDev[i] = CreateFile(aux1, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
...
result |= DeviceIoControl(hKbdDev[i], IOCTL_KEYBOARD_SET_INDICATORS, &InputBuffer, DataLength, NULL, 0, &ReturnedLength, NULL);
I'm pretty sure you can't toggle the LED without toggling the actual caps lock, unless you were writing a keyboard driver. (I'm not recommending that!)

Categories