2023年6月23日发(作者:)
⽤C++写windows服务之前急着为完成项⽬中某个功能,需要写个windows服务程序, 定时来调⽤另外的⼀个exe程序, 完成过程遇到颇多问题,作为初学者,分享⼀下我的整个过程. 参考了多位⼤神的⽂章.资料来⾃⽹上, .真⼼表⽰感谢. 颇具有傻⽠型的填空代码, 先这样, 还在学习中,之后把原理搞清楚后再过来补充博客 .博客搬迁, 转移过来的环境: vs2010,多字符集以下是⼀些基本知识, 不得不耐下⼼去理解,这对程序理解和编写⾮常⽤帮助.⾸先Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们⾃⼰的Windows 会话中可长时间运⾏的可执⾏应⽤程序。这些服务可以在计算机启动时⾃动启动,可以暂停和重新启动⽽且不显⽰任何⽤户界⾯。这使服务⾮常适合在服务器上使⽤,或任何时候,为了不影响在同⼀台计算机上⼯作的其他⽤户,需要长时间运⾏功能时使⽤。还可以在不同于登录⽤户的特定⽤户帐户或默认计算机帐户的安全上下⽂中运⾏服务。
服务是有状态的,当我们使⽤windows⾃带的服务管理程序 查看服务状态时可以显⽰服务的当前状态,这个状态是由我们在程序代码中进⾏控制的。你最好在服务初始化的时候将服务设置为SERVICE_START_PENDING,当初始化完毕时设为SERVICE_RUNNING,这些状态是系统⾃定义的状态,可通过msdn查看其他状态。这个状态信息你会在中看到。
在编写windows服务程序过程中你需要关注的函数有:
1.⾸先是main函数,由于windows服务不需要界⾯,所以⼤部分程序为win32控制台应⽤程序,所以程序主函数为main ⽽不是WinMain()。在主函数要做的主要⼯作就是初始化⼀个SERVICE_TABLE_ENTRY 分派表结构体,然后调⽤StartServiceCtrlDispatcher();这将把调⽤进程的主线程转换为控制分派器。该分派器启动⼀个新线程,该线程运⾏分派表中对应于你的服务的ServiceMain()函数。ServiceMain()函数将在下⾯提到。
此过程⽰例代码如下:
SERVICE_TABLE_ENTRY entrytable[2];
entrytable[0].lpServiceName="testservice";
entrytable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;
entrytable[1].lpServiceName=NULL;
entrytable[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(entrytable);
在这之后系统将⾃动创建⼀个线程去执⾏ServiceMain函数的内容,你应该将你要执⾏的任务在ServiceMain中循环,这样服务就开始运⾏了。
2.ServiceMain函数为void WINAPI ServiceMain(int argc, char** argv)格式的函数,函数名字可以任意定义。它的作⽤就是:将你需要执⾏的任务放到该函数中循环执⾏即可。这就是服务程序的⼯作函数。在ServiceMain执⾏你的任务前,需要给SERVICE_TABLE_ENTRY 分派表结构体进⾏赋值,注意由于此时服务还没有开始执⾏你的任务所以我们将服务的状态设置为SERVICE_START_PENDING,即正在初始化。我们进⾏如下赋值:
iceType = SERVICE_WIN32;
entState = SERVICE_START_PENDING;
rolsAccepted=SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP;
//在本例中只接受系统关机和停⽌服务两种控制命令
32ExitCode = 0;
iceSpecificExitCode = 0;
kPoint = 0;
Hint = 0;
hstatus = ::RegisterServiceCtrlHandler("testservice", CtrlHandler);
CtrlHandler为void WINAPI CtrlHandler(DWORD request)型的函数,函数名字可以任意设定。将在下⼀点讲到。Hstatus 为SERVICE_STATUS_HANDLE 类 型 的 全 局 变 量 。 当 需 要 改 变 服 务 状 态 时SetServiceStatus()函数需要它做为参数来标识⼀个服务。
3. void WINAPI CtrlHandler(DWORD request),函数的主要功能是,接收系统传递的控制命令,⽐如当你通过关闭服务时,该函数会收到SERVICE_CONTROL_STOP消息,你就可以对服务进⾏必要的管理。在本例⼦程序中就只接收SERVICE_ACCEPT_SHUTDOWN 和SERVICE_ACCEPT_STOP消息,这是通过前⾯给servicestatus赋值设定的。 这样⼀个基本的服务程序就完成了。 本⽂结束的时候会附上如何安装服务。当服务程序需要使⽤某些功能时,由于⽤户的关系⽽受到限制,⽐如访问注册表的HKEY_CURRENT_USER键,使⽤⽹络等等,这时候就需要以当前登陆⽤户的⾝份去进⾏操作,通常会创建⼀个进程来完成需要的功能。如果使⽤CreateProcess, 来创建进程的话,新创建的进程和服务程序依然是相同的⽤户⾝份,还是⽆法达到⽬的,只有使⽤CreateProcessAsUser了。但CreateProcessAsUser的第⼀个参数是 HANDLE
,该参数通常应该⽤LogonUser来获得,但是LogonUser⼜需要⽤户名和⽤户密码,这样就很不现实。那应该怎么办呢?我想到了⼀个⽅法可以绕过LogonUser直接获得hToken。因为⽤户已经登陆,那么肯定有Shell(就是)运⾏了,我们可以通过遍历进程来取得Shell的hToken来运⾏进程。
因此需要BOOL GetTokenByName(HANDLE &hToken,LPSTR lpName);BOOL RunProcess(LPCSTR lpImage);两个函数⽰例是关于基于opencv⼈脸识别, 遍历样本⽂件夹,删除多余的图⽚保留10张, 然后执⾏ 外部⾃定义程序" "函数提取特征
中最头上加上#pragma comment(linker,"/subsystem:"Windows" /entry:"mainCRTStartup"")就可以隐藏控制台窗⼝开发环境vs2010, 控制台应⽤程序 ⼀个.cpp⽂件.//服务程序主函数。#include"stdio.h"#include"stdio.h"#include"vector"#include"Tlhelp32.h"#include#define_AFXDLL//由于做的图像识别需要opencv头⽂件, 需要什么⽂件⾃⾏更改#include"cv.h"#include"highgui.h"usingnamespacestd;//你的服务程序需要以下代码SERVICE_STATUS servicestatus;SERVICE_STATUS_HANDLE hstatus;//全局变量. 是setServiceStatus()的参数, 改变服务状态voidWINAPI ServiceMain(intargc,char**argv);voidWINAPI CtrlHandler(DWORD request);boolbrun=false;//原来代码有的,我没有⽤,还是保留//以下是以获取登录⽤户名BOOL GetTokenByName(HANDLE &hToken,LPSTR lpName);BOOL RunProcess(LPCSTR lpImage);//⾃⼰添加的代码inttrain_time;//以分钟计vectorVec_Dir;//存放图⽚⽂件夹⽬录名称vectorVec_Img;//voidTraverseDir(CString&strDir,std::vector&vecDir);intTraverseImg(CString&strDir,std::vector&vecFile);voidTraverseDir(CString&strDir,std::vector&vecDir){WIN32_FIND_DATA FindFileData;CStringstrDirTmp;strDirTmp =strDir;strDirTmp +="*.*";HANDLE hFind=::FindFirstFile(strDirTmp,&FindFileData);if(INVALID_HANDLE_VALUE ==hFind){return;}while(TRUE){if(Attributes &FILE_ATTRIBUTE_DIRECTORY){if(ame[0]!=_T('.')){strDirTmp =strDir;strDirTmp +="";strDirTmp +=ame;_back(strDirTmp);//保存所有⽬录//TraverseDir(strDirTmp,vecFile) ;}}else//是⽂件{/*strDirTmp = strDir;strDirTmp += "";strDirTmp += ame;_back(strDirTmp);*/}if(!FindNextFile(hFind,&FindFileData))break;}FindClose(hFind);}}intTraverseImg(CString&strDir,std::vector&vecFile)//输⼊路径,得到img路径⽂件名 不⽤的请忽视{intImgNum=0;WIN32_FIND_DATA FindFileData;CStringstrDirTmp;strDirTmp =strDir;strDirTmp +="*.*";HANDLE hFind=::FindFirstFile(strDirTmp,&FindFileData);if(INVALID_HANDLE_VALUE ==hFind){//return;}while(TRUE){if(Attributes &FILE_ATTRIBUTE_DIRECTORY){if(ame[0]!=_T('.')){/*strDirTmp = strDir;strDirTmp += "";strDirTmp += ame;TraverseDir(strDirTmp,vecFile) ;*/}}else{strDirTmp =strDir;strDirTmp +="";strDirTmp +=ame;_back(strDirTmp);//将图⽚路径传⼊ImgNum++;}if(!FindNextFile(hFind,&FindFileData))break;}FindClose(hFind);returnImgNum;}voidWINAPI ServiceMain(intargc,char**argv){iceType =SERVICE_WIN32;entState =SERVICE_START_PENDING;rolsAccepted =SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP;//在本例中只接受系统关机和停⽌服务两种控制命令32ExitCode =0;iceSpecificExitCode =0;kPoint =0;Hint =0;hstatus =::RegisterServiceCtrlHandler("testservice",CtrlHandler);if(hstatus==0){return;}//向SCM 报告运⾏状态
entState =SERVICE_RUNNING;SetServiceStatus(hstatus,&servicestatus);//下⾯就//下⾯就brun=true;//以下是⾃⼰要写的代码的执⾏调⽤地⽅.开始任务循环了,你可以添加你⾃⼰希望服务做的⼯作
//SYSTEMTIME t;
//GetLocalTime(&t);
//int hour = ;//获取⼩时, 可以在固定某个⼩时执⾏程序while(1)//{CStringSamplesDirPath=_T("G:Samples");TraverseDir(SamplesDirPath,Vec_Dir);//获取⽬录名称到vec_Dirfor(inti(0);i10){for(intj=Img_Num-10-1;j>=0;j--){remove(Vec_Img[j]);}}Vec_();}//⼏种调⽤外部程序的⽅法,但除了RunProcess其他都是以system⾝份打开程序.//WinExec("G:about_", 0);//system( "");//ShellExecute(NULL,"open","G:about_",NULL,NULL,SW_SHOWNORMAL);RunProcess("G:about_");//从硬盘⾥读取时间来做个每隔多少时间进⾏CvFileStorage*Threshold=cvOpenFileStorage("./service_",0,CV_STORAGE_READ);//读取预值CvFileNode*ThresholdNode=cvGetFileNodeByName(Threshold,0,"circle_time");doubleservice_time =cvReadRealByName(Threshold,ThresholdNode,"circle_time");Sleep(service_time*60*1000);//sleep⾃定时间后再次执⾏操作}}BOOL GetTokenByName(HANDLE &hToken,LPSTR lpName){if(!lpName){returnFALSE;}HANDLE hProcessSnap =NULL;BOOL bRet =FALSE;PROCESSENTRY32 pe32 ={0};hProcessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);if(hProcessSnap ==INVALID_HANDLE_VALUE)return(FALSE); =sizeof(PROCESSENTRY32);if(Process32First(hProcessSnap,&pe32)){do{if(!strcmp(_strupr(ile),_strupr(lpName))){{HANDLE hProcess =OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,32ProcessID);bRet =OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,&hToken);CloseHandle(hProcessSnap);return(bRet);}}while(Process32Next(hProcessSnap,&pe32));bRet =TRUE;}elsebRet =FALSE;CloseHandle(hProcessSnap);return(bRet);}BOOL RunProcess(LPCSTR lpImage){if(!lpImage){returnFALSE;}HANDLE hToken;if(!GetTokenByName(hToken,"")){returnFALSE;}STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si,sizeof(STARTUPINFO));=sizeof(STARTUPINFO);top =TEXT("winsta0default");BOOL bResult =CreateProcessAsUser(hToken,lpImage,NULL,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);CloseHandle(hToken);if(bResult){OutputDebugString("CreateProcessAsUser ok!rn");}else{OutputDebugString("CreateProcessAsUser false!rn");}returnbResult;}然后安装服务点开始运⾏输⼊以下:sc create your_service_name binpath= D: //这步注意等号右边有个空格sc start your_service_name //启动服务, 也可以启动任务管理器在服务⼀栏中找到你的服务启动或停⽌ sc stop testservicename //停⽌服务sc delete testservicename //删除服务,该服务将在下次重启后删除,在重启之前将不能注册同⼀个名字的服务。
启动服务, 停⽌服务,等也可以在windows任务管理器中管理
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687516718a16283.html
评论列表(0条)