Reputation: 801
I am trying to make a program that blocks some Windows hotkeys and similar functionality, to help the user avoid doing an action they might not want to make in a flash program that runs in a browser.
The program I wish to make will be a .NET C# WinForms program, that acts as a keyboard hook.
Currently, it can block key combinations like Ctrl+W, Alt+F4. But those are just "secondary functions". They are working, by using the RegisterHotkey method.
What I would really want to achieve, is to be able to disable, if possible, in any way, the holding down of Ctrl+Left Mouse Button Click, holding down of Alt+Left Mouse Button Click, and Shift+Left Mouse Button Click.
The way to achieve it should also preferably "unhook" them and enable them again when the program is closed.
Here is the relevant snippet of the current code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace HotKeyBlocker
{
public partial class HotkeyBlocker : Form
{
//Credits to: http://www.codeproject.com/KB/cs/Kiosk_CS.aspx?display=Print
//And: http://support.microsoft.com/kb/318804
#region Dynamic Link Library Imports for Hotkeys
[DllImport("user32.dll")]
private static extern int FindWindow(string cls, string wndwText);
[DllImport("user32.dll")]
private static extern int ShowWindow(int hwnd, int cmd);
[DllImport("user32.dll")]
private static extern long SHAppBarMessage(long dword, int cmd);
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hwnd, int id, int
fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern int UnregisterHotKey(IntPtr hwnd, int id);
#endregion
#region Modifier Constants and Variables
// Constants for modifier keys
private const int USE_NONE = 0;
private const int USE_ALT = 1;
private const int USE_CTRL = 2;
private const int USE_SHIFT = 4;
private const int USE_WIN = 8;
// Hot key ID tracker
short mHotKeyId = 0;
#endregion
public HotkeyBlocker()
{
InitializeComponent();
// Related browser window key combinations
// -- Some things that you may want to disable --
//CTRL+A Select all
//CTRL+B Organize favorites
//CTRL+C Copy
//CTRL+F Find
//CTRL+H View history
//CTRL+L Open locate
//CTRL+N Open new browser window
//CTRL+O Open locate
//CTRL+P Print
//CTRL+R Refresh
//CTRL+S Save
//CTRL+V Paste
//CTRL+W Close
//CTRL+X Cut
//ALT+F4 Close
// Disable ALT+F4 - exit
RegisterGlobalHotKey(Keys.F4, USE_ALT);
// Disable CTRL+F4 - close tab
RegisterGlobalHotKey(Keys.F4, USE_CTRL);
// Disable CTRL+W - exit
RegisterGlobalHotKey(Keys.W, USE_CTRL);
// Disable CTRL+N - new window
RegisterGlobalHotKey(Keys.N, USE_CTRL);
// Disable CTRL+S - save
RegisterGlobalHotKey(Keys.S, USE_CTRL);
// Disable CTRL+A - select all
RegisterGlobalHotKey(Keys.A, USE_CTRL);
// Disable CTRL+C - copy
RegisterGlobalHotKey(Keys.C, USE_CTRL);
// Disable CTRL+X - cut
RegisterGlobalHotKey(Keys.X, USE_CTRL);
// Disable CTRL+V - paste
RegisterGlobalHotKey(Keys.V, USE_CTRL);
// Disable CTRL+B - organize favorites
RegisterGlobalHotKey(Keys.B, USE_CTRL);
// Disable CTRL+F - find
RegisterGlobalHotKey(Keys.F, USE_CTRL);
// Disable CTRL+H - view history
RegisterGlobalHotKey(Keys.H, USE_CTRL);
// Disable CTRL+P - print
RegisterGlobalHotKey(Keys.P, USE_CTRL);
// Disable CTRL+Tab - tab through browser tabs
RegisterGlobalHotKey(Keys.Tab, USE_CTRL);
// Disable CTRL+T - new browser tab
RegisterGlobalHotKey(Keys.T, USE_CTRL);
// Disable CTRL+O - open
RegisterGlobalHotKey(Keys.O, USE_CTRL);
// Disable CTRL+D - Bookmarks
RegisterGlobalHotKey(Keys.D, USE_CTRL);
// Disable ALT+Esc - tab through open applications
RegisterGlobalHotKey(Keys.Escape, USE_ALT);
// Disable F1 Key - help in most applications
RegisterGlobalHotKey(Keys.F1, USE_NONE);
// Disable ALT+Tab - tab through open applications
//RegisterGlobalHotKey(Keys.Tab, USE_ALT); <-- Does not work on W8
// hide the task bar - not a big deal, they can
// still CTRL+ESC to get the start menu; for that
// matter, CTRL+ALT+DEL also works; if you need to
// disable that you will have to violate SAS and
// monkey with the security policies on the machine
//ShowWindow(FindWindow("Shell_TrayWnd", null), 0);
}
private void RegisterGlobalHotKey(Keys hotkey, int modifiers)
{
try
{
// increment the hot key value - we are just identifying
// them with a sequential number since we have multiples
mHotKeyId++;
if (mHotKeyId > 0)
{
// register the hot key combination
if (RegisterHotKey(this.Handle, mHotKeyId, modifiers,
Convert.ToInt16(hotkey)) == 0)
{
// tell the user which combination failed to register
// this is useful to you, not an end user; the user
// should never see this application run
MessageBox.Show("Error: " +
mHotKeyId.ToString() + " - " +
Marshal.GetLastWin32Error().ToString(),
"Hot Key Registration");
}
}
}
catch
{
// clean up if hotkey registration failed -
// nothing works if it fails
UnregisterGlobalHotKey();
}
}
private void UnregisterGlobalHotKey()
{
// loop through each hotkey id and
// disable it
for (int i = 0; i < mHotKeyId; i++)
{
UnregisterHotKey(this.Handle, i);
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// if the message matches,
// disregard it
const int WM_HOTKEY = 0x312;
if (m.Msg == WM_HOTKEY)
{
// Ignore the request or each
// disabled hotkey combination
}
}
private void HotkeyBlocker_FormClosed(object sender, FormClosedEventArgs e)
{
// unregister the hot keys
UnregisterGlobalHotKey();
// show the taskbar - does not matter really
//ShowWindow(FindWindow("Shell_TrayWnd", null), 1);
}
}
}
I do know that it might have something to do with a method called SetWindowsHookEx, but I do not know how to use it to achieve it if it can be done.
It would be best if the best way to achieve it will not clash with the existing code I have, and can work alongside it.
I am also trying to ensure this program can be compatible with all versions of Windows from Windows XP and up, for both 32-bit and 64-bit if possible. I am using Visual Studio 2010 Professional, on a Windows 8 Professional 64-bit computer.
I hope this would be specific enough? It's my first time posting on here... (though I have searched through this site many times in the past)
(I have tried using "RegisterGlobalHotKey(Keys.LButton, USE_CTRL)", "RegisterGlobalHotKey(Keys.LButton, USE_ALT)" and "RegisterGlobalHotKey(Keys.LButton, USE_SHIFT)", but they do not work at all.)
Upvotes: 1
Views: 4295
Reputation: 801
I believe I have finally found the solution to my question long ago, and also because it seems no one knows how to solve it.
I have not tested this myself personally yet, but if I combine the mouse click detecting functionality, from http://www.codeproject.com/Articles/32556/Auto-Clicker-C , alongside with the code below...
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public SendInputEventType type;
public KeybdInputUnion mkhi;
}
[StructLayout(LayoutKind.Explicit)]
public struct KeybdInputUnion
{
[FieldOffset(0)]
public KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
public enum SendInputEventType : int
{
InputKeyboard
}
public enum KeyCode : ushort
{
/// <summary>
/// Right shift
/// </summary>
RSHIFT = 0xa1,
/// <summary>
/// Shift key
/// </summary>
SHIFT = 0x10,
/// <summary>
/// Right control
/// </summary>
RCONTROL = 0xa3,
/// <summary>
/// Left control
/// </summary>
LCONTROL = 0xa2,
/// <summary>
/// Left shift
/// </summary>
LSHIFT = 160,
/// <summary>
/// Ctlr key
/// </summary>
CONTROL = 0x11,
/// <summary>
/// Alt key
/// </summary>
ALT = 18,
}
[DllImport("User32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
void YourMethodHereLikeTimerTickOrWE()
{
if (Control.ModifierKeys == Keys.Shift)
{
SendKeyUp(KeyCode.SHIFT);
}
if (Control.ModifierKeys == Keys.Alt)
{
SendKeyUp(KeyCode.ALT);
}
if (Control.ModifierKeys == Keys.Control)
{
SendKeyUp(KeyCode.CONTROL);
}
if (Control.ModifierKeys == (Keys.Control | Keys.Shift))
{
SendKeyUp(KeyCode.CONTROL);
SendKeyUp(KeyCode.SHIFT);
}
if (Control.ModifierKeys == (Keys.Control | Keys.Alt))
{
SendKeyUp(KeyCode.CONTROL);
SendKeyUp(KeyCode.ALT);
}
if (Control.ModifierKeys == (Keys.Alt | Keys.Shift))
{
SendKeyUp(KeyCode.ALT);
SendKeyUp(KeyCode.SHIFT);
}
if (Control.ModifierKeys == (Keys.Alt | Keys.Shift | Keys.Control))
{
SendKeyUp(KeyCode.ALT);
SendKeyUp(KeyCode.SHIFT);
SendKeyUp(KeyCode.CONTROL);
}
}
public static void SendKeyUp(KeyCode keyCode)
{
INPUT input = new INPUT
{
type = SendInputEventType.InputKeyboard,
};
input.mkhi.ki = new KEYBDINPUT();
input.mkhi.ki.wVk = (ushort)keyCode;
input.mkhi.ki.wScan = 0;
input.mkhi.ki.dwFlags = 2;
input.mkhi.ki.time = 0;
input.mkhi.ki.dwExtraInfo = IntPtr.Zero;
//INPUT[] inputs = new INPUT[] { input };
if (SendInput(1, ref input, Marshal.SizeOf(typeof(INPUT))) == 0)
throw new Exception();
}
It should theorectically work. I have used both parts seperately before without any issues so far.
Also, for the code from CodeProject, for a particular method called
private static void EnsureSubscribedToGlobalMouseEvents()
There is a line of code in there that has to be changed to be properly used in C# NET 4 Framework and up properly.
Namely, the code
s_MouseHookHandle = SetWindowsHookEx(
WH_MOUSE_LL,
s_MouseDelegate,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
Should be changed to this:
s_MouseHookHandle = SetWindowsHookEx(
WH_MOUSE_LL,
s_MouseDelegate,
LoadLibrary("user32.dll"),
0);
In order to use LoadLibrary, you need to call this P/Invoke below:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
References:
If you think you have a better answer, please provide one, as I would very much like to see it :)
Upvotes: 1