有时候需要windows服务程序来执行一些操作, 比如需要在登陆前启动, 不想被杀软判为自启动, 当然还是能被发现, 但是杀软的普通清理时不会清理的。但是服务程序不能进行界面交互, 因为他不属于用户界面。他只是在后台默默的启动, 运行, 对于收集数据, 后台更新再合适不过了。 一个服务程序的简单写法:
SERVICE_STATUS_HANDLE hServiceStatus;
SERVICE_STATUS ServiceStatus;
void WINAPI ServiceHandler(DWORD fdwControl)
{
switch(fdwControl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
break;
default:
return;
};
if (!SetServiceStatus(hServiceStatus, &ServiceStatus))
{
DWORD nError = GetLastError();
}
}
void WINAPI ServiceMain(DWORD argc, char**argv)
{
ServiceStatus.dwCurrentState=SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
//注册服务控制函数
hServiceStatus=RegisterServiceCtrlHandler(L"UlogReport",ServiceHandler);
if(!hServiceStatus)
{
return;
}
//设置服务状态
SetServiceStatus(hServiceStatus,&ServiceStatus);
ServiceStatus.dwWin32ExitCode=S_OK;
ServiceStatus.dwCheckPoint=0;
ServiceStatus.dwWaitHint=0;
ServiceStatus.dwCurrentState=SERVICE_RUNNING;
SetServiceStatus(hServiceStatus,&ServiceStatus);
//guid
TCHAR szGuid[50] = {0};
Base::GUID::GetGuid(szGuid, 50);
//osversion
CString szOSVer = L"0.0";
OSVERSIONINFOEX osVer;
osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((OSVERSIONINFO *)&osVer))
{
szOSVer.Format(L"%d.%d", osVer.dwMajorVersion,osVer.dwMinorVersion);
}
//supplyid
TCHAR szModule[MAX_PATH] = {0};
GetModuleFileName(NULL, szModule, MAX_PATH);
PathRemoveFileSpec(szModule);
PathAppend(szModule, L"record.ini");
CIniFile iniRecord;
iniRecord.SetPath(szModule);
CString szSuppyID = iniRecord.GetKeyValue(_T("Supply"),_T("id"));
//install software
CString softwares = iniRecord.GetKeyValue(_T("installsoft"),_T("sofid"));
char szTypeSend[]="install";
CStringA csMsgSend;
csMsgSend.Format("guid:%ws;os:%ws;supplyid:%ws;software:%ws;", szGuid, szOSVer, szSuppyID,softwares);
BYTE typelen = strlen(szTypeSend);
DWORD msgLen = csMsgSend.GetLength();
BYTE appID = 3;
int packLen = sizeof(MSG_PACK) + typelen + msgLen + 1;
MSG_PACK* msg = (MSG_PACK*) new BYTE[packLen];
memset((char*)msg,'\n',packLen);
msg->typeLen = typelen;
msg->msgLen = htonl(msgLen);
msg->appID = appID;
memcpy((BYTE*)(msg->szInfo), szTypeSend ,typelen);
memcpy((BYTE*)(msg->szInfo)+typelen, csMsgSend.GetString(), msgLen);
char szlog[MAX_PATH] = {0};
GetTempPathA(MAX_PATH, szlog);
PathAppendA(szlog, "logContent.txt");
std::ofstream ofile(szlog);
ofile<<csMsgSend.GetString()<<std::endl;
ofile.close();
CLogReport::Instance()->Send((char*)msg, packLen);
CLogReport::Release();
//退出服务
ServiceStatus.dwCurrentState=SERVICE_STOPPED;
SetServiceStatus(hServiceStatus,&ServiceStatus);
ShellRun(L"cmd.exe", L"/c sc delete UlogReport");
if (GetExecDir() == _T("C:\\Program Files\\Common Files\\BDInstall"))
{
std::ofstream ofile("C:\\Program Files\\Common Files\\BDInstall\\CleanTemp.bat");
ofile<<"cd..&rd /s /q \"%~dp0\"&exit";
ofile.close();
WinExec("C:\\Program Files\\Common Files\\BDInstall\\CleanTemp.bat",SW_HIDE);
}
exit(0);
}
int _tmain(int argc, _TCHAR* argv[])
{
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ L"UlogReport", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
// Call the service control dispatcher with our entry table
if (!StartServiceCtrlDispatcher(dispatchTable))
{
return GetLastError();
}
return 0;
}
_tmain不用说, 不管是什么程序都需要这么一个入口, 要不操作系统该如何去启动这个程序。
ServiceMain就是用来被调用的主函数, 只有用StartServiceCtrlDispatcher调用这个主函数, 才会被服务管理程序认可, 否则就会被服务管理程序终止掉。
ServiceHandler是提供给服务管理器的控制接口, 就是能够在服务管理器中手动启动, 停止的函数。
服务程序写好了,现在就该把他交给服务管理器, 以便能够在系统启动时启动, 有两种方法:
1. cmd命令。这个最方便, 一句命令行就能搞定。
<span style="white-space:pre"> </span>CString strCmd;
strCmd.Format(L"/c sc create UlogReport binPath= \"%s\" start= auto", GetExecDir()+_T("\\Ulogsvc.exe"));
ShellRun(L"cmd.exe", strCmd);
2. windows API
OpenSCManager打开服务管理器, 然后CreateService。最后CloseServiceHandle关闭句柄。
创建完成之后启动服务, 同样有两种方法cmd和API:
1.ShellRun(L"cmd.exe", L"/c sc start UlogReport");
2.api需要3步:
OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
OpenService( schSCManager, pName, SERVICE_ALL_ACCESS);
StartService(schService,nArg,(LPCTSTR*)pArg));