In certain cases when programming with libraries written in C involving callbacks, I like to use Lambda expressions; however, if I need to alter the state of a class member variable I can't juts pass this into a stateless(function pointer) lambda. But I can assign this to a data in a context structure. What I find strange is being able to access that member variable even if it's private in the class.
Here's an example code I wrote to demonstrate.
#include <iostream>
using std::cout;
typedef struct extradatatype{
void* data;
}extradata;
extradata e = {0};
typedef void(*callback)(extradata* e);
void cb(callback c){
c(&e);
}
class Test{
private:
int x;
public:
Test(int x){
this->x = x;
}
void setcb(){
cb([](extradata* e){
Test* self = reinterpret_cast<Test*>(e->data);
self->x = 20;
});
}
int getx(){
return x;
}
};
int main(){
Test t(10);
e.data = &t;
t.setcb();
cout << t.getx();
return 0;
}
In the Lambda expression Test* self is assigned to e->data but I can access self->x as if it were a public member instead of private. So what I'm confused about is, is the lambda expression expression being executed within the stack/context of the setcb function or is it being executed elsewhere as its own function but C++ is doing some weird trick to allow private members to be accessed. Because I assume a stateless lambda is really no different than a non member static function which has no access to private members of a class.
Since your lambda function is defined within the class Test context, it will have access to class Test private member (regardless if it's this.x or self.x where self is of type Test). It is similar to this example:
class Example {
private:
int x;
public:
int f(Example e) {
return e.x;
}
};
where, since f is a member of Example, it can access e.x because e has type Example.
If you move your lambda function definition out of the class context you'll see the expected error message:
void outside(extradata* e);
class Test{
private:
int x;
public:
void setcb(){
cb(outside);
}
};
void outside(extradata* e) {
Test* self = reinterpret_cast<Test*>(e->data);
self->x = 20; // error here!
}
test.cpp:32:11: error: 'int Test::x' is private within this context
self->x = 20;
^
I have a std map that combines a string with a function pointer like:
std::map<std::string, void (*)()> funcs {
{"print", &h::print},
{"scan", &h::scan_cmd},
{"connect", &h::stream},
{"stream", &h::stream}
};
where h is the enclosing class in which this map has been initialized:
class h {
public:
void print();
void scan();
void connect();
void stream();
std::map<std::string, void (*)()> funcs {
{"print", &h::print},
{"scan", &h::scan_cmd},
{"connect", &h::stream},
{"stream", &h::stream}
};
};
I get this error:
No matching constructor for initialization of 'std::map<std::string, void (*)()>' (aka 'map<basic_string<char>, void (*)()>')
I've also tried puttting the map in this form:
std::map<std::string, void (*)()> funcs;
funcs["print"] = &print;
funcs["scan"] = &scan_cmd;
funcs["connect"] = &stream;
funcs["stream"] = &stream;
But then I got this error:
Size of array has non-integer type ' const char [6]'
I'm not exactly sure where the problem is - my guess is that it's with the void (*) () portion. I'm sure this is a c++ 11 compiler.
void (*)()
is pointer to ordinary function which takes no arguments and returns no value.
In your example print, stream, scan_cmd are non-static member functions of h class. Syntax to define pointer to member functions of h class looks like
void (h::*)()
Try:
std::map<std::string, void (h::*)()> funcs {
{"print", &h::print},
{"connect", &h::stream},
{"stream", &h::stream}
};
public int Number;
public int NumberTwo;
int GiveDouble() {
return Number * 3;
return NumberTwo * 5; // compiler says this is unreachable
}
void Start() {
int returnedNumber = GiveDouble();
// ...
int returnedNumber2 = GiveDouble();
// ...
}
Or image
I am trying to return 2 values from one function to 2 distinct variables in an another function.
In this case... I am sending 2 values from GiveDouble() function to the variables ReturnedNumber and ReturnedNumber2 in the Start() function. The inputs are given in UNIY engine.
Please check the picture I added..where the popup says unreachable code detected at the second return function.
My question may be not so reasonable. I am still learning now. I wanna know why is that coming?
The compiler is right; one call can only return one value. You're calling the method twice, but that doesn't resume a method - it calls it again from the start, so the first result will be returned each time.
One option here could be "value tuples":
(int, int) GiveDouble()
{
return (Number * 3, NumberTwo * 5);
}
void Start()
{
(int returnedNumber, int returnedNumber2) = GiveDouble();
}
or with better naming on the tuple:
(int Foo, int Bar) GiveDouble()
{
return (Number * 3, NumberTwo * 5);
}
just to be clear, when you say return Number * 3; you end that method there, and return Number * 3. return makes the compiler drop out of the method and move on, so basically its like saying your done with that method, thats why the code is unreachable.
int GiveDouble() {
return Number * 3;
return NumberTwo * 5; // compiler says this is unreachable
}
erase this and try
int TimesThree(int one){
return one*3;
}
int TimesFive(int two){
return two*5;
}
then change:
int returnedNumber = GiveDouble();
// ...
int returnedNumber2 = GiveDouble();
to
int returnedNumber = TimesThree(Number);
// ...
int returnedNumber2 = TimesFive(NumberTwo);
I make a interface class in C++ for voice recognition, i´m using the Julius API. http://julius.sourceforge.jp/en_index.php?q=index-en.html.
Well, my class has some events, these events will be triggered by the Julius API.
The Julius API has the function call callback_add with this signature:
int callback_add (Recog *recog, int code, void(*func)(Recog *recog, void *data), void data)
I using some 'proxy' functions to Invoke the events and passing this functions to callback_add.
If the property event is static, it works fine, but if is a non static, inside the proxy function the property not be recognized.
The difficult is because I have to use the callback_add function and can't modify this.
Here is a summary of the class with 2 events (static and non-static)
Header
#ifndef FALAENGINE_H_
#define FALAENGINE_H_
#pragma once
extern "C"{
#include <julius/julius.h>
}
namespace FalaAPI {
public ref class FalaEngine
{
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
static property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
}
#endif /* FALAENGINE_H_*/
Source
#include "stdafx.h"
using System::String;
using System::Console;
#include "FalaEngine.h"
#include <windows.h>
namespace FalaAPI{
void StartOnEngineStart()(Recog *recog, void * dummy){
if(FalaEngine::OnEngineStart->GetInvocationList()->Length > 0)
FalaEngine::OnEngineStart->Invoke();
}
void StartOnRecognized()(Recog *recog, void * dummy){
if(FalaEngine::OnRecognized->GetInvocationList()->Length > 0)
FalaEngine::OnRecognized->Invoke();
}
FalaEngine::FalaEngine(){
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, NULL);
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, NULL);
}
}
The problem occurs inside StartOnEngineStart function:
error C2227: left of '->GetInvocationList' must point to class/struct/union/generic type
A non-static member exists separately in each instance. You haven't specified which instance contains the delegate you want to inspect, you've only specified a class (and there may be many instances).
Try using the dummy parameter to pass your instance. But be careful, because the garbage collector will move objects around unless you have pinned them, so simply passing the address will not work. You need to create and pass a GCHandle instead. (Be careful not to leak the GCHandle, or your object will never be released)
Something like this should be effective:
ref class FalaEngine;
struct EngineHandle
{
gcroot<FalaEngine^> handle;
EngineHandle(FalaEngine^ engine) : handle(engine) {}
};
public ref class FalaEngine
{
clr_scoped_ptr<EngineHandle> callback_ptr;
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
void StartOnEngineStart(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnEngineStart(); // C++/CLI already checks if the invocation list is empty
}
void StartOnRecognized(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnRecognized(recog->get_result());
}
FalaEngine::FalaEngine()
: callback_ptr(new EngineHandle(this))
{
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, callback_ptr.get());
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, callback_ptr.get());
}
The clr_scoped_ptr class is here. There are not many license requirements, make sure you follow them though if you use it.
I am trying to create a utility keystroke app so i can do things like kill a preprogrammed process or launch something. I am thinking I should hold cmd in any app, then type in a 4 digit command key so I can launch or kill anything quickly while programming, debugging watching a video, etc.
I figured out how to get the keyboard callback, but for whatever reason once I click into another app, my keystroke util receives no more keys. Even if I click back to my console window or msvc, I will not receive any input. This is unless its global, so how do I set the hook to be global?
My code is
int main()
{
hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(0), 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hook);
}
In order for your keyboard hook to be accessible from all processes, it has to be placed in a DLL which will then be loaded into each process' address space. One important thing to keep in mind is that since each instance of the DLL is loaded in a separate process, each will have its own copy of any global variables in the DLL. If you need to share data between these instances, the simplest way is to create a shared data segment in the DLL. The following code is from an RSI monitoring program I wrote.
//
// some data will be shared across all
// instances of the DLL
//
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
int iKeyCount = 0;
HHOOK hKeyboardHook = 0;
HHOOK hMouseHook = 0;
#pragma data_seg()
//
// instance specific data
//
HMODULE hInstance = 0;
//
// DLL load/unload entry point
//
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD dwReason,
LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH :
hInstance = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH :
break;
case DLL_THREAD_DETACH :
break;
case DLL_PROCESS_DETACH :
break;
}
return TRUE;
}
//
// keyboard hook
//
LRESULT CALLBACK KeyboardProc(int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam) // keystroke-message information
{
if ((lParam & 0x80000000) != 0)
{
++iKeyCount;
}
return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
}
//
// mouse hook
//
LRESULT CALLBACK MouseProc(int code, // hook code
WPARAM wParam, // message identifier
LPARAM lParam) // mouse coordinates
{
switch (wParam)
{
case WM_LBUTTONDOWN :
case WM_MBUTTONDOWN :
case WM_RBUTTONDOWN :
case WM_LBUTTONDBLCLK :
case WM_MBUTTONDBLCLK :
case WM_RBUTTONDBLCLK :
++iKeyCount;
break;
}
return CallNextHookEx(hMouseHook, code, wParam, lParam);
}
//
// install keyboard/mouse hooks
//
void KBM_API InstallHooks(void)
{
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, 0);
hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, hInstance, 0);
}
//
// remove keyboard/mouse hooks
//
void KBM_API RemoveHooks(void)
{
UnhookWindowsHookEx(hKeyboardHook);
UnhookWindowsHookEx(hMouseHook);
hKeyboardHook = hMouseHook = 0;
}
//
// retrieve number of keystrokes
//
int KBM_API FetchKeyCount(bool bClear)
{
int kc = iKeyCount;
if (bClear)
iKeyCount = 0;
return kc;
}
Avoid codeproject samples. ( plenty of bugs, bad copies of MSDN )
See the tons of complete samples on MSDN on hooks (MSDN, SDK, KB, etc)
And you don't need any DLL, just use LL hooks
Re-read the introduction to hooks in the Win32 guide. (A good place to start is here.)
Specifically, if you want to hook events in other processes, you need to place your callback in a DLL, which is injected into other processes by Win32. From the code you've supplied, I can't tell if KeyboardProc is in a DLL or in the main program. It doesn't look like it, considering the HINSTANCE you're passing.
For Glopbal Keyboard Hook just for Hot Key's, then Register hotkey is the best (its done by Microsoft):
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309(v=vs.85).aspx
Download the sample winform app and see for yourself:
https://code.msdn.microsoft.com/CppRegisterHotkey-7bd897a8 C++
https://code.msdn.microsoft.com/CSRegisterHotkey-e3f5061e C#
https://code.msdn.microsoft.com/VBRegisterHotkey-50af3179 VB.Net
Winform App:
/****************************** Module Header ******************************\
* Module Name: MainForm.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This is the main form of this application. It is used to initialize the UI
* and handle the events.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CSRegisterHotkey
{
public partial class MainForm : Form
{
HotKeyRegister hotKeyToRegister = null;
Keys registerKey = Keys.None;
KeyModifiers registerModifiers = KeyModifiers.None;
public MainForm()
{
InitializeComponent();
}
/// <summary>
/// Handle the KeyDown of tbHotKey. In this event handler, check the pressed keys.
/// The keys that must be pressed in combination with the key Ctrl, Shift or Alt,
/// like Ctrl+Alt+T. The method HotKeyRegister.GetModifiers could check whether
/// "T" is pressed.
/// </summary>
private void tbHotKey_KeyDown(object sender, KeyEventArgs e)
{
// The key event should not be sent to the underlying control.
e.SuppressKeyPress = true;
// Check whether the modifier keys are pressed.
if (e.Modifiers != Keys.None)
{
Keys key = Keys.None;
KeyModifiers modifiers = HotKeyRegister.GetModifiers(e.KeyData, out key);
// If the pressed key is valid...
if (key != Keys.None)
{
this.registerKey = key;
this.registerModifiers = modifiers;
// Display the pressed key in the textbox.
tbHotKey.Text = string.Format("{0}+{1}",
this.registerModifiers, this.registerKey);
// Enable the button.
btnRegister.Enabled = true;
}
}
}
/// <summary>
/// Handle the Click event of btnRegister.
/// </summary>
private void btnRegister_Click(object sender, EventArgs e)
{
try
{
// Register the hotkey.
hotKeyToRegister = new HotKeyRegister(this.Handle, 100,
this.registerModifiers, this.registerKey);
// Register the HotKeyPressed event.
hotKeyToRegister.HotKeyPressed += new EventHandler(HotKeyPressed);
// Update the UI.
btnRegister.Enabled = false;
tbHotKey.Enabled = false;
btnUnregister.Enabled = true;
}
catch (ArgumentException argumentException)
{
MessageBox.Show(argumentException.Message);
}
catch (ApplicationException applicationException)
{
MessageBox.Show(applicationException.Message);
}
}
/// <summary>
/// Show a message box if the HotKeyPressed event is raised.
/// </summary>
void HotKeyPressed(object sender, EventArgs e)
{
//Here is the magic!!!!!!!!'
//DO SOMETHING COOL!!! Or Just activate this winform
if (this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal;
}
this.Activate();
}
/// <summary>
/// Handle the Click event of btnUnregister.
/// </summary>
private void btnUnregister_Click(object sender, EventArgs e)
{
// Dispose the hotKeyToRegister.
if (hotKeyToRegister != null)
{
hotKeyToRegister.Dispose();
hotKeyToRegister = null;
}
// Update the UI.
tbHotKey.Enabled = true;
btnRegister.Enabled = true;
btnUnregister.Enabled = false;
}
/// <summary>
/// Dispose the hotKeyToRegister when the form is closed.
/// </summary>
protected override void OnClosed(EventArgs e)
{
if (hotKeyToRegister != null)
{
hotKeyToRegister.Dispose();
hotKeyToRegister = null;
}
base.OnClosed(e);
}
}
}
HotKeyRegister.cs
/****************************** Module Header ******************************\
* Module Name: HotKeyRegister.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This class imports the method RegisterHotKey and UnregisterHotKey in
* user32.dll to define or free a system-wide hot key.
*
* The method Application.AddMessageFilter is used to add a message filter to
* monitor Windows messages as they are routed to their destinations. Before a
* message is dispatched, the method PreFilterMessage could handle it. If a
* WM_HOTKEY messages was generated by the hot key that was registered by this
* HotKeyRegister object, then raise a HotKeyPressed event.
*
* This class also supplies a static method GetModifiers to get the modifiers
* and key from the KeyData property of KeyEventArgs.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Security.Permissions;
namespace CSRegisterHotkey
{
public class HotKeyRegister : IMessageFilter, IDisposable
{
/// <summary>
/// Define a system-wide hot key.
/// </summary>
/// <param name="hWnd">
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key. If this parameter is NULL, WM_HOTKEY messages are posted to the
/// message queue of the calling thread and must be processed in the message loop.
/// </param>
/// <param name="id">
/// The identifier of the hot key. If the hWnd parameter is NULL, then the hot
/// key is associated with the current thread rather than with a particular
/// window.
/// </param>
/// <param name="fsModifiers">
/// The keys that must be pressed in combination with the key specified by the
/// uVirtKey parameter in order to generate the WM_HOTKEY message. The fsModifiers
/// parameter can be a combination of the following values.
/// MOD_ALT 0x0001
/// MOD_CONTROL 0x0002
/// MOD_SHIFT 0x0004
/// MOD_WIN 0x0008
/// </param>
/// <param name="vk">The virtual-key code of the hot key.</param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id,
KeyModifiers fsModifiers, Keys vk);
/// <summary>
/// Frees a hot key previously registered by the calling thread.
/// </summary>
/// <param name="hWnd">
/// A handle to the window associated with the hot key to be freed. This parameter
/// should be NULL if the hot key is not associated with a window.
/// </param>
/// <param name="id">
/// The identifier of the hot key to be freed.
/// </param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
/// <summary>
/// Get the modifiers and key from the KeyData property of KeyEventArgs.
/// </summary>
/// <param name="keydata">
/// The KeyData property of KeyEventArgs. The KeyData is a key in combination
/// with modifiers.
/// </param>
/// <param name="key">The pressed key.</param>
public static KeyModifiers GetModifiers(Keys keydata, out Keys key)
{
key = keydata;
KeyModifiers modifers = KeyModifiers.None;
// Check whether the keydata contains the CTRL modifier key.
// The value of Keys.Control is 131072.
if ((keydata & Keys.Control) == Keys.Control)
{
modifers |= KeyModifiers.Control;
key = keydata ^ Keys.Control;
}
// Check whether the keydata contains the SHIFT modifier key.
// The value of Keys.Control is 65536.
if ((keydata & Keys.Shift) == Keys.Shift)
{
modifers |= KeyModifiers.Shift;
key = key ^ Keys.Shift;
}
// Check whether the keydata contains the ALT modifier key.
// The value of Keys.Control is 262144.
if ((keydata & Keys.Alt) == Keys.Alt)
{
modifers |= KeyModifiers.Alt;
key = key ^ Keys.Alt;
}
// Check whether a key other than SHIFT, CTRL or ALT (Menu) is pressed.
if (key == Keys.ShiftKey || key == Keys.ControlKey || key == Keys.Menu)
{
key = Keys.None;
}
return modifers;
}
/// <summary>
/// Specify whether this object is disposed.
/// </summary>
bool disposed = false;
/// <summary>
/// This constant could be found in WinUser.h if you installed Windows SDK.
/// Each windows message has an identifier, 0x0312 means that the mesage is
/// a WM_HOTKEY message.
/// </summary>
const int WM_HOTKEY = 0x0312;
/// <summary>
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key.
/// </summary>
public IntPtr Handle { get; private set; }
/// <summary>
/// A normal application can use any value between 0x0000 and 0xBFFF as the ID
/// but if you are writing a DLL, then you must use GlobalAddAtom to get a
/// unique identifier for your hot key.
/// </summary>
public int ID { get; private set; }
public KeyModifiers Modifiers { get; private set; }
public Keys Key { get; private set; }
/// <summary>
/// Raise an event when the hotkey is pressed.
/// </summary>
public event EventHandler HotKeyPressed;
public HotKeyRegister(IntPtr handle, int id, KeyModifiers modifiers, Keys key)
{
if (key == Keys.None || modifiers == KeyModifiers.None)
{
throw new ArgumentException("The key or modifiers could not be None.");
}
this.Handle = handle;
this.ID = id;
this.Modifiers = modifiers;
this.Key = key;
RegisterHotKey();
// Adds a message filter to monitor Windows messages as they are routed to
// their destinations.
Application.AddMessageFilter(this);
}
/// <summary>
/// Register the hotkey.
/// </summary>
private void RegisterHotKey()
{
bool isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation failed, try to unregister the hotkey if the thread
// has registered it before.
if (!isKeyRegisterd)
{
// IntPtr.Zero means the hotkey registered by the thread.
UnregisterHotKey(IntPtr.Zero, ID);
// Try to register the hotkey again.
isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation still failed, it means that the hotkey was already
// used in another thread or process.
if (!isKeyRegisterd)
{
throw new ApplicationException("The hotkey is in use");
}
}
}
/// <summary>
/// Filters out a message before it is dispatched.
/// </summary>
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public bool PreFilterMessage(ref Message m)
{
// The property WParam of Message is typically used to store small pieces
// of information. In this scenario, it stores the ID.
if (m.Msg == WM_HOTKEY
&& m.HWnd == this.Handle
&& m.WParam == (IntPtr)this.ID
&& HotKeyPressed != null)
{
// Raise the HotKeyPressed event if it is an WM_HOTKEY message.
HotKeyPressed(this, EventArgs.Empty);
// True to filter the message and stop it from being dispatched.
return true;
}
// Return false to allow the message to continue to the next filter or
// control.
return false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Unregister the hotkey.
/// </summary>
protected virtual void Dispose(bool disposing)
{
// Protect from being called multiple times.
if (disposed)
{
return;
}
if (disposing)
{
// Removes a message filter from the message pump of the application.
Application.RemoveMessageFilter(this);
UnregisterHotKey(Handle, ID);
}
disposed = true;
}
}
}
KeyModifiers.cs:
/****************************** Module Header ******************************\
* Module Name: KeyModifiers.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This enum defines the modifiers to generate the WM_HOTKEY message.
* See http://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
namespace CSRegisterHotkey
{
[Flags]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
// Either WINDOWS key was held down. These keys are labeled with the Windows logo.
// Keyboard shortcuts that involve the WINDOWS key are reserved for use by the
// operating system.
Windows = 8
}
}
I had to mess with global hooks when I was writing a "Picture in Picture" sort of app. This article and sample code helped me out tremendously:
http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx