闲来无聊,给自己做了个锁屏小工具,在这里写写步骤,感兴趣的看看。
新建Windws工程后将Form1的FormBorderStyle设置为None,使这个窗口没有边框;TopMost为True,使这个窗口始终在所有窗口的最上方;设置WindowState为Maximized,使窗口一开始就是最大化状态;设置ShowInTaskbar为False,使窗口不在任务栏出现。
然后给窗体设置一张自己喜欢的背景图片,再把BackGroundImageLayout设置为None,使背景图片不重复。
然后在窗口左下角放置一个密码框和一个按钮,按钮单击事件里面暂定密码为“123”就可以退出锁屏。
现在按下Ctrl + F5启动程序,初步效果就已经出来了。
但是这个时候按下Alt + F4就可以关闭锁屏工具,没有安全性可言,所以我们要使用键盘钩子来阻止这一行为。键盘钩子的原理就是通过Windows API实现我们的程序可以获取到所有的键盘事件,只要键盘稍有“响动”,锁屏工具就会第一时间获取到这一信息,然后我们就可以把这一信息拦截,拦截之后不告诉操作系统有按键事件发生,操作系统就不会做出反应。
但是由于我们需要用键盘输入密码来解锁,所以我们不能屏蔽所有的按键,我们也不需要屏蔽所有的按键,只需要把Ctrl、Alt、Win、Tab等功能键屏蔽掉就够了,没了这些功能键,就不能用键盘关闭锁屏工具了。
到底屏蔽哪些键,在下面这个类中的方法KeyboardHookProc中可以自行添加修改。
安装钩子:InstallHook()、卸载钩子:UnInstallHook()。
001 |
using System; |
002 |
using System.Collections.Generic; |
003 |
using System.Text; |
004 |
using System.Runtime.InteropServices; |
005 |
using System.IO; |
006 |
using System.Reflection; |
007 |
using System.Windows.Forms; |
008 |
|
009 |
namespace LockScreen |
010 |
{ |
011 |
public class Hook_Keyboard |
012 |
{ |
013 |
#region 私有变量 |
014 |
|
015 |
/// <summary> |
016 |
/// 键盘钩子句柄 |
017 |
/// </summary> |
018 |
private IntPtr m_pKeyboardHook = IntPtr.Zero; |
019 |
|
020 |
/// <summary> |
021 |
/// 钩子委托声明 |
022 |
/// </summary> |
023 |
/// <param name="nCode"></param> |
024 |
/// <param name="wParam"></param> |
025 |
/// <param name="lParam"></param> |
026 |
/// <returns></returns> |
027 |
public delegate int HookProc( int nCode, Int32 wParam, IntPtr lParam); |
028 |
|
029 |
/// <summary> |
030 |
/// 键盘钩子委托实例 |
031 |
/// </summary> |
032 |
/// <remarks> |
033 |
/// 不要试图省略此变量,否则将会导致 |
034 |
/// 激活 CallbackOnCollectedDelegate 托管调试助手 (MDA)。 |
035 |
/// 详细请参见MSDN中关于 CallbackOnCollectedDelegate 的描述 |
036 |
/// </remarks> |
037 |
private HookProc m_KeyboardHookProcedure; |
038 |
|
039 |
// 底层键盘钩子 |
040 |
public const int idHook = 13; |
041 |
|
042 |
/// <summary> |
043 |
/// 安装钩子 |
044 |
/// </summary> |
045 |
/// <param name="idHook"></param> |
046 |
/// <param name="lpfn"></param> |
047 |
/// <param name="hInstance"></param> |
048 |
/// <param name="threadId"></param> |
049 |
/// <returns></returns> |
050 |
[DllImport( "user32.dll" , CallingConvention = CallingConvention.StdCall)] |
051 |
public static extern IntPtr SetWindowsHookEx( int idHook, HookProc lpfn, |
052 |
IntPtr pInstance, int threadId); |
053 |
|
054 |
/// <summary> |
055 |
/// 卸载钩子 |
056 |
/// </summary> |
057 |
/// <param name="idHook"></param> |
058 |
/// <returns></returns> |
059 |
[DllImport( "user32.dll" , CallingConvention = CallingConvention.StdCall)] |
060 |
public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle); |
061 |
|
062 |
/// <summary> |
063 |
/// 传递钩子 |
064 |
/// </summary> |
065 |
/// <param name="pHookHandle">是您自己的钩子函数的句柄。用该句柄可以遍历钩子链</param> |
066 |
/// <param name="nCode">把传入的参数简单传给CallNextHookEx即可</param> |
067 |
/// <param name="wParam">把传入的参数简单传给CallNextHookEx即可</param> |
068 |
/// <param name="lParam"></param> |
069 |
/// <returns></returns> |
070 |
[DllImport( "user32.dll" , CallingConvention = CallingConvention.StdCall)] |
071 |
public static extern int CallNextHookEx(IntPtr pHookHandle, int nCode, |
072 |
Int32 wParam, IntPtr lParam); |
073 |
|
074 |
#endregion 私有变量 |
075 |
|
076 |
#region 私有方法 |
077 |
|
078 |
|
079 |
/// <summary> |
080 |
/// 键盘钩子处理函数 |
081 |
/// </summary> |
082 |
/// <param name="nCode"></param> |
083 |
/// <param name="wParam"></param> |
084 |
/// <param name="lParam"></param> |
085 |
/// <returns></returns> |
086 |
/// <remarks>此版本的键盘事件处理不是很好,还有待修正.</remarks> |
087 |
private int KeyboardHookProc( int nCode, Int32 wParam, IntPtr lParam) |
088 |
{ |
089 |
//return 1; |
090 |
KeyMSG m = (KeyMSG)Marshal.PtrToStructure(lParam, typeof (KeyMSG)); |
091 |
if (m_pKeyboardHook != IntPtr.Zero) |
092 |
{ |
093 |
switch (((Keys)m.vkCode)) |
094 |
{ |
095 |
case Keys.LWin: |
096 |
case Keys.RWin: |
097 |
case Keys.Delete: |
098 |
case Keys.Alt: |
099 |
case Keys.Escape: |
100 |
case Keys.F4: |
101 |
case Keys.Control: |
102 |
case Keys.Tab: |
103 |
return 1; |
104 |
} |
105 |
} |
106 |
return 0; |
107 |
} |
108 |
|
109 |
#endregion 私有方法 |
110 |
|
111 |
#region 公共方法 |
112 |
|
113 |
/// <summary> |
114 |
/// 安装钩子 |
115 |
/// </summary> |
116 |
/// <returns></returns> |
117 |
public bool InstallHook() |
118 |
{ |
119 |
//IntPtr pInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule); |
120 |
IntPtr pInstance = (IntPtr)4194304; |
121 |
if ( this .m_pKeyboardHook == IntPtr.Zero) |
122 |
{ |
123 |
this .m_KeyboardHookProcedure = new HookProc(KeyboardHookProc); |
124 |
this .m_pKeyboardHook = SetWindowsHookEx(idHook, m_KeyboardHookProcedure, pInstance, 0); |
125 |
if ( this .m_pKeyboardHook == IntPtr.Zero) |
126 |
{ |
127 |
this .UnInstallHook(); |
128 |
return false ; |
129 |
} |
130 |
} |
131 |
|
132 |
return true ; |
133 |
} |
134 |
|
135 |
/// <summary> |
136 |
/// 卸载钩子 |
137 |
/// </summary> |
138 |
/// <returns></returns> |
139 |
public bool UnInstallHook() |
140 |
{ |
141 |
bool result = true ; |
142 |
if ( this .m_pKeyboardHook != IntPtr.Zero) |
143 |
{ |
144 |
result = (UnhookWindowsHookEx( this .m_pKeyboardHook) && result); |
145 |
this .m_pKeyboardHook = IntPtr.Zero; |
146 |
} |
147 |
return result; |
148 |
} |
149 |
|
150 |
[StructLayout(LayoutKind.Sequential)] |
151 |
public struct KeyMSG |
152 |
{ |
153 |
public int vkCode; |
154 |
public int scanCode; |
155 |
public int flags; |
156 |
public int time; |
157 |
public int dwExtraInfo; |
158 |
} |
159 |
#endregion 公共方法 |
160 |
} |
161 |
} |
1 |
修改我们刚刚的锁屏工具,在窗体加载的时候安装钩子,在窗体关闭ing的时候卸载钩子即可。 |
1 |
启动程序观察效果,果然强大了。 |
1 |
但是还是有问题,理论上是屏蔽了这些功能键了,但是按下Ctrl + Alt + Del的时候居然还 |
1 |
是可以调出系统的任务管理器,并且任务管理器的窗口Top级别是最高的,所以可以通过任务管理器 |
1 |
关闭锁屏工具,这个问题可以这样解决: |
01 |
/// <summary> |
02 |
/// 用Timer杀死任务管理器 |
03 |
/// </summary> |
04 |
/// <param name="sender"></param> |
05 |
/// <param name="e"></param> |
06 |
private void timer1_Tick( object sender, EventArgs e) |
07 |
{ |
08 |
try |
09 |
{ |
10 |
this .Activate(); |
11 |
12 |
Process[] myProcess = Process.GetProcesses(); |
13 |
foreach (Process p in myProcess) |
14 |
{ |
15 |
if (p.ProcessName == "taskmgr" ) |
16 |
{ |
17 |
p.Kill(); |
18 |
return ; |
19 |
} |
20 |
} |
21 |
} |
22 |
catch (Exception) |
23 |
{ |
24 |
25 |
} |
26 |
} |
这是我在Form中添加一个Timer的Tick事件,用这个事件来“杀死”任务管理器进程就OK了。
到这里,我们的锁屏工具基本上已经刀枪不入了。锁屏之后除非有密码,别人是动不了被锁的电脑的。
警告:卸载钩子这一步骤在程序被关闭时一定要执行,不然很麻烦,很可能出现锁屏工具已经关闭了,但是键盘还是被锁着在。然后建议在制作调试的过程中不启用Timer,以免一不小心出点小毛病输不了密码,键盘也被锁,任务管理器也打不开,那就只好重启电脑了。如果后期加了开机自启动功能,出现这毛病,重启都不顶事,哭去吧。
虽然这个锁屏工具实现是锁屏的功能,安全性也还不错,但是这个简易的锁屏工具离真正的“产品”的要求还很远,比如密码不能修改、锁屏功能不能被快捷键呼出、锁屏工具不能开机自启动等细节问题。我将在接下来的几章里继续完善。