1、C+快速编写 windows 服务程序调用程序 http:/ 23:42:22| 分类: C/C+ | 标签: windows 服务程序 c+ 快速简单 |字号 订阅前不久,为完成项目中某个功能 ,需要写个 windows 服务程序, 定时来调用另外的一个 exe程序, 完成过程遇到颇多问题 ,作为初学者,分享一下我的整个过程. 参考了多位大神的文章.资料来自网上, 最后给出链接 .表示感谢以下是一些基本知识, 不得不耐下心去理解 ,这对程序理解和编写非常用帮助 .首先 Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的Windows 会话中可长时间运行
2、的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这使服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用。还可以在不同于登录用户的特定用户帐户或默认计算机帐户的安全上下文中运行服务。 服务是有状态的,当我们使用 windows 自带的服务管理程序 sc.exe 查看服务状态时可以显示服务的当前状态,这个状态是由我们在程序代码中进行控制的。你最好在服务初始化的时候将服务设置为 SERVICE_START_PENDING,当初始化完毕时设为SERVICE_RUNNING,这些状态是系统自定义的状态
3、,可通过 msdn 查看其他状态。这个状态信息你会在 sc.exe 中看到。在编写 windows 服务程序过程中你需要关注的函数有: 1.首先是 main 函数,由于 windows 服务不需要界面,所以大部分程序为 win32 控制台应用程序,所以程序主函数为 main 而不是 WinMain()。在主函数要做的主要工作就是初始化一个 SERVICE_TABLE_ENTRY 分派表结构体,然后调用 StartServiceCtrlDispatcher();这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中对应于你的服务的 ServiceMain()函数。Se
4、rviceMain()函数将在下面提到。 此过程示例代码如下: SERVICE_TABLE_ENTRY entrytable2; entrytable0.lpServiceName=“testservice“; entrytable0.lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain; entrytable1.lpServiceName=NULL; entrytable1.lpServiceProc=NULL; StartServiceCtrlDispatcher(entrytable); 在这之后系统将自动创建一个线程去执行 Service
5、Main 函数的内容,你应该将你要执行的任务在 ServiceMain 中循环,这样服务就开始运行了。2ServiceMain 函数为 void WINAPI ServiceMain(int argc, char* argv)格式的函数,函数名字可以任意定义。它的作用就是:将你需要执行的任务放到该函数中循环执行即可。这就是服务程序的工作函数。在 ServiceMain 执行你的任务前,需要给SERVICE_TABLE_ENTRY 分派表结构体进行赋值,注意由于此时服务还没有开始执行你的任务所以我们将服务的状态设置为 SERVICE_START_PENDING,即正在初始化。我们进行如下赋值:
6、servicestatus.dwServiceType = SERVICE_WIN32; servicestatus.dwCurrentState = SERVICE_START_PENDING; servicestatus.dwControlsAccepted=SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP; /在本例中只接受系统关机和停止服务两种控制命令 servicestatus.dwWin32ExitCode = 0; servicestatus.dwServiceSpecificExitCode = 0; servicestatus.dwChe
7、ckPoint = 0; servicestatus.dwWaitHint = 0; hstatus = :RegisterServiceCtrlHandler(“testservice“, CtrlHandler); CtrlHandler 为 void WINAPI CtrlHandler(DWORD request)型的函数,函数名字可以任意设定。将在下一点讲到。Hstatus 为 SERVICE_STATUS_HANDLE 类 型 的 全 局 变 量 。 当 需 要 改 变 服 务 状 态 时 SetServiceStatus()函数需要它做为参数来标识一个服务。3. void WIN
8、API CtrlHandler(DWORD request),函数的主要功能是,接收系统传递的控制命令,比如当你通过 sc.exe 关闭服务时,该函数会收到 SERVICE_CONTROL_STOP 消息,你就可以对服务进行必要的管理。在本例子程序中就只接收SERVICE_ACCEPT_SHUTDOWN 和SERVICE_ACCEPT_STOP 消息,这是通过前面给 servicestatus 赋值设定的。 这样一个基本的服务程序就完成了。 本文结束的时候会附上如何安装服务。当服务程序需要使用某些功能时,由于用户的关系而受到限制,比如访问注册表的HKEY_CURRENT_USER 键,使用网络
9、等等,这时候就需要以当前登陆用户的身份去进行操作,通常会创建一个进程来完成需要的功能。如果使用 CreateProcess, 来创建进程的话,新创建的进程和服务程序依然是相同的用户身份,还是无法达到目的,只有使用 CreateProcessAsUser 了。但 CreateProcessAsUser 的第一个参数是 HANDLE hToken, 该参数通常应该用 LogonUser 来获得,但是 LogonUser又需要用户名和用户密码,这样就很不现实。那应该怎么办呢?我想到了一个方法可以绕过 LogonUser 直接获得 hToken。因为用户已经登陆,那么肯定有Shell(就是 EXPLO
10、RER.EXE)运行了,我们可以通过遍历进程来取得 Shell 的hToken 来运行进程。因此需要BOOL GetTokenByName(HANDLE BOOL RunProcess(LPCSTR lpImage);两个函数示例是关于基于 opencv 人脸识别, 遍历样本文件夹,删除多余的图片保留 10 张, 然后执行 外部自定义程序“GetFeatureDATA.exe “函数提取特征GetFeatureDATA.exe 中最头上加上 #pragma comment(linker,“/subsystem:“Windows“ /entry:“mainCRTStartup“)就可以隐藏控制台
11、窗口开发环境 vs2010, 控制台应用程序./服务程序主函数。#include “stdio.h“#include “vector“#include “Tlhelp32.h“#include #define _AFXDLL/由于做的图像识别需要 opencv 头文件, 需要什么文件自行更改#include “cv.h“#include “highgui.h“using namespace std;/你的服务程序需要以下代码SERVICE_STATUS servicestatus; SERVICE_STATUS_HANDLE hstatus; /全局变量. 是 setServiceStatus
12、()的参数, 改变服务状态void WINAPI ServiceMain(int argc, char* argv); void WINAPI CtrlHandler(DWORD request);bool brun=false;/原来代码有的,我没有用,还是保留/以下是以获取登录用户名BOOL GetTokenByName(HANDLE BOOL RunProcess(LPCSTR lpImage);/自己添加的代码int train_time;/以分钟计vector Vec_Dir;/存放图片文件夹目录名称vector Vec_Img;/void TraverseDir(CStringin
13、t TraverseImg(CStringvoid TraverseDir(CStringCString strDirTmp;strDirTmp = strDir;strDirTmp += “*.*“;HANDLE hFind=:FindFirstFile(strDirTmp,if(INVALID_HANDLE_VALUE = hFind)return;while(TRUE)if(FindFileData.dwFileAttributes strDirTmp += “;strDirTmp += FindFileData.cFileName;vecDir.push_back(strDirTmp)
14、;/保存所有目录/TraverseDir(strDirTmp,vecFile) ;else/是文件/*strDirTmp = strDir;strDirTmp += “;strDirTmp += FindFileData.cFileName;vecFile.push_back(strDirTmp);*/if(!FindNextFile(hFind,FindClose(hFind); int TraverseImg(CStringWIN32_FIND_DATA FindFileData;CString strDirTmp;strDirTmp = strDir;strDirTmp += “*.*“
15、;HANDLE hFind=:FindFirstFile(strDirTmp,if(INVALID_HANDLE_VALUE = hFind)/return;while(TRUE)if(FindFileData.dwFileAttributes strDirTmp += “;strDirTmp += FindFileData.cFileName;TraverseDir(strDirTmp,vecFile) ;*/elsestrDirTmp = strDir;strDirTmp += “;strDirTmp += FindFileData.cFileName;vecFile.push_back(
16、strDirTmp);/将图片路径传入ImgNum +;if(!FindNextFile(hFind,FindClose(hFind);return ImgNum; void WINAPI ServiceMain(int argc, char* argv) servicestatus.dwServiceType = SERVICE_WIN32; servicestatus.dwCurrentState = SERVICE_START_PENDING; servicestatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEP
17、T_STOP;/在本例中只接受系统关机和停止服务两种控制命令servicestatus.dwWin32ExitCode = 0; servicestatus.dwServiceSpecificExitCode = 0; servicestatus.dwCheckPoint = 0; servicestatus.dwWaitHint = 0; hstatus = :RegisterServiceCtrlHandler(“testservice“, CtrlHandler); if (hstatus=0) /WriteToLog(“RegisterServiceCtrlHandler failed
18、“); return; /WriteToLog(“RegisterServiceCtrlHandler success“); /向 SCM 报告运行状态 servicestatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus (hstatus, /下面就开始任务循环了,你可以添加你自己希望服务做的工作 brun=true; /MEMORYSTATUS memstatus; /char str100; /memset(str,0,100); /while (brun) / / GlobalMemoryStatus( / int avail
19、mb=memstatus.dwAvailPhys/1024/1024; / sprintf_s(str,100,“available memory is %dMB“,availmb); / WriteToLog(str); / Sleep(SLEEP_TIME); / /WriteToLog(“service stopped“); /以下是自己要写的代码的执行调用地方/SYSTEMTIME t; /GetLocalTime( /int hour = t.wHour ;/获取小时, 可以在固定某个小时执行程序while(1)/CString SamplesDirPath = _T(“G:Samp
20、les“);TraverseDir(SamplesDirPath, Vec_Dir);/获取目录名称到 vec_Dirfor (int i(0); i10)for (int j=Img_Num - 10 - 1;j=0; j-)remove(Vec_Imgj);Vec_Img.clear();/执行提取 xml 文件 exe 文件/WinExec(“G:about_MFCGetFeatureDATA.exe“, 0);/system( “GetFeatureDATA.exe“);/ShellExecute(NULL,“open“,“G:about_MFCGetFeatureDATA.exe“,
21、NULL,NULL,SW_SHOWNORMAL);RunProcess(“G:about_MFCGetFeatureDATA.exe“);/从硬盘里读取时间来做个每隔多少时间进行CvFileStorage *Threshold = cvOpenFileStorage(“./service_time.xml“,0,CV_STORAGE_READ);/读取预值CvFileNode *ThresholdNode = cvGetFileNodeByName(Threshold,0,“circle_time“);double service_time = cvReadRealByName(Thresho
22、ld,ThresholdNode,“circle_time“);Sleep(service_time*60*1000);/sleep 自定时间后再次执行操作BOOL GetTokenByName(HANDLE HANDLE hProcessSnap = NULL; BOOL bRet = FALSE; PROCESSENTRY32 pe32 = 0; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap = INVALID_HANDLE_VALUE) return (FALSE); pe
23、32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hProcessSnap, bRet = OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,CloseHandle (hProcessSnap); return (bRet); while (Process32Next(hProcessSnap, bRet = TRUE; else bRet = FALSE;CloseHandle (hProcessSnap); return (bRet);BOOL RunProcess(LPCSTR lpImage
24、)if(!lpImage)return FALSE;HANDLE hToken;if(!GetTokenByName(hToken,“EXPLORER.EXE“)return FALSE;STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(si.cb= sizeof(STARTUPINFO);si.lpDesktop = TEXT(“winsta0default“);BOOL bResult = CreateProcessAsUser(hToken,lpImage,NULL,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,CloseHandle(hToken);if(bResult)OutputDebugString(“CreateProcessAsUser ok!rn“);elseOutputDebugString(“CreateProcessAsUser false!rn“);