The last day i've tried to make this work: Wake up my computer from sleep or hibernation, using a WPF application. Nothing i've tried worked.
So far I've tried the most popular examples on the net.. For example:
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
bool bManualReset,
string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
[In] ref long pDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
bool fResume);
public static void SetWaitForWakeUpTime()
{
DateTime utc = DateTime.Now.AddMinutes(2);
long duetime = utc.ToFileTime();
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, "MyWaitabletimer"))
{
if (SetWaitableTimer(handle, ref duetime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
// You could make it a recursive call here, setting it to 1 hours time or similar
Console.WriteLine("Wake up call");
Console.ReadLine();
}
And the usage:
WakeUp.SetWaitForWakeUpTime();
I've also tried this example (see how I use the method after the code):
public event EventHandler Woken;
private BackgroundWorker bgWorker = new BackgroundWorker();
public WakeUp()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time)
{
bgWorker.RunWorkerAsync(time.ToFileTime());
}
void bgWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long waketime = (long)e.Argument;
using (SafeWaitHandle handle =
CreateWaitableTimer(IntPtr.Zero, true,
this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
if (SetWaitableTimer(handle, ref waketime, 0,
IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false,
EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
And the usage:
WakeUp w = new WakeUp();
w.Woken += new EventHandler(w_Woken);
w.SetWakeUpTime(alarmDate.Subtract(new TimeSpan(0,0,0,20)));
Nothing seems to wake up my PC.
I know it is possible, as several other alarm clocks can do it. They manage to wake up my computer with no problems, so there must be some mistake.
The problem was not all computers support this issue. There is apparently not much to do if your computer doesn't support it.
Related
I'm trying to make a little application to enumerate in console every window name from each process thread.
I'm currently using this code that works fine on a windows 7 machine but for some reason it stucks at windows 10 without any stack trace or error message, I tried to add almost everything inside a try-catch but I didn't get any message as well so I'm lost.
Here is the code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
static void Main(string[] args)
{
Process[] processes = Process.GetProcesses();
Console.WriteLine("Detected: " + processes.Length + " processes.");
foreach (Process process in processes)
{
IEnumerable<IntPtr> windowHandles = null;
try
{
windowHandles = EnumerateProcessWindowHandles(process);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
Console.WriteLine("Checking process" + process.ProcessName);
foreach (var handle in windowHandles)
{
StringBuilder message = new StringBuilder();
try
{
SendMessage(handle, 0x000D, message.Capacity, message);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
if (message.Length == 0)
{
continue;
}
Console.WriteLine("Window name: " + message.ToString());
}
}
Console.WriteLine("Finished!");
Console.ReadLine();
}
private static IEnumerable<IntPtr> EnumerateProcessWindowHandles(Process process)
{
List<IntPtr> handles = new List<IntPtr>();
ProcessThreadCollection threads = null;
try
{
threads = process.Threads;
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
return handles;
}
foreach (ProcessThread thread in threads)
{
try
{
EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
}
return handles;
}
}
}
Any idea about why it could stuck?
UPDATE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr handle);
static void Main(string[] args)
{
Process[] processes = Process.GetProcesses();
Console.WriteLine("Detected: " + processes.Length + " processes.");
foreach (Process process in processes)
{
IEnumerable<IntPtr> windowHandles = null;
try
{
windowHandles = EnumerateProcessWindowHandles(process);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
Console.WriteLine("Checking process: " + process.ProcessName);
foreach (IntPtr handle in windowHandles)
{
if (handle == null)
{
continue;
}
if (!IsWindowVisible(handle))
{
continue;
}
StringBuilder message = new StringBuilder();
int capacity = 0;
try
{
capacity = message.Capacity;
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
try
{
SendMessage(handle, 0x000D, capacity, message);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
if (message.Length == 0)
{
continue;
}
Console.WriteLine("Window name: " + message.ToString());
}
}
Console.WriteLine("Finished!");
Console.ReadLine();
}
private static IEnumerable<IntPtr> EnumerateProcessWindowHandles(Process process)
{
List<IntPtr> handles = new List<IntPtr>();
ProcessThreadCollection threads = null;
try
{
threads = process.Threads;
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
return handles;
}
foreach (ProcessThread thread in threads)
{
if (thread == null)
{
continue;
}
int threadId = 0;
try
{
threadId = thread.Id;
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
try
{
EnumThreadWindows(threadId, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
continue;
}
}
return handles;
}
}
}
Thank you all.
Try running your console application as administrator if the problem still arises I suggest using the API GetLastError that's what it does:
Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. Multiple threads do not overwrite each other's last-error code.
Try reading the value to know the problem , try this link for guidence on how to use it:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679360(v=vs.85).aspx
To clear things up before you mention the most obvious choice, I am already calling ShowDialog and not the Show method!!!
I would like to block the close (invoked from a different thread) off an WPF window if a OpenFileDialog is open.
Here is the code I have (reduced to show my problem):
public class FooWindow : Window
{
public FooWindow()
{
InitializeComponent();
this.Closing += OnClosing;
}
public void OpenDialogAndCloseMe()
{
var ofd = new OpenFileDialog();
Thread th = new Thread(() => CloseMe());
th.Start();
ofd.ShowDialog(this);
}
public void CloseMe()
{
System.Threading.Thread.Sleep(2000); //give the OpenFileDialog time to pop up...
//since this method gets called from a different thread invoke it...
this.Dispatcher.Invoke(() => this.Close());
}
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
e.Cancel = true;
}
}
The problem I am facing is the OnClosing part, how would I get the OpenFileDialog (or any other Dialog in that case).
I have searched the web and found Win32 methods like:
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
I have tried them both but they return 0 children, any ideas what's going wrong?
Here is the full code I tried so far:
//replace the above OnClosing with this implementation... all 3 return false
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
var hWnd = new WindowInteropHelper(this).Handle;
if (WindowHandling.GetChildren(hWnd).Any())
e.Cancel = true;
if (WindowHandling.GetChildrenV2(hWnd).Any())
e.Cancel = true;
if (WindowHandling.GetChildrenV3(hWnd).Any())
e.Cancel = true;
}
public static class WindowHandling
{
private delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
{
windowHandles.Add(windowHandle);
return true;
}
public static IEnumerable<IntPtr> GetChildren(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
return Enumerable.Empty<IntPtr>();
var x = new WindowHandleInfo(hWnd);
return x.GetAllChildHandles();
}
public static IEnumerable<IntPtr> GetChildrenV2(IntPtr hWnd)
{
var windowHandles = new ArrayList();
EnumedWindow callBackPtr = GetWindowHandle;
EnumChildWindows(hWnd, callBackPtr, windowHandles);
return windowHandles.OfType<IntPtr>();
}
public static IEnumerable<IntPtr> GetChildrenV3(IntPtr hParent)
{
var result = new List<IntPtr>();
var ct = 0;
var maxCount = 100;
var prevChild = IntPtr.Zero;
while (true && ct < maxCount)
{
var currChild = FindWindowEx(hParent, prevChild, null, null);
if (currChild == IntPtr.Zero)
break;
result.Add(currChild);
prevChild = currChild;
++ct;
}
return result;
}
//http://stackoverflow.com/questions/1363167/how-can-i-get-the-child-windows-of-a-window-given-its-hwnd
private class WindowHandleInfo
{
private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
private readonly IntPtr _mainHandle;
public WindowHandleInfo(IntPtr handle)
{
_mainHandle = handle;
}
public IEnumerable<IntPtr> GetAllChildHandles()
{
var childHandles = new List<IntPtr>();
var gcChildhandlesList = GCHandle.Alloc(childHandles);
var pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
try
{
var childProc = new EnumWindowProc(EnumWindow);
var x = EnumChildWindows(this._mainHandle, childProc, pointerChildHandlesList);
if (x == false)
{
var error = Marshal.GetLastWin32Error();
}
}
finally
{
gcChildhandlesList.Free();
}
return childHandles;
}
private static bool EnumWindow(IntPtr hWnd, IntPtr lParam)
{
var gcChildhandlesList = GCHandle.FromIntPtr(lParam);
if (gcChildhandlesList.Target == null)
return false;
var childHandles = gcChildhandlesList.Target as List<IntPtr>;
if (childHandles != null)
childHandles.Add(hWnd);
return true;
}
}
}
You can solve this with a boolean value tracking if it's open or not:
bool dialogOpen = false;
public void OpenDialogAndCloseMe()
{
var ofd = new OpenFileDialog();
Thread th = new Thread(() => CloseMe());
th.Start();
dialogOpen = true;
ofd.ShowDialog(this);
dialogOpen = false;
}
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
if(dialogOpen)
{
e.Cancel = true;
}
}
In Windows Server 2003 and Windows XP the function to start a Windows Service in Automatic(Delayed) mode isnĀ“t available, so I need make that simulation with code, but when I start the service this will show a message that I need wait cuz are a Timer in the code and this need finish to start. But now I get this error "Tghe PcRegister Service service on local Computer started and then stopped. Some Services stop automatically if they have no work to do, for example, the Performance Logs and Alerts service"... Please I need help to add the timer.
class WinService : System.ServiceProcess.ServiceBase`
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new WinService() };
ServiceBase.Run(ServicesToRun);
}
protected override void OnStart(string[] args)
{
InitializeComponent();
PcRegisterService();
}
protected override void OnStop()
{
}
public void InitializeComponent()
{
this.ServiceName = "WinService";
RestartService("WinService", 600000);
}
public static void RestartService(string serviceName, int timeoutMilliseconds)
{
ServiceController service = new ServiceController(serviceName);
try
{
int millisec1 = Environment.TickCount;
TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
// count the rest of the timeout
int millisec2 = Environment.TickCount;
timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds - (millisec2 - millisec1));
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
catch
{
// ...
}
}
public static void PcRegisterService()
{
}
public static class PerformanceInfo
{
[DllImport("psapi.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size);
[StructLayout(LayoutKind.Sequential)]
public struct PerformanceInformation
{
public int Size;
public IntPtr CommitTotal;
public IntPtr CommitLimit;
public IntPtr CommitPeak;
public IntPtr PhysicalTotal;
public IntPtr PhysicalAvailable;
public IntPtr SystemCache;
public IntPtr KernelTotal;
public IntPtr KernelPaged;
public IntPtr KernelNonPaged;
public IntPtr PageSize;
public int HandlesCount;
public int ProcessCount;
public int ThreadCount;
}
public static Int64 GetTotalMemoryInMiB()
{
PerformanceInformation pi = new PerformanceInformation();
if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi)))
{
return Convert.ToInt64((pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64() / 1048576));
}
else
{
return -1;
}
}
}
}
Because - I would say - something in your RestartServicve method you blow (with an exception). THe code makes no sense. As in none at all. THere is no need to restart a service at all. THis is now how services work. Back to reading the documentation.
I was looking for a possibility to be notified in a .NET windows application when any window is activated in the OS (Windows XP 32-bit). On CodeProject I have found a solution by using global system hooks.
http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H .
Here is a short summary of this procedure:
In an unmanaged assembly (written in C++) a method is implemented which installs the WH_CBT hook.
bool InitializeCbtHook(int threadID, HWND destination)
{
if (g_appInstance == NULL)
{
return false;
}
if (GetProp(GetDesktopWindow(), " HOOK_HWND_CBT") != NULL)
{
SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "HOOK_HWND_CBT"),
RegisterWindowMessage("HOOK_CBT_REPLACED"), 0, 0);
}
SetProp(GetDesktopWindow(), " HOOK_HWND_CBT", destination);
hookCbt = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtHookCallback, g_appInstance, threadID);
return hookCbt != NULL;
}
In the callback method (filter function) depending on the hook type windows messages are sent to a destination window.
static LRESULT CALLBACK CbtHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code >= 0)
{
UINT msg = 0;
if (code == HCBT_ACTIVATE)
msg = RegisterWindowMessage("HOOK_HCBT_ACTIVATE");
else if (code == HCBT_CREATEWND)
msg = RegisterWindowMessage("HOOK_HCBT_CREATEWND");
else if (code == HCBT_DESTROYWND)
msg = RegisterWindowMessage("HOOK_HCBT_DESTROYWND");
else if (code == HCBT_MINMAX)
msg = RegisterWindowMessage("HOOK_HCBT_MINMAX");
else if (code == HCBT_MOVESIZE)
msg = RegisterWindowMessage("HOOK_HCBT_MOVESIZE");
else if (code == HCBT_SETFOCUS)
msg = RegisterWindowMessage("HOOK_HCBT_SETFOCUS");
else if (code == HCBT_SYSCOMMAND)
msg = RegisterWindowMessage("HOOK_HCBT_SYSCOMMAND");
HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), HOOK_HWND_CBT");
if (msg != 0)
SendNotifyMessage(dstWnd, msg, wparam, lparam);
}
return CallNextHookEx(hookCbt, code, wparam, lparam);
}
To use this assembly in a .NET Windows Application the following method has to be imported:
[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializeCbtHook (int threadID, IntPtr DestWindow);
[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UninitializeCbtHook(int hookType);
After calling InitializeCbtHook the messages received from GlobalCbtHook.dll can be processed in:
protected override void WndProc(ref Message msg)
The messages have to be registered in both the assembly and the application by calling
RegisterWindowMessage.
[DllImport("user32.dll")]
private static extern int RegisterWindowMessage(string lpString);
This implementation works fine. But in most cases when I activate Microsoft Office Outlook
my .NET Application receives the activate-event after I minimize Outlook or activate an other window. At first I thought that my .NET wrapper is the cause of the problem. But after I used the sources from the above link I could recognized the same behaviour.
My actually workaround is to use WH_SHELL hook. I know that one difference between WH_CBT and WH_SHELL hook is when using WH_CBT hook it is possible to interrupt the filter function chain by not calling the CallNextHookEx method. Could this play a role in my problem?
Please provide help.
obviously the hooking does not work in cases of outlook - what about other microsoft products (word, power point ...)??
but, why hooking? this little class will work even if outlook is activated
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsMonitor
{
public class ActiveWindowChangedEventArgs : EventArgs
{
public IntPtr CurrentActiveWindow { get; private set; }
public IntPtr LastActiveWindow { get; private set; }
public ActiveWindowChangedEventArgs(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
{
this.LastActiveWindow = lastActiveWindow;
this.CurrentActiveWindow = currentActiveWindow;
}
}
public delegate void ActiveWindowChangedEventHandler(object sender, ActiveWindowChangedEventArgs e);
public class ActiveWindowMonitor
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private Timer monitorTimer;
public IntPtr ActiveWindow { get; private set; }
public event ActiveWindowChangedEventHandler ActiveWindowChanged;
public ActiveWindowMonitor()
{
this.monitorTimer = new Timer();
this.monitorTimer.Tick += new EventHandler(monitorTimer_Tick);
this.monitorTimer.Interval = 10;
this.monitorTimer.Start();
}
private void monitorTimer_Tick(object sender, EventArgs e)
{
CheckActiveWindow();
}
private void CheckActiveWindow()
{
IntPtr currentActiveWindow = GetForegroundWindow();
if (this.ActiveWindow != currentActiveWindow)
{
IntPtr lastActiveWindow = this.ActiveWindow;
this.ActiveWindow = currentActiveWindow;
OnActiveWindowChanged(lastActiveWindow, this.ActiveWindow);
}
}
protected virtual void OnActiveWindowChanged(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
{
ActiveWindowChangedEventHandler temp = ActiveWindowChanged;
if (temp != null)
{
temp.Invoke(this, new ActiveWindowChangedEventArgs(lastActiveWindow, currentActiveWindow));
}
}
}
}
usage
public void InitActiveWindowMonitor()
{
WindowsMonitor.ActiveWindowMonitor monitor = new WindowsMonitor.ActiveWindowMonitor();
monitor.ActiveWindowChanged += new WindowsMonitor.ActiveWindowChangedEventHandler(monitor_ActiveWindowChanged);
}
private void monitor_ActiveWindowChanged(object sender, WindowsMonitor.ActiveWindowChangedEventArgs e)
{
//ouh a window got activated
}
I want to leverage machine learning to model a user's intent and potentially automate commonly performed tasks. To do this I would like to have access to a fire-hose of information about user actions and the machine state. To this end, it is my current thinking that getting access to the stream of windows messages is probably the way forward.
I would like to have as much information as is possible, filtering the information to that which is pertinent I would like to leave to the machine learning tool.
How would this be accomplished? (Preferably in C#).
Please assume that I know how to manage and use this large influx of data.
Any help would be gratefully appreciated.
You can use SetWindowsHookEx to set low level hooks to catch (specific) windows messages.
Specifically these hook-ids might be interesting for monitoring:
WH_CALLWNDPROC (4) Installs a hook procedure that monitors messages
before the system sends them to the destination window procedure. For
more information, see the CallWndProc hook procedure.
WH_CALLWNDPROCRET(12) Installs a hook procedure that monitors
messages after they have been processed by the destination window
procedure. For more information, see the CallWndRetProc hook
procedure.
It's been a while since I've implemented it, but as an example I've posted the base class I use to hook specific messages. (For example, I've used it in a global mousewheel trapper, that makes sure my winforms apps behave the same as internet explorer: scroll the control underneath the cursor, instead of the active control).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Subro.Win32;
namespace Subro
{
/// <summary>
/// Base class to relatively safely register global windows hooks
/// </summary>
public abstract class GlobalHookTrapper : FinalizerBase
{
[DllImport("user32", EntryPoint = "SetWindowsHookExA")]
static extern IntPtr SetWindowsHookEx(int idHook, Delegate lpfn, IntPtr hmod, IntPtr dwThreadId);
[DllImport("user32", EntryPoint = "UnhookWindowsHookEx")]
private static extern int UnhookWindowsHookEx(IntPtr hHook);
[DllImport("user32", EntryPoint = "CallNextHookEx")]
static extern int CallNextHook(IntPtr hHook, int ncode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThreadId();
IntPtr hook;
public readonly int HookId;
public readonly GlobalHookTypes HookType;
public GlobalHookTrapper(GlobalHookTypes Type):this(Type,false)
{
}
public GlobalHookTrapper(GlobalHookTypes Type, bool OnThread)
{
this.HookType = Type;
this.HookId = (int)Type;
del = ProcessMessage;
if (OnThread)
hook = SetWindowsHookEx(HookId, del, IntPtr.Zero, GetCurrentThreadId());
else
{
var hmod = IntPtr.Zero; // Marshal.GetHINSTANCE(GetType().Module);
hook = SetWindowsHookEx(HookId, del, hmod, IntPtr.Zero);
}
if (hook == IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
if (err != 0)
OnHookFailed(err);
}
}
protected virtual void OnHookFailed(int Error)
{
throw Win32Functions.TranslateError(Error);
}
private const int HC_ACTION = 0;
[MarshalAs(UnmanagedType.FunctionPtr)]
private MessageDelegate del;
private delegate int MessageDelegate(int code, IntPtr wparam, IntPtr lparam);
private int ProcessMessage(int hookcode, IntPtr wparam, IntPtr lparam)
{
if (HC_ACTION == hookcode)
{
try
{
if (Handle(wparam, lparam)) return 1;
}
catch { }
}
return CallNextHook(hook, hookcode, wparam, lparam);
}
protected abstract bool Handle(IntPtr wparam, IntPtr lparam);
protected override sealed void OnDispose()
{
UnhookWindowsHookEx(hook);
AfterDispose();
}
protected virtual void AfterDispose()
{
}
}
public enum GlobalHookTypes
{
BeforeWindow = 4, //WH_CALLWNDPROC
AfterWindow = 12, //WH_CALLWNDPROCRET
KeyBoard = 2, //WH_KEYBOARD
KeyBoard_Global = 13, //WH_KEYBOARD_LL
Mouse = 7, //WH_MOUSE
Mouse_Global = 14, //WH_MOUSE_LL
JournalRecord = 0, //WH_JOURNALRECORD
JournalPlayback = 1, //WH_JOURNALPLAYBACK
ForeGroundIdle = 11, //WH_FOREGROUNDIDLE
SystemMessages = 6, //WH_SYSMSGFILTER
MessageQueue = 3, //WH_GETMESSAGE
ComputerBasedTraining = 5, //WH_CBT
Hardware = 8, //WH_HARDWARE
Debug = 9, //WH_DEBUG
Shell = 10, //WH_SHELL
}
public abstract class FinalizerBase : IDisposable
{
protected readonly AppDomain domain;
public FinalizerBase()
{
System.Windows.Forms.Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
domain = AppDomain.CurrentDomain;
domain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
domain.DomainUnload += new EventHandler(domain_DomainUnload);
}
private bool disposed;
public bool IsDisposed{get{return disposed;}}
public void Dispose()
{
if (!disposed)
{
GC.SuppressFinalize(this);
if (domain != null)
{
domain.ProcessExit -= new EventHandler(CurrentDomain_ProcessExit);
domain.DomainUnload -= new EventHandler(domain_DomainUnload);
System.Windows.Forms.Application.ApplicationExit -= new EventHandler(Application_ApplicationExit);
}
disposed = true;
OnDispose();
}
}
void Application_ApplicationExit(object sender, EventArgs e)
{
Dispose();
}
void domain_DomainUnload(object sender, EventArgs e)
{
Dispose();
}
void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Dispose();
}
protected abstract void OnDispose();
/// Destructor
~FinalizerBase()
{
Dispose();
}
}
}