摘要 本文给出了一种通过设置系统热键来呼出在系统后台隐藏运行的服务程序的一种方法 通过这种方法 可以实现后台服务程序在必要的时候同用户的交互设置
引言
通常情况下 用于后台监控的服务程序(Service)是没有界面的 甚至也没有提供任务栏图标 因此绝大多数情况下服务程序是无法同用户进行交互的 但是在实际应用中 这些服务程序虽然绝大多数时间是在后台运行 但是在某些必要的情况下还是需要用户的干预并同用户进行一些必要的交互 *** 作 但是由于服务程序没有提供任何可供交互 *** 作之用的界面 因此如何将其从后台激活(即呼出)成为解决此问题的一个关键 本文下面就给出一种通过设置系统热键的方法来激活运行于后台的服务程序
设计思路
尽管从理论上可以有许多方法来激活后台运行的服务程序 比如可以通过寻找服务程序的窗口标题名而得到其窗口指针 然后再向此窗口发送消息使其出现到前台也可以通过系统快照对当前系统进程进行枚举 然后再将其激活到前台 但是以上这些方法都需要另外编写应用程序 对后台服务程序的激活实际是再这些应用程序中进行的 这样的处理方式显然十分不便 最好的方法是对程序的激活和隐藏处理均在服务程序内部完成 因此可以考虑接收系统发出的消息 如果通过设置全局钩子对设置事件进行拦截捕获 显然是相当烦琐的 在此考虑使用系统热键来激活后台服务程序 其实现过程非常简单 只需先向 *** 作系统添加一个全局原子(Atom) 然后再向 *** 作系统登记一个热键 当程序在后台运行期间一旦有此热键按下 *** 作系统将会抛出系统消息WM_HOTKEY 所以服务程序只需在 WM_HOTKEY消息响应函数中添加相应代码即可实现服务程序的后台激活
系统热键的注册
根据前面的介绍 不难写出为后台服务程序添加对系统热键响应的功能代码 首先通过函数GlobalFindAtom()查询本服务程序所对应的全局原子是否已存在于全局原子表中 如果发现 则说明系统中已经存在有此服务 程序退出 如果没有发现 则通过GlobalAddAtom()函数向全局原子表添加一个字串 并获取得到一个唯一标识此字串的原子 以上两函数原型分别为
以下是引用片段 ATOM GlobalFindAtom(LPCTSTR lpString)ATOM GlobalAddAtom(LPCTSTR lpString)
其中 输入参数为一个描述原子的字符串 如果GlobalFindAtom()从全局原子表中找到了指定的字串 那么将返回此字串对应的原子 否则返回 GlobalAddAtom()如果创建成功 将返回一个新创建的原子
接下来 为了能在程序运行期间捕获到系统热键 需要通过RegisterHotKey()定义一个系统范围的热键 该函数原形如下
以下是引用片段 BOOL RegisterHotKey(HWND hWnd // 接收热键响应的窗口句柄 int id // 热键的标识 UINT fsModifiers // 控制键标志 UINT vk // 虚拟键值 )
其中 热键标识id必须是一个范围在 xC 到 xFFFF之间的全局唯一的值 为了避免可能引起的热键冲突 通常把GlobalAddAtom ()返回的原子作为参数传入 而且GlobalAddAtom()返回值的范围同id参数的允许范围是完全一致的 参数fsModifiers定义了同虚拟键值vk同时按下而产生出系统热键消息WM_HOTKEY的控制键组合 如MOD_ALT MOD_CONTROL MOD_SHIFT和 MOD_WIN等 在本例中将要设定的系统热键为Alt+Ctrl+R 因此 参数fsModifiers和vk分别设置为MOD_ALT| MOD_CONTROL和VK_R 有关系统热键的注册实现方法可以整理如下
以下是引用片段 // 获取当前窗口句柄 HWND handle = GetSafeHwnd()// 寻找HotKey对应的原子是否存在于原子列表 if(GlobalFindAtom( Hotkey ) == ) { // 如果没有存在于原子列表 则创建一个原子 id = GlobalAddAtom( Hotkey )//注册全局热键Ctrl + Alt + R RegisterHotKey(handle id CONTROL + ALT R)} else // 如果HotKey已经存在于原子列表 则终止程序运行 PostQuitMessage( )
服务程序的隐藏与激活
服务程序除了被激活后同用户的交互 绝大部分时间都是在后台隐藏运行的 不仅界面是不可视的 而且在任务列表中也不应当出现 关于界面的隐藏比较简单 可以通过向ShowWindow()函数设置SW_HIDE参数来实现 而在任务列表中的隐身则一般的做法是通过调用系统内核Kernel DLL的RegisterServiceProcess()函数将其设置成为一个服务进程 这样 在任务列表中也实现了隐身 但是RegisterServiceProcess()函数并非一个标准的API函数 使用起来有点烦琐 首先要通过 GetModuleHandle()函数得到Kernel DLL模块的句柄 并由此通过GetProcAddress()函数进一步得出 RegisterServiceProcess()函数在Kernel DLL中的入口地址 最后才能使用 RegisterServiceProcess()函数 该函数原型声明如下
以下是引用片段 DWORD RegisterServiceProcess(DWORD dwProcessId DWORD dwType)其第一个参数指定了将要注册为服务进程的进程标识 参数dwType指定是去注册一个服务进程(为 时)还是去卸载一个服务进程(为 时) 其具体服务注册过程如下 typedef DWORD (WINAPI *RSP)(DWORD dwProcessId DWORD dwType)// 获取Kernel DLL模块句柄 HMODULE m_hKernel = ::GetModuleHandle( Kernel DLL )// 得到RegisterServiceProcess()函数入口地址 RSP m_rsp = (RSP)::GetProcAddress(m_hKernel RegisterServiceProcess )// 注册当前进程为服务进程 m_rsp(::GetCurrentProcessId() )
在服务程序后台运行期间 一旦有系统热键Alt+Ctrl+R按下 将发出系统热键消息WM_HOTKEY 该消息的消息响应函数不能通过 ClassWizard来添加 而只能手工完成消息映射 在消息响应函数中 通过对消息参数 wParam的判断可以确定出是否是本服务程序所设定的系统热键 如果是 通过ShowWindow(SW_SHOW)将程序界面显示出来 以进行同用户的交互 *** 作:
以下是引用片段 void CServiceDlg::OnHotKey(WPARAM wParam LPARAM lParam) { // 判断是否是本服务程序设置的系统热键 if (wParam == id) { …… // 在此发送WM_PAINT消息 在OnPain()中通过 // ShowWindow(SW_SHOW)将界面设置为可视 PostMessage(WM_PAINT )} }
系统热键的卸载
由于前面将系统热键 全局原子等都注册到系统 因此必须在服务程序退出之前将其卸载 否则将导致下次注册时的失败 函数UnregisterHotKey()负责完成对系统热键的释放 GlobalDeleteAtom()将全局原子从全局原子列表删除
小结
lishixinzhi/Article/program/net/201311/13819这是我前几天写的一个例子:我给你注释一下,如果还不懂,就加我:Q11(59)Q(59)903
// ProcApp.cpp : 定义控制台应用程序的入口点。
//
#pragma once
#include "stdafx.h"
#include <windows.h>
#include <winioctl.h>
//这是我自定义的类型,和你问和问题无关,你就把它当成一个类型就是了
typedef struct _CALL_BACK
{
HANDLE pParentId
HANDLE pProcessId
BOOLEAN bCreate
}CALLBACKINFO,*PCALLBACKINFO
//这个定义也和创建服务无关。你就把它当成一个常量吧
#define IOCTL_NTPROCDRV_GET_PROCINFO CTL_CODE(FILE_DEVICE_UNKNOWN,0x0800,METHOD_BUFFERED,FILE_READ_ACCESS|FILE_WRITE_ACCESS)
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t szSymlName[]=L"slNTProcDrvYH"
wchar_t szDriverPath[MAX_PATH]
wchar_t *p
::GetFullPathName(L"服务.sys",MAX_PATH,szDriverPath,&p)
//上面这个是得到服务的可执行文件路径,一个服务总有一个可执行文件的
//打开服务管理器,第一个参数是计算机名,本机就用NULL,第二个是数据库名
//默认数据库就用NULL,第三个是访问权限。这个API返回服务管理器句柄。
SC_HANDLE scm=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS)
if (!scm)
{
printf("OpenSCManager失败!")
return 0
}
//这个是创建服务,它的第一个参数是服务管理器的句柄,第二个参数是服务
//名,第三个参数是服务的显示名,第四个参数是权限设置,第五个是服务类
//型,其它的你看一下MSDN吧
SC_HANDLE scs=CreateService(scm,szSymlName,szSymlName,SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,szDriverPath,NULL,0,NULL,NULL, NULL)
if (!scs)
{
int nError=GetLastError()
if (nError==ERROR_SERVICE_EXISTS||nError==ERROR_SERVICE_MARKED_FOR_DELETE)
{
scs=OpenService(scm,szSymlName,SERVICE_ALL_ACCESS)
}
if (!scs)
{
printf("CreateService失败!%d",GetLastError())
return 0
}
}
//这里是启动服务
if (!::StartService(scs,0,NULL))
{
int nError=::GetLastError()
if (nError!=ERROR_SERVICE_ALREADY_RUNNING)
{
printf("StartService失败!%d\n",nError)
CloseServiceHandle(scs)
CloseServiceHandle(scm)
return 0
}
}
//下面的和你的问题没有关了,你可以不用看了,但是后面有个关闭句柄的,不要忘了
HANDLE hDriver=::CreateFile(L"\\\\.\\slNTProcDrv",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)
if (hDriver==INVALID_HANDLE_VALUE)
{
printf("打开设备失败!")
return 0
}
HANDLE hProcessEvent=::OpenEvent(SYNCHRONIZE,FALSE,TEXT("Global\\NTProcDrvProcessEvent"))
if (!hProcessEvent)
{
printf("OpenEvent失败!%d\n",::GetLastError())
CloseServiceHandle(scs)
CloseServiceHandle(scm)
return 0
}
CALLBACKINFO callbackinfo,callbacktemp={0}
while(::WaitForSingleObject(hProcessEvent,INFINITE)==WAIT_OBJECT_0)
{
DWORD nBytsReturn
BOOL bRet=::DeviceIoControl(hDriver,IOCTL_NTPROCDRV_GET_PROCINFO,NULL,0,(LPVOID)&callbackinfo,sizeof(callbackinfo),&nBytsReturn,NULL)
if (bRet)
{
if (callbackinfo.bCreate!=callbacktemp.bCreate||callbacktemp.pParentId!=callbackinfo.pParentId||callbackinfo.pProcessId!=callbacktemp.pProcessId)
{
if (callbackinfo.bCreate)
{
printf("有进程被创建,PID:%d\n",callbackinfo.pProcessId)
}
else
{
printf("有进程被结束,PID: %d\n",callbackinfo.pProcessId)
}
}
}
else
{
printf("进程信息获取失败!")
break
}
}
CloseHandle(hDriver)
SERVICE_STATUS ss
::ControlService(scs,SERVICE_CONTROL_STOP,&ss)
::DeleteService(scs)
::CloseServiceHandle(scs)
::CloseServiceHandle(scm)
return 0
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)