昨天写了《C# 实现假关机》,发现其中的办法对Win7不适应,还是会被强制关掉。今天想到另一种方法,在鼠标即将点击开始菜单关机按钮的时候,把鼠标的消息拦截下来,然后我们的程序收到这个消息,处理一下,比如关闭显示器,然后把消息丢弃掉,这样关机按钮的消息就收不到,自然就不会关机了。这样的好处是,系统收不到关机消息,自然没有强制关闭那个窗口了。
这里要用到钩子HOOK,需要用Form,完整代码如下:
using System; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Diagnostics; namespace HookShutdown { public partial class FormMain : Form { internal enum HookType { MsgFilter = -1, JournalRecord = 0, JournalPlayback = 1, Keyboard = 2, GetMessage = 3, CallWndProc = 4, CBT = 5, SysMsgFilter = 6, Mouse = 7, Hardware = 8, Debug = 9, Shell = 10, ForegroundIdle = 11, CallWndProcRet = 12, KeyboardLL = 13, MouseLL = 14 } internal delegate IntPtr HookProc(int code, int wparam, IntPtr lparam); public struct POINT { public int x; public int y; } public struct MSLLHOOKSTRUCT { public POINT pt; public uint mouseData; public uint flags; public uint time; public int dwExtraInfo; } private IntPtr m_NextHookPtr = IntPtr.Zero; private IntPtr m_Handle = IntPtr.Zero; private IntPtr _handle = IntPtr.Zero; private uint trayWndThreadId = 0; [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("User32.dll")] static extern void UnhookWindowsHookEx(IntPtr handle); [DllImport("User32.dll", SetLastError = true)] static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("User32.dll")] static extern IntPtr CallNextHookEx(IntPtr handle, int code, int wparam, IntPtr lparam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(POINT Point); [DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); [DllImport("kernel32.dll")] static extern uint GetLastError(); [DllImport("user32.dll")] static extern bool BlockInput(bool fBlockIt); [DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); const UInt32 WM_SYSCOMMAND = 0x0112; const UInt32 SC_MONITORPOWER = 0xF170; public FormMain() { InitializeComponent(); IntPtr hWnd = FindWindow("Shell_TrayWnd", (string)null); if (hWnd != IntPtr.Zero) { uint procID = 0; trayWndThreadId = GetWindowThreadProcessId(hWnd, out procID); InstallHook();//安装钩子 } } ~FormMain() { UnInstallHook();//卸载钩子 } private void InstallHook() { HookProc hookProc = new HookProc(IdentyFormHookProc); this.m_NextHookPtr = SetWindowsHookEx(HookType.MouseLL, hookProc, GetModuleHandle( Process.GetCurrentProcess().MainModule.ModuleName ), 0); //if(m_NextHookPtr == IntPtr.Zero) // MessageBox.Show(GetLastError().ToString()); } private void UnInstallHook() { if (this.m_NextHookPtr != IntPtr.Zero) { UnhookWindowsHookEx(this.m_NextHookPtr); } } private IntPtr IdentyFormHookProc(int code, int wparam, IntPtr lparam) { switch (wparam) { case (int)MsgType.WM_LBUTTONDOWN: case (int)MsgType.WM_LBUTTONUP: MSLLHOOKSTRUCT msll = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lparam, typeof(MSLLHOOKSTRUCT)); IntPtr hWnd = WindowFromPoint(msll.pt); if (hWnd != IntPtr.Zero) { uint procID = 0; if (trayWndThreadId == GetWindowThreadProcessId(hWnd, out procID)) // 如果窗口线程是开始菜单所在的窗口线程 { StringBuilder wndText = new StringBuilder(256); GetWindowText(hWnd, wndText, 256); if (wndText.ToString() == "关机") // 如果鼠标点击的是关机按钮 { BlockInput(true); SendMessage(this.Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2); // 关闭显示器 return (IntPtr)1; } } } break; } return CallNextHookEx(this.m_NextHookPtr, code, wparam, lparam); } } } |
注意:1.把开始菜单的电源按钮默认为 关机
2.要使得BlockInput这个函数有效,也就是在关闭显示器后中断鼠标键盘输入,程序要运行在管理员权限下。
3.要恢复原有的关机,退出程序即可。