电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

VC实现系统热键激活后台服务程序


发布日期:2023/12/18
 

摘要 本文给出了一种通过设置系统热键来呼出在系统后台隐藏运行的服务程序的一种方法通过这种方法可以实现后台服务程序在必要的时候同用户的交互设置

引言

通常情况下用于后台监控的服务程序(Service)是没有界面的甚至也没有提供任务栏图标因此绝大多数情况下服务程序是无法同用户进行交互的但是在实际应用中这些服务程序虽然绝大多数时间是在后台运行但是在某些必要的情况下还是需要用户的干预并同用户进行一些必要的交互操作但是由于服务程序没有提供任何可供交互操作之用的界面因此如何将其从后台激活(即呼出)成为解决此问题的一个关键本文下面就给出一种通过设置系统热键的方法来激活运行于后台的服务程序

设计思路

尽管从理论上可以有许多方法来激活后台运行的服务程序比如可以通过寻找服务程序的窗口标题名而得到其窗口指针然后再向此窗口发送消息使其出现到前台;也可以通过系统快照对当前系统进程进行枚举然后再将其激活到前台但是以上这些方法都需要另外编写应用程序对后台服务程序的激活实际是再这些应用程序中进行的这样的处理方式显然十分不便最好的方法是对程序的激活和隐藏处理均在服务程序内部完成因此可以考虑接收系统发出的消息如果通过设置全局钩子对设置事件进行拦截捕获显然是相当烦琐的在此考虑使用系统热键来激活后台服务程序其实现过程非常简单只需先向操作系统添加一个全局原子(Atom)然后再向操作系统登记一个热键当程序在后台运行期间一旦有此热键按下操作系统将会抛出系统消息WM_HOTKEY所以服务程序只需在 WM_HOTKEY消息响应函数中添加相应代码即可实现服务程序的后台激活

系统热键的注册

根据前面的介绍不难写出为后台服务程序添加对系统热键响应的功能代码首先通过函数GlobalFindAtom()查询本服务程序所对应的全局原子是否已存在于全局原子表中如果发现则说明系统中已经存在有此服务程序退出如果没有发现则通过GlobalAddAtom()函数向全局原子表添加一个字串并获取得到一个唯一标识此字串的原子以上两函数原型分别为

以下是引用片段

ATOMGlobalFindAtom(LPCTSTRlpString);

ATOMGlobalAddAtom(LPCTSTRlpString);

其中输入参数为一个描述原子的字符串如果GlobalFindAtom()从全局原子表中找到了指定的字串那么将返回此字串对应的原子否则返回GlobalAddAtom()如果创建成功将返回一个新创建的原子

接下来为了能在程序运行期间捕获到系统热键需要通过RegisterHotKey()定义一个系统范围的热键该函数原形如下

以下是引用片段

BOOLRegisterHotKey(HWNDhWnd//接收热键响应的窗口句柄

intid//热键的标识

UINTfsModifiers//控制键标志

UINTvk//虚拟键值

);

其中热键标识id必须是一个范围在xCxFFFF之间的全局唯一的值为了避免可能引起的热键沖突通常把GlobalAddAtom ()返回的原子作为参数传入而且GlobalAddAtom()返回值的范围同id参数的允许范围是完全一致的参数fsModifiers定义了同虚拟键值vk同时按下而产生出系统热键消息WM_HOTKEY的控制键组合如MOD_ALTMOD_CONTROLMOD_SHIFT和 MOD_WIN等在本例中将要设定的系统热键为Alt+Ctrl+R因此参数fsModifiers和vk分别设置为MOD_ALT| MOD_CONTROL和VK_R有关系统热键的注册实现方法可以整理如下

以下是引用片段

//获取当前窗口句柄

HWNDhandle=GetSafeHwnd();

//寻找HotKey对应的原子是否存在于原子列表

if(GlobalFindAtom(Hotkey)==)

{

//如果没有存在于原子列表则创建一个原子

id=GlobalAddAtom(Hotkey);

//注册全局热键Ctrl+Alt+R

RegisterHotKey(handleidCONTROL+ALTR);

}

else//如果HotKey已经存在于原子列表则终止程序运行

PostQuitMessage();

服务程序的隐藏与激活

服务程序除了被激活后同用户的交互绝大部分时间都是在后台隐藏运行的不仅界面是不可视的而且在任务列表中也不应当出现关于界面的隐藏比较简单可以通过向ShowWindow()函数设置SW_HIDE参数来实现而在任务列表中的隐身则一般的做法是通过调用系统内核KernelDLL的RegisterServiceProcess()函数将其设置成为一个服务进程这样在任务列表中也实现了隐身但是RegisterServiceProcess()函数并非一个标准的API函数使用起来有点烦琐首先要通过 GetModuleHandle()函数得到KernelDLL模块的句柄并由此通过GetProcAddress()函数进一步得出 RegisterServiceProcess()函数在KernelDLL中的入口地址最后才能使用 RegisterServiceProcess()函数该函数原型声明如下

以下是引用片段

DWORDRegisterServiceProcess(DWORDdwProcessIdDWORDdwType);

其第一个参数指定了将要注册为服务进程的进程标识参数dwType指定是去注册一个服务进程(为时)还是去卸载一个服务进程(为时)其具体服务注册过程如下

typedefDWORD(WINAPI*RSP)(DWORDdwProcessIdDWORDdwType);

//获取KernelDLL模块句柄

HMODULEm_hKernel=::GetModuleHandle(KernelDLL);

//得到RegisterServiceProcess()函数入口地址

RSPm_rsp=(RSP)::GetProcAddress(m_hKernelRegisterServiceProcess);

//注册当前进程为服务进程

m_rsp(::GetCurrentProcessId());

在服务程序后台运行期间一旦有系统热键Alt+Ctrl+R按下将发出系统热键消息WM_HOTKEY该消息的消息响应函数不能通过 ClassWizard来添加而只能手工完成消息映射在消息响应函数中通过对消息参数 wParam的判断可以确定出是否是本服务程序所设定的系统热键如果是通过ShowWindow(SW_SHOW)将程序界面显示出来以进行同用户的交互操作:

以下是引用片段

voidCServiceDlg::OnHotKey(WPARAMwParamLPARAMlParam)

{

//判断是否是本服务程序设置的系统热键

if(wParam==id)

{

……

//在此发送WM_PAINT消息在OnPain()中通过

//ShowWindow(SW_SHOW)将界面设置为可视

PostMessage(WM_PAINT);

}

}

系统热键的卸载

由于前面将系统热键全局原子等都注册到系统因此必须在服务程序退出之前将其卸载否则将导致下次注册时的失败函数UnregisterHotKey()负责完成对系统热键的释放GlobalDeleteAtom()将全局原子从全局原子列表删除

小结

通过本文所述方法为后台运行的系统服务程序添加此热键呼出功能可以真正实现程序的后台隐蔽运行热键激活非常有利于管理员和用户的管理与使用本文所述程序在Windows Professional下由Microsoft Visual C++ 编译通过

上一篇:Task.Unwrap基本使用

下一篇:建立同SAS交互的开发式VB客户端