228上位机VC MFC实现服务程序安装卸载启动停止及执行任务
228上位机VC MFC实现服务程序安装卸载启动停止及执行任务 功能展示 窗口服务程序能够在后台无声息实现多种功能,能够随系统一同启动,那么如何实现窗口服务程序的编写呢?我们当前例程实现窗口服务程序的编写,以及编写另一例程实现服务程序的安装,卸载,启动停止及执行指定功能,效果如图; 要点提示 服务程序可以用ATL创建向导建立工程。 1.程序的进入点是全局函数_tWinMain()函数中有三个参数由外部程序调用传入RegServer这个实现本地服务器注册,Service这个实现服务程序的注册,UnRegServer这个实现服务程序的删除; 2.向导为我们建立了一个类:CServiceModule,全局变量_Module就是这个类的实例。还有函数Init():这个函数用于完成一些初始化工作;Run():这个函数就是服务开始运行后的内容。 Install()函数为服务程序安装时调用,有如下一段代码 SC_HANDLE hService =::CreateService( hSCM, m_szServiceNamem_szServiceName, SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL,_T("RPCSS\0"), NULL, NULL); 注意:如果服务中启动的程序具有窗口(即具有交互功能则要求使用如下代码) SC_HANDLE hService =::CreteService( hSCM, m_szServiceName,m_szServiceName, SERVICE_ALL_ACCESS,SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START,SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL,_T("RPCSS\0"), NULL, NULL); 这个CreateService函数原型为: SC_HANDLE CreateService( SC_HANDLE hSCManager, // handle to SCM database LPCTSTR lpServiceName, // name of service to start LPCTSTR lpDisplayName, // display name DWORD dwDesiredAccess, // type of access to service DWORD dwServiceType, // type of service DWORD dwStartType, // when to start service DWORD dwErrorControl, // severity of servicefailure LPCTSTR lpBinaryPathName, // name of binary file LPCTSTR lpLoadOrderGroup, // name of load ordering group LPDWORD lpdwTagId, // tag identifier LPCTSTR lpDependencies, // array of dependency names LPCTSTR lpServiceStartName, // account name LPCTSTR lpPassword // account password ); 第六个参数是服务的启动类型。 SERVICE_DEMAND_START是手动启动,SERVICE_AUTO_START是自动启动。 第十一个参数是服务的依存关系,比如说服务的启动想要依存SQL Server的启动,那我们可以把这个参数写成:_T("MSSQLSERVER\0"); 如果我们写的服务不依存于其他的任何服务,那我们就将此参数设置为NULL就可以了。 接下来我们实现我们需要实现的业务。 首先,我们在类CServiceModule中找到Run函数,并在Run函数中找到以下代码: MSG msg; while (GetMessage(&msg, 0, 0,0)) DispatchMessage(&msg); 并在此代码前加入自己的代码,实现自己想要的功能;当然也可以在函数Handler()中实现功能,如例程添加代码实现指定功能case SERVICE_CONTROL_CUSTOM:CustomFunc();break; 3.有时我们会用到MFC类如Cstring时会编译出错,可以在stdafx.h头文件中找到#include<atlbase.h>位置,并在它之前加入#include <afx.h>。重新编译即可。 4.如果需要修改出现在scm中的服务名,可以在工程中找到资源文件中的IDS_SERVICENAME项的内容就可以 5.服务程序的安装,卸载,启动,停止,执行任务,可以参考调用例程的按钮代码 实现功能 1.新建基于ATL COM AppWizard工程的服务(EXE)(S)程序; 2.在头文件stdafx.h头文件中找到#include <atlbase.h>,前面添加代码//<> #include <afx.h> //CString 等MFC类头文件 #include<shlobj.h>//SHGetSpecialFolderPath 函数头文件 #define SERVICE_CONTROL_CUSTOM 129//定义用户消息 3.添加自定义函数CustomFunc(),并在函数Handler()中调用caseSERVICE_CONTROL_CUSTOM: CustomFunc();break; - void CServiceModule::CustomFunc()
- {
- BOOL bRes = FALSE;
- char lpPath[MAX_PATH];
- DWORD RetVal = 0;
- DWORD ErrCode = 0;
- DWORD ConsoleSessionId = 0;
- // 函数的句柄
- HMODULE hInstKernel32 = NULL;
- HMODULE hInstWtsapi32 = NULL;
- // Token的句柄
- HANDLE hTokenUser = NULL;
- HANDLE hTokenThisProcess = NULL;
- HANDLE hTokenThis = NULL;
- // WTSGetActiveConsoleSessionId 函数,得到当前登录用户的会话ID
- // 例程使用VC6.0编译,新版编译器已包含此函数,无需LoadLibrary。
- typedef DWORD (WINAPI *WTSGetActiveConsoleSessionIdPROC)();
- WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;
- hInstKernel32 = LoadLibrary("Kernel32.dll");
- if (!hInstKernel32) return ;
- WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC)GetProcAddress(hInstKernel32,"WTSGetActiveConsoleSessionId");
- if (!WTSGetActiveConsoleSessionId) return ;
- // WTSQueryUserToken 函数,通过会话ID得到令牌
- typedef BOOL (WINAPI *WTSQueryUserTokenPROC)(ULONG SessionId, PHANDLE phToken );
- WTSQueryUserTokenPROC WTSQueryUserToken = NULL;
- hInstWtsapi32 = LoadLibrary("Wtsapi32.dll
复制代码- if (!hInstWtsapi32) return ;
- WTSQueryUserToken = (WTSQueryUserTokenPROC)GetProcAddress(hInstWtsapi32,"WTSQueryUserToken");
- if (!WTSQueryUserToken) return ;
- // 得到当前激活用户的会话ID
- ConsoleSessionId = WTSGetActiveConsoleSessionId();
- // 得到当前登录用户的令牌
- bRes = WTSQueryUserToken( ConsoleSessionId, &hTokenUser);
- if (!bRes) return ;
- // 模仿成当前登录用户
- bRes = ImpersonateLoggedOnUser(hTokenUser);
- if (!bRes) return ;
- bRes = SHGetSpecialFolderPath(NULL,lpPath,CSIDL_DESKTOP,TRUE);
- if (!bRes) return ;
- RevertToSelf();// 终止模拟
- //<>
- TCHAR szFilePath[_MAX_PATH];
- ::GetModuleFileName(NULL, szFilePath, _MAX_PATH);//获取窗口服务程序路径
- CString sServiceName(szFilePath);;
- sServiceName=sServiceName.Right(sServiceName.GetLength()-sServiceName.ReverseFind('\\'));/*获取窗口服务程序名+\\*/
- CString sTo(lpPath);
- sTo+=sServiceName;
- //<>
- //执行功能1:复制服务程序到桌面
- CopyFile(szFilePath,sTo,true);
- //执行功能2:保存源路径与目标路径到桌面
- sTo=lpPath;
- sTo+="\\gkbc8.txt";
- CStdioFile file(sTo,CFile::modeCreate|CFile::modeReadWrite);
- file.WriteString(szFilePath);
- file.WriteString("\r\n");
- file.WriteString(sTo);
- }
复制代码4.再新建一个基于对话框的程序; 5.分别添加编辑框按钮ID为IDC_EDIT1,按钮控件<安装服务程序><启/停服务程序><卸载服务程序><让服务程序执行指定功能>分别关联按钮的点击函数,函数体参考例程源码 6.最后在头文件StdAfx.h中添加以下代码 #include <winsvc.h>//服务函数头文件 #define SERVICE_CONTROL_CUSTOM 129//自定义消息 便可实现服务程序的注册卸载,启动停止,执行功能; 我们来演示下功能实现的整个过程
|