Media keys in WndProc not firing - c#

I am creating a media player in WinForms, C#. I want to respond to the user pressing the multimedia keys on the keyboard using the following code that can be found all over the internet:
public const int WM_APPCOMMAND = 0x0319;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_APPCOMMAND)
{
switch ((int)m.LParam)
{
case 14: // MediaPlayPause
TogglePlayPause();
break;
default:
break;
}
}
base.WndProc(ref m);
}
But it won't work. It just never recieves the key command. Media keys work with every other application (and the TogglePlayPause() method works as well).

The value reported by LParam is a composite one.
As specified in the Docs, about WM_APPCOMMAND, the value can be extracted using:
cmd = GET_APPCOMMAND_LPARAM(lParam);
uDevice = GET_DEVICE_LPARAM(lParam);
dwKeys = GET_KEYSTATE_LPARAM(lParam);
You need the cmd value.
In C#, it can be coded as:
private const int WM_APPCOMMAND = 0x0319;
private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_APPCOMMAND:
int cmd = (int)m.LParam >> 16 & 0xFF;
switch (cmd)
{
case APPCOMMAND_MEDIA_PLAY_PAUSE:
TogglePlayPause();
break;
default:
break;
}
m.Result = (IntPtr)1;
break;
default:
break;
}
}
Edit:
Some meaningful links about KeyBoard Hooks and registering HotKeys.
On SetWindowHookEx:
SetWindowsHookEx WH_KEYBOARD_LL not getting events
Low-Level Keyboard Hook in C#
On RegisterHotKey:
Capture a keyboard keypress in the background

Related

How to intercept unminizing? [duplicate]

Is there an event that is fired when you maximize a Form or un-maximize it?
Before you say Resize or SizeChanged: Those get only fired if the Size actually changes. If your window happens to be equal in size to the maximized window, they do not fire. Location looks like the next best bet, but that again feels like gambling on a coincidence.
Suprising that no one mentioned the inbuilt .NET method.
This way you don't need to override the Window Message Processing handler.
It even captures maximize/restore events caused by double-clicking the window titlebar, which the WndProc method does not.
Copy this in and link it to the "Resize" event handler on the form.
FormWindowState LastWindowState = FormWindowState.Minimized;
private void Form1_Resize(object sender, EventArgs e) {
// When window state changes
if (WindowState != LastWindowState) {
LastWindowState = WindowState;
if (WindowState == FormWindowState.Maximized) {
// Maximized!
}
if (WindowState == FormWindowState.Normal) {
// Restored!
}
}
}
You can do this by overriding WndProc:
protected override void WndProc( ref Message m )
{
if( m.Msg == 0x0112 ) // WM_SYSCOMMAND
{
// Check your window state here
if (m.WParam == new IntPtr( 0xF030 ) ) // Maximize event - SC_MAXIMIZE from Winuser.h
{
// THe window is being maximized
}
}
base.WndProc(ref m);
}
This should handle the event on any window. SC_RESTORE is 0xF120, and SC_MINIMIZE is 0XF020, if you need those constants, too.
Another little addition in order to check for the restore to the original dimension and position after the maximization:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// WM_SYSCOMMAND
if (m.Msg == 0x0112)
{
if (m.WParam == new IntPtr(0xF030) // Maximize event - SC_MAXIMIZE from Winuser.h
|| m.WParam == new IntPtr(0xF120)) // Restore event - SC_RESTORE from Winuser.h
{
UpdateYourUI();
}
}
}
Hope this help.
I believe the code is even simpler than that. You don't need to save the lastState because the WindowState is checked anytime when the event is fired.
private void MainForm_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Maximized)
{
spContainer.SplitterDistance = 1000;
}
if (WindowState == FormWindowState.Normal)
spContainer.SplitterDistance = 500;
}
I had the same problem, and I could solve it without overriding.
Because I have a PictureBox in dock mode "Fill" I could use it's SizeChanged event, which fired also on maximizing the window.
I hope this part of code will be useful.
if (m.Msg == User32.WM_WINDOWPOSCHANGING && IsHandleCreated)
{
User32.WINDOWPLACEMENT wp = new User32.WINDOWPLACEMENT();
wp.length = Marshal.SizeOf(typeof(User32.WINDOWPLACEMENT));
User32.GetWindowPlacement(Handle, ref wp);
switch (wp.showCmd)
{
case User32.SW_RESTORE:
case User32.SW_NORMAL:
case User32.SW_SHOW:
case User32.SW_SHOWNA:
case User32.SW_SHOWNOACTIVATE:
_windState = FormWindowState.Normal;
if (wp.showCmd == User32.SW_RESTORE)
Update();
break;
case User32.SW_SHOWMAXIMIZED:
_windState = FormWindowState.Maximized;
SetMaximumSize();
break;
case User32.SW_SHOWMINIMIZED:
case User32.SW_MINIMIZE:
case User32.SW_SHOWMINNOACTIVE:
_windState = FormWindowState.Minimized;
break;
}
}
private void SetMaximumSize()
{
Screen screen = Screen.FromControl(this);
if (screen != null && !screen.WorkingArea.IsEmpty)
{
int sizeDiff = this.Size.Width - this.ClientSize.Width;
var maxSize = new Size(screen.WorkingArea.Width + sizeDiff, screen.WorkingArea.Height + sizeDiff);
this.MaximumSize = maxSize;
}
}
#region Window State
public const int SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9;
#endregion Window State
If there's no obvious event to listen for, you're probably going to need to hook into the Windows API and catch the appropriate message (Google turns up that you'll want to intercept the WM_SYSCOMMAND message: http://www.codeguru.com/forum/archive/index.php/t-234554.html).
I'm a newbie here so comments not allowed, but this IS a comment to the clean answer by GeoTarget:
The first line OUGHT to be slightly changed to nullable, to catch if the form is started Minimized:
FormWindowState? LastWindowState = null;
And a banal suggestion: Move the assignment of LastWindowState to after the "if"s, so the user can easily check not only what you go to, but also what it came from:
FormWindowState? LastWindowState = null;
private void Form1_Resize(object sender, EventArgs e) {
// When window state changes
if (WindowState != LastWindowState) {
if (WindowState == FormWindowState.Maximized) {
// Maximized!
}
if (WindowState == FormWindowState.Normal) {
// Restored!
}
LastWindowState = WindowState;
}
}
A complete solution with maximize, minimize, restore and correct remove of the lower bits which are used for internal purposes only.
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MAXIMIZE = 0xF030;
const int SC_MINIMIZE = 0xF020;
const int SC_RESTORE = 0xF120;
// Call beofre - don't use when "call after" is used
// dependig on the needs may be called before, after or even never (see below)
// base.WndProc(ref m);
if (m.Msg == WM_SYSCOMMAND)
{
/// <see cref="https://learn.microsoft.com/en-us/windows/win32/menurc/wm-syscommand"/>
/// Quote:
/// In WM_SYSCOMMAND messages, the four low - order bits of the wParam parameter
/// are used internally by the system.To obtain the correct result when testing
/// the value of wParam, an application must combine the value 0xFFF0 with the
/// wParam value by using the bitwise AND operator.
int wParam = (m.WParam.ToInt32() & 0xFFF0);
Debug.WriteLine($"Received param: { Convert.ToString(wParam, 16) } ");
if (wParam == SC_MAXIMIZE)
{
}
if (wParam == SC_MINIMIZE)
{
}
if (wParam == SC_RESTORE)
{
}
}
// Call after - don't use when "call before" is used
base.WndProc(ref m);
}
' Great tip. So if it helps to VisualBasic In Code
Private Const WM_SYSCOMMAND As Integer = &H112
Private Const SC_MAXIMIZE As Integer = &HF030
' # WndProcess 루프함수
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg.Equals(WM_SYSCOMMAND) Then
If (m.WParam.ToInt32.Equals(SC_MAXIMIZE)) Then
Me.p_FullScreen()
Return
End If
End If
MyBase.WndProc(m)
End Sub

Moving a borderless form results in weird side effects

I am fairly new to C#.
In order to get a modern design application, I decided to make my form borderless. I then found a code snippet for making it possible to move my borderless form, which works perfectly fine.
private const int WM_NCHITTEST = 0x84;
private const int HT_CAPTION = 0x2;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg) {
case WM_NCHITTEST:
m.Result = (IntPtr)(HT_CAPTION);
break;
}
}
I also need to fetch the form maximize event, and found another code snippet, which again, works perfectly. At least if I use them independently.
case WM_SYSCOMMAND:
if (IsMaximized == false)
{
IsMaximized = true;
Btn_Ribbon_MaximizeMinimize.Image = Properties.Resources.Img_MinimizeForm;
this.WindowState = FormWindowState.Maximized;
}
else if (IsMaximized == true)
{
IsMaximized = false;
Btn_Ribbon_MaximizeMinimize.Image = Properties.Resources.Img_MaximizeForm;
this.WindowState = FormWindowState.Normal;
}
break;
Now here comes the weird part. If I use them both together...
#region Move borderless Form
private const int WM_NCHITTEST = 0x84;
private const int HT_CLIENT = 0x1;
private const int HT_CAPTION = 0x2;
private const int WM_SYSCOMMAND = 0x0112;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg) {
case WM_NCHITTEST:
m.Result = (IntPtr)(HT_CAPTION);
break;
case WM_SYSCOMMAND:
if (IsMaximized == false)
{
IsMaximized = true;
Btn_Ribbon_MaximizeMinimize.Image = Properties.Resources.Img_MinimizeForm;
this.WindowState = FormWindowState.Maximized;
}
else if (IsMaximized == true)
{
IsMaximized = false;
Btn_Ribbon_MaximizeMinimize.Image = Properties.Resources.Img_MaximizeForm;
this.WindowState = FormWindowState.Normal;
}
break;
}
}
#endregion
...I get all kind of weird side effects:
A single click on my form is enought for it to maximize itself
After minimizing my application, clicking its icon in the windows taskbar again, does not result in it normalizing, but maximizing
How can I get rid of those side effects, or even better than a workaround, is there a better way to make this work?
The WM_SYSCOMMAND message can contain information about more than just the maximize event. When you handle the WM_NCHITTEST and tell the OS that the caption bar was clicked, it also causes a WM_SYSCOMMAND when you release the mouse button.
You should inspect the m.WParam value to determine what action was performed.
The documentation mentions SC_MAXIMIZE (0xF030) but on my machine the value is actually SC_MAXIMIZE2 (0xF032). I can't find any documentation about that value, but this answer also mentions it.

How to detect a USB HDD programatically using c#

Hi I have to develop an app that detects a USB HDD using C#. Is it possible?. I have a code for detecting a USB Mass storage device.
I added reference to System.Runtime.InteropServices
I tried to override WndProc() method. I will include the snippet
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if ((message.Msg != WM_DEVICECHANGE) || (message.LParam == IntPtr.Zero))
return;
DEV_BROADCAST_VOLUME volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(message.LParam, typeof(DEV_BROADCAST_VOLUME));
if (volume.dbcv_devicetype == DBT_DEVTYP_VOLUME)
{
switch (message.WParam.ToInt32())
{
// New device inserted...
case DBT_DEVICEARRIVAL:
MessageBox.Show(
string.Format("A storage device has been inserted; Drive :{0}", ToDriveName(volume.dbcv_unitmask)), "Detect USB");
break;
// Device Removed.
case DBT_DEVICEREMOVECOMPLETE:
MessageBox.Show("Storage has been removed.", "Detect USB");
break;
}
}
}
}
I developed an application for the USB Pen drives below is my declarations:
const int WM_DEVICECHANGE = 0x0219;
const int DBT_DEVICEARRIVAL = 0x8000;
const int DBT_DEVICEREMOVALCOMPLETE = 0x8004;
const int DBT_DEVTYPVOLUME = 0x00000002;
Below is my override of WinProc :
protected override void WndProc(ref Message m)
{
try
{
if (m.Msg == WM_DEVICECHANGE)
{
DEV_BROADCAST_VOLUME vol = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
if ((m.WParam.ToInt32() == DBT_DEVICEARRIVAL) && (vol.dbcv_devicetype == DBT_DEVTYPVOLUME))
{
usb_drive = DriveMaskToLetter(vol.dbcv_unitmask).ToString();
if (usb_drive.Replace(" ", "").Length > 0)
{
// USB Inserted
}
}
if ((m.WParam.ToInt32() == DBT_DEVICEREMOVALCOMPLETE) && (vol.dbcv_devicetype == DBT_DEVTYPVOLUME))
{
//USB Removed
}
}
base.WndProc(ref m);
}
catch
{
}
}

How to return a value in WndProc for C#?

In my application, I hid the cursor using SetCursor(NULL) and to make sure that Windows does not reset the cursor state, I handled WM_SETCURSOR in my WndProc method.
However in the msdn documentation for C++, in order to handle WM_SETCURSOR I have to return TRUE. However in C#'s WndProc, it is a void method so I cannot return any value.
So how would I accomplish that return statement in C#?
C++ Variant:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
{
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT)
{
SetCursor(hCursor);
return TRUE;
}
break;
}
You can return without calling base.WndProc:
protected override void WndProc(ref Message m){
if(m.Msg == WM_SETCURSOR) {
int lowWord = (m.LParam.ToInt32() << 16) >> 16;
if(lowWord == HTCLIENT){
SetCursor(hCursor);
return;
}
}
base.WndProc(ref m);
}
I guess this also works (I've experienced it with some messages but not sure with WM_SETCURSOR):
protected override void WndProc(ref Message m){
base.WndProc(ref m);
if(m.Msg == WM_SETCURSOR) {
int lowWord = (m.LParam.ToInt32() << 16) >> 16;
if(lowWord == HTCLIENT){
SetCursor(hCursor);
m.Result = new IntPtr(1);
}
}
}

How do you prevent a windows from being moved?

How would i go about stopping a form from being moved. I have the form border style set as FixedSingle and would like to keep it this way because it looks good in vista :)
Take a look at this link. You might be interested in option #3. It will require you to wrap some native code, but should work. There's also a comment at the bottom of the link that shows an easier way to do it. Taken from the comment (can't take credit for it, but I'll save you some searching):
protected override void WndProc(ref Message message)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch(message.Msg)
{
case WM_SYSCOMMAND:
int command = message.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref message);
}
You can set the FormBorderStyle property of the Form to None
this.FormBorderStyle=System.Windows.Forms.FormBorderStyle.None
I found this to stop the form from moving (its in c#)
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (m.Msg)
{
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref m);
}
Found here
Try to override WndProc:
protected override void WndProc(ref Message m)
{
const int WM_NCLBUTTONDOWN = 161;
const int WM_SYSCOMMAND = 274;
const int HTCAPTION = 2;
const int SC_MOVE = 61456;
if ((m.Msg == WM_SYSCOMMAND) && (m.WParam.ToInt32() == SC_MOVE))
{
return;
}
if ((m.Msg == WM_NCLBUTTONDOWN) && (m.WParam.ToInt32() == HTCAPTION))
{
return;
}
base.WndProc(ref m);
}
It's not all pretty (there is some flashing going on when you try to move the form), but you can use the LocationChanged property to keep the form where you want it:
private Point _desiredLocation;
// assign the _desiredLocation variable with the form location at some
// point in the code where you know that the form is in the "correct" position
private void Form_LocationChanged(object sender, EventArgs e)
{
if (this.Location != _desiredLocation)
{
this.Location = _desiredLocation;
}
}
Out of curiousity; why would you want to do this?
In Windows, the WS_CAPTION style is the non-client area that allows your window to be moved with a mouse. So the easiest way to do what you want is to remove this style from your window.
However, if you need to have a caption and still achieve what you want, then the next style would be to capture the WM_NCHITTEST message and check for HTCAPTION. If the code is HTCAPTION, return NTNOWHERE instead. This will prevent the default window procedure from executing the default move window thing.
It's not a good practice to make your form immovable. I'd think agfain about it if I were you.
Anyway, you can do this by overridding the WinProc to disable the [Move] menuitem from the system menu.
[DllImport("user32.dll")]
private static extern Int32 EnableMenuItem ( System.IntPtr hMenu , Int32uIDEnableItem, Int32 uEnable);
private const Int32 HTCAPTION = 0×00000002;
private const Int32 MF_BYCOMMAND =0×00000000;
private const Int32 MF_ENABLED =0×00000000;
private const Int32 MF_GRAYED =0×00000001;
private const Int32 MF_DISABLED =0×00000002;
private const Int32 SC_MOVE = 0xF010;
private const Int32 WM_NCLBUTTONDOWN = 0xA1;
private const Int32 WM_SYSCOMMAND = 0×112;
private const Int32 WM_INITMENUPOPUP = 0×117;
protected override void WndProc(ref System.Windows.Forms.Message m )
{
if (m.Msg == WM_INITMENUPOPUP)
{
//handles popup of system menu
if ((m.LParam.ToInt32() / 65536) != 0) // 'divide by 65536 to get hiword
{
Int32 AbleFlags = MF_ENABLED;
if (!Moveable)
{
AbleFlags = MF_DISABLED | MF_GRAYED; // disable the move
}
EnableMenuItem(m.WParam, SC_MOVE, MF_BYCOMMAND | AbleFlags);
}
}
if (!Moveable)
{
if (m.Msg == WM_NCLBUTTONDOWN) //cancels the drag this is IMP
{
if (m.WParam.ToInt32() == HTCAPTION) return;
}
if (m.Msg == WM_SYSCOMMAND) // Cancels any clicks on move menu
{
if ((m.WParam.ToInt32() & 0xFFF0) == SC_MOVE) return;
}
}
base.WndProc(ref m);
}
Also, you can handle OnMove event of your form. But I think this will cause some flickering:
private void Form1_Move(object sender, EventArgs e)
{
this.Location = defaultLocation;
}
Just change the FormBorderStyle property to None.
change the Form property StartPostion to Manual.
Then, handle the LocationChanged event:
private void frmMain_LocationChanged(object sender, EventArgs e)
{
Location = new Point(0, 0);
}
Go to form events-> Location changed
write the following code
Location = new Point(this.Width,this.Height);
I would question your need to make the form unmovable. This doesn't sound nice. You could of course save the location of the window when the window closes and reopen the window into that position. That gives the user some control over where the window should be located.
You can subscribe to the Form.Move event and reposition from it.
Just reset the location on formlocation_changed event to where it was i.e. set the Form.Location to a variable before it's moved and when the user tries to move it, it will go back to the variable location you set it to.
Private Sub MyFormLock()
Me.Location = New Point(0, 0)
End Sub
Private Sub SearchSDR_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
Call MyFormLock()
End Sub
You can try:
this.Locked = true;

Categories