前言
工业自动化和生产环境中,上位机程序的稳定运行至关重要。
操作人员的误操作,如意外按下Win键,可能会导致程序退出或界面切换,从而影响生产效率甚至引发安全问题。为了避免这种情况,许多企业需要在程序启动后禁用Win键,确保操作人员专注于当前任务。
本文将介绍如何在C#上位机程序中实现启动后锁定Win键,防止意外退出,确保程序的稳定运行。
键盘消息拦截与钩子 (Hook) 实现思路
在Windows系统中,所有键盘操作都是基于消息机制的。每当按下或释放一个键时,系统会生成相应的消息并发送给当前活动的应用程序。
因此,要实现特定按键或功能的屏蔽,最直接的方法是通过拦截这些消息来阻止它们到达目标应用程序。
实现思路:使用钩子 (Hook)
为了实现上位机程序启动后禁用Win组合键的功能,我们可以采用钩子 (Hook) 技术。
钩子是一种拦截并处理系统消息的机制。通过设置全局或线程级别的键盘钩子,可以在消息传递到目标应用程序之前对其进行检查和过滤。
具体来说,在程序启动时,我们可以安装一个低级键盘钩子(Low-Level Keyboard Hook),它能够捕获所有键盘输入事件,包括Win组合键。一旦检测到需要屏蔽的按键组合,就直接丢弃该消息,从而防止其影响系统的正常操作。
这种方法不仅适用于禁用Win组合键,还可以灵活地扩展以支持其他类型的按键控制需求。此前在讲解扫码枪集成时也提及了类似的消息拦截技术,证明了钩子在处理特定输入场景下的有效性和实用性。
钩子原理
钩子是操作系统消息处理的一种机制。通过钩子,应用程序可以安装一个钩子回调过程让系统调用,从而监视系统中的消息队列。在这些消息到达目标窗口之前对这些消息进行处理。
钩子特点
钩子函数会降低操作系统的性能,因为它增加系统处理每一个消息的开销。
操作系统支持多种类型的钩子,每种类型都提供了它特有的消息处理机制。
对于每种类型的钩子,系统都维护一个各自独立的钩子链,钩子链是一个指向用户提供的回调函数钩子过程的链表指针。
钩子尽量避免大量使用,对于一个钩子,一般是需要的时候安装,使用完成后,尽快卸载。
代码实现
下面就直接贴代码了,主要是在键盘钩子处理那里加了一些需要截获的消息。
public class Hook : IDisposable
{
public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
static int hHook = 0;
public const int WH_KEYBOARD_LL = 13;
HookProc KeyBoardHookProcedure;
[StructLayout(LayoutKind.Sequential)]
public class KeyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
public void Start()
{
// 安装键盘钩子
if (hHook == 0)
{
KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
//如果设置钩子失败.
if (hHook == 0)
Close();
else
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SoftwareMicrosoftWindowsCurrentVersionPoliciesSystem", true);
if (key == null)//如果该项不存在的话,则创建该项
key = Registry.CurrentUser.CreateSubKey(@"SoftwareMicrosoftWindowsCurrentVersionPoliciesSystem");
key.SetValue("DisableTaskMgr", 1, RegistryValueKind.DWord);
key.Close();
}
}
}
public void Close()
{
bool retKeyboard = true;
if (hHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hHook);
hHook = 0;
}
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SoftwareMicrosoftWindowsCurrentVersionPoliciesSystem", true);
if (key != null)
{
key.DeleteValue("DisableTaskMgr", false);
key.Close();
}
}
public static int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
{
if (nCode >= 0)
{
KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
if (kbh.vkCode == 91) // 截获左win(开始菜单键)
return 1;
if (kbh.vkCode == 92)// 截获右win
return 1;
if (kbh.vkCode == (int)Keys.Escape && (int)Control.ModifierKeys == (int)Keys.Control) //截获Ctrl+Esc
return 1;
if (kbh.vkCode == (int)Keys.F4 && (int)Control.ModifierKeys == (int)Keys.Alt) //截获alt+f4
return 1;
if (kbh.vkCode == (int)Keys.Tab && (int)Control.ModifierKeys == (int)Keys.Alt) //截获alt+tab
return 1;
if (kbh.vkCode == (int)Keys.Escape && (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Shift) //截获Ctrl+Shift+Esc
return 1;
if (kbh.vkCode == (int)Keys.Space && (int)Control.ModifierKeys == (int)Keys.Alt) //截获alt+空格
return 1;
if (kbh.vkCode == 241) //截获F1
return 1; if (kbh.vkCode == (int)Keys.Control && kbh.vkCode == (int)Keys.Alt && kbh.vkCode == (int)Keys.Delete)
return 1;
if ((int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt + (int)Keys.Delete) //截获Ctrl+Alt+Delete
return 1;
if ((int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Shift) //截获Ctrl+Shift
return 1;
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
public void Dispose()
{
Close();
}
}
钩子使用
使用方法也很简单,在窗体初始化的时候,采用无边框并最大化窗体,然后启动钩子。
private Hook hook;
public FrmMain()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState =
FormWindowState.Maximized;
this.TopMost = true;
hook = new Hook();
hook.Start();
this.FormClosing +=
new FormClosingEventHandler
(FrmMain_FormClosing);
}
在窗体关闭事件里关闭钩子,钩子使用一定要及时关闭。
private void FrmMain_FormClosing
(object sender, FormClosingEventArgs e)
{
hook.Close();
}
这样运行程序后,如果没有提供关闭程序的入门,似乎就只能重启系统了,大家测试时要注意保存电脑现有程序及文件。
总结
通过在C#上位机程序中实现启动后锁定Win键,我们可以有效防止操作人员因误操作导致的程序意外退出,从而保障程序的稳定运行和操作的连续性。
本文介绍了具体的实现方法,通过全局钩子监听键盘事件并阻止Win键的按下。这种方法不仅简单高效,而且能够显著提升上位机程序在工业环境中的可靠性和安全性。通过这些技术手段,企业可以更好地管理生产流程,减少因误操作带来的潜在风险。
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:dotNet工控上位机
出处:mp.weixin.qq.com/s/9selRYddIYITfJYwDF1Xxg
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!