win7和winservice2003下的守护进程服务,备忘

时间:2020-12-14 18:21:29

有一个几年前的老的数据转发软件,偶尔会自动挂掉,由于历史太久远,不愿意再去做代码修改。就想到开放另外一个守护进程,对转发软件进行检查,发现进程列表中没有该进程就自动重启数据转发程序。而且考虑到可能会有其他相似的需求,考虑做成一个通用的程序,方便再次遇到时候使用。

开始时候一直纠结是做成windows service还是普通的单实例进程,最终选择用windows service

1、创建一个win32控制台程序,添加main.cpp

main.cpp:


#define CONFIGPATH "config\\config.json"
CProcessGroup* pProcessServer;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();

//获取当前目录下的配置文件
Json::Value GetConfig(std::string strPath = "")
{
char szCurrentDir[MAX_PATH + 1] = {0};
char szFileName[MAX_PATH] = {0};
GetModuleFileNameA(NULL, szCurrentDir, MAX_PATH);


sprintf(szFileName,"%s",&(strrchr(szCurrentDir, '\\'))[1]);
(strrchr(szFileName, '.'))[0] = 0;


(strrchr(szCurrentDir, '\\'))[1] = 0;

Json::Reader jrReader;
Json::Value jvProCnfg;
std::ifstream is;

is.open(std::string(szCurrentDir)+CONFIGPATH, std::ios::binary);

if(!jrReader.parse(is, jvProCnfg))
{

return jvProCnfg;
}

std::string capstr = strfunc::StrW2A(strfunc::StrA2W(jvProCnfg.toFastString(),CP_UTF8));
if(!jrReader.parse(capstr, jvProCnfg))
{
ProcessGroupLog().WriteError(L"%s,参数打开配置文件%s失败",
strfunc::StrA2W(__FUNCTION__).c_str(),
strfunc::StrA2W(capstr).c_str());
return jvProCnfg;
}


ProcessGroupLog().WriteInfo(L"%s,打开配置完成 参数:%s",
strfunc::StrA2W(__FUNCTION__).c_str(),
strfunc::StrA2W(jvProCnfg.toFastString()).c_str());


return jvProCnfg;
}

//入口
int _tmain(int argc, _TCHAR* argv[])
{


SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = L"ProcessProtectService";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;


StartServiceCtrlDispatcher(ServiceTable);


return 0;
}

//服务执行函数
void ServiceMain(int argc, char** argv)
{
int error;

ServiceStatus.dwServiceType =
SERVICE_WIN32;
ServiceStatus.dwCurrentState =
SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;



hStatus = RegisterServiceCtrlHandler(
L"ProcessProtectService",
(LPHANDLER_FUNCTION)ControlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
// Registering Control Handler failed
return;
}
// Initialize Service
error = InitService();
if (!error)
{
// Initialization failed
ServiceStatus.dwCurrentState =
SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// We report the running status to SCM.
ServiceStatus.dwCurrentState =
SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);



//MEMORYSTATUS memory;
// The worker loop of a service
while (ServiceStatus.dwCurrentState ==
SERVICE_RUNNING)
{


if(!pProcessServer)
{
pProcessServer = new CProcessGroup();
if(pProcessServer)
{
Json::Value jv = GetConfig();

pProcessServer->Start(jv["processlist"]);
}
}
Sleep(SLEEP_TIME);
}
return;
}

//控制接收,只做了服务的关机和服务关闭响应
void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
ProcessGroupLog().WriteInfo(L"%s,服务关闭",
strfunc::StrA2W(__FUNCTION__).c_str());
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
if(pProcessServer)
{
pProcessServer->Stop();
delete pProcessServer;
pProcessServer = NULL;
}
return;




case SERVICE_CONTROL_SHUTDOWN:
ProcessGroupLog().WriteInfo(L"%s,服务关机",
strfunc::StrA2W(__FUNCTION__).c_str());
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
if(pProcessServer)
{
pProcessServer->Stop();
delete pProcessServer;
pProcessServer = NULL;
}
return;

default:
break;
}


// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
return;
}


int InitService(){

ProcessGroupLog().WriteInfo(L"%s,服务启动",
strfunc::StrA2W(__FUNCTION__).c_str());
pProcessServer = NULL;
return true;
}


进程守护主要代码:

//关闭所有同名进程
bool CProcessGroup::CloseProcess(const std::wstring& name)
{
std::wstring nameUp = name;

transform(name.begin(), name.end(), nameUp.begin(), ::toupper);

PROCESSENTRY32W entry = { 0 };
entry.dwSize = sizeof(PROCESSENTRY32W);

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

if (Process32FirstW(snapshot, &entry))
{
while (Process32NextW(snapshot, &entry))
{
std::wstring temp = entry.szExeFile;

transform(temp.begin(), temp.end(), temp.begin(), ::toupper);
if (nameUp.compare(temp) == 0)
{
if(!TerminateProcess(OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID), 0))
{
ProcessGroupLog().WriteInfo(L"%s 结束进程:%s id:%s 失败!",
strfunc::StrA2W(__FUNCTION__).c_str(),
name.c_str(),
entry.th32ProcessID);
}


}
}
}

CloseHandle(snapshot);
return true;
}


如果被守护进程崩溃,有时不会自动结束进程,会弹出对话框,提示需要关闭,下面做弹窗关闭操作,目前测试只使用了2003和win7测试,没做其他方面考虑:

bool CProcessGroup::CloseHangProcess(const std::wstring &name)
{


//查找转发服务器被挂起时windows提示的窗口
DWORD errThreadID = FindProcess(L"WerFault.exe");
if(0 != errThreadID)
{

HANDLE hHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, errThreadID);//根据PROCESS_ALL_ACCESS属性打开进程
if(NULL == hHandle)
{
hHandle = OpenProcess(0x1F0FFF, FALSE, errThreadID);//根据 0x1F0FFF 属性打开进程
}
TerminateProcess(hHandle, 0);

}
else{
//windows2003里面不会有"WerFault.exe",用以下方法解决(测试可行)。
//
wchar_t wszFilename[MAX_PATH + 1] = {0};


wsprintfW(wszFilename,L"%s",name.c_str());
(wcsrchr(wszFilename, L'.'))[0] = 0;
wsprintfW(wszFilename,L"%s: %s.exe - 应用程序错误",wszFilename,wszFilename);

HWND hwnd = ::FindWindowW(NULL,wszFilename);//0C06FE
if(NULL != hwnd)
{
::PostMessageA(hwnd,WM_CLOSE,NULL,NULL);
}
else{

}
}
return true;
}


开启进程

由于在win7之后的windows版本,增加了Session 0隔离(关于Session 0隔离没有细致了解,可以百度一下),在windows服务中,很多直接对用户的操作不起作用,比如windows service无法通过CreateProcessW开启进程,需要用CreateProcessAsUserW开启进程,但需要注意,在service2003和XP中,貌似不支持CreateProcessAsUserW,所以需要做版本判断

判断window版本,:

std::string GetSystemName()  
{
SYSTEM_INFO info; //用SYSTEM_INFO结构判断64位AMD处理器
GetSystemInfo(&info); //调用GetSystemInfo函数填充结构
OSVERSIONINFOEX os;
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

std::string osname = "unknown";

if(GetVersionEx((OSVERSIONINFO *)&os))
{
//下面根据版本信息判断操作系统名称
switch(os.dwMajorVersion)//判断主版本号
{
case 4:
switch(os.dwMinorVersion)//判断次版本号
{
case 0:
if(os.dwPlatformId==VER_PLATFORM_WIN32_NT)
osname = PRG_WINTYPE::PG_WINNT40;//"NT 4.0"; //1996年7月发布
else if(os.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
osname = PRG_WINTYPE::PG_WIN95;//"Microsoft Windows 95";
break;
case 10:
osname = PRG_WINTYPE::PG_WIN98;//"Microsoft Windows 98";
break;
case 90:
osname = PRG_WINTYPE::PG_WINME;//"Microsoft Windows Me";
break;
}
break;

case 5:
switch(os.dwMinorVersion) //再比较dwMinorVersion的值
{
case 0:
osname = PRG_WINTYPE::PG_WIN2000;//"Microsoft Windows 2000";//1999年12月发布
break;

case 1:
osname = PRG_WINTYPE::PG_WINXP;//"Microsoft Windows XP";//2001年8月发布
break;

case 2:
if(os.wProductType==VER_NT_WORKSTATION
&& info.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
{
osname = PRG_WINTYPE::PG_WINXP64;//"Microsoft Windows XP Professional x64 Edition";
}
else if(GetSystemMetrics(SM_SERVERR2)==0)
osname = PRG_WINTYPE::PG_WIN2003;//"Microsoft Windows Server 2003";//2003年3月发布
else if(GetSystemMetrics(SM_SERVERR2)!=0)
osname = PRG_WINTYPE::PG_WIN2003R2;//"Microsoft Windows Server 2003 R2";
break;
}
break;

case 6:
switch(os.dwMinorVersion)
{
case 0:
if(os.wProductType == VER_NT_WORKSTATION)
osname = PRG_WINTYPE::PG_WINVISTA;//"Microsoft Windows Vista";
else
osname = PRG_WINTYPE::PG_WIN2008;//"Microsoft Windows Server 2008";//服务器版本
break;
case 1:
if(os.wProductType == VER_NT_WORKSTATION)
osname = PRG_WINTYPE::PG_WIN7;//"Microsoft Windows 7";
else
osname = PRG_WINTYPE::PG_WIN2008R2;//"Microsoft Windows Server 2008 R2";
break;
case 2:
if(os.wProductType == VER_NT_WORKSTATION)
osname = PRG_WINTYPE::PG_WIN8;//"Microsoft Windows 8";
else
osname = PRG_WINTYPE::PG_WIN2012;//"Microsoft Windows Server 2012";
break;
case 3:
if(os.wProductType == VER_NT_WORKSTATION)
osname = PRG_WINTYPE::PG_WIN81;//"Microsoft Windows 8.1";
else
osname = PRG_WINTYPE::PG_WIN2012R2;//"Microsoft Windows Server 2012 R2";
break;
}
break;

case 10:
switch(os.dwMinorVersion)
{
case 0:
if(os.wProductType == VER_NT_WORKSTATION)
osname = PRG_WINTYPE::PG_WIN10;//"Microsoft Windows 10";
else
osname = PRG_WINTYPE::PG_WIN2016;//"Microsoft Windows Server 2016 Technical Preview";//服务器版本
break;
}
break;
}
}//if(GetVersionEx((OSVERSIONINFO *)&os))
//https://msdn.microsoft.com/en-us/library/ms724832.aspx
return osname;
}

在win7之后开启进程方法:

int create_processs( const std::wstring& wstrpath)
{

HANDLE hTokenThis = NULL;
HANDLE hTokenDup = NULL;
HANDLE hThisProcess = GetCurrentProcess();
BOOL bResult = FALSE;
bResult = OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);
if(!bResult)
{
ProcessGroupLog().WriteError(L"OpenProcessToken Failed! Error = 0x%08lx\n", GetLastError());
return -1;
}

bResult = DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
if(!bResult)
{
ProcessGroupLog().WriteError(L"DuplicateTokenEx Failed! Error = 0x%08lx\n", GetLastError());
return -1;
}

DWORD dwSessionId = WTSGetActiveConsoleSessionId();
bResult = SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));
if(!bResult)
{
ProcessGroupLog().WriteError(L"SetTokenInformation Failed! Error = 0x%08lx\n", GetLastError());
return -1;
}

STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = L"WinSta0\\Default";

LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE| CREATE_UNICODE_ENVIRONMENT;

bResult = CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE);
if(!bResult)
{
ProcessGroupLog().WriteError(L"CreateEnvironmentBlock Failed! Error = 0x%08lx\n", GetLastError());
return -1;
}

bResult = CreateProcessAsUserW(hTokenDup, NULL, (TCHAR*)wstrpath.c_str(), NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);
if(!bResult)
{
ProcessGroupLog().WriteError(L"%s 启动:%s 失败,请检查配置文件中路径是否正确,laserror:%X",
strfunc::StrA2W(__FUNCTION__).c_str(),
wstrpath.c_str(),
::GetLastError());
return -1;
}

if(pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
if(hTokenDup != NULL && hTokenDup != INVALID_HANDLE_VALUE)
CloseHandle(hTokenDup);
if(hTokenThis != NULL && hTokenThis != INVALID_HANDLE_VALUE)
CloseHandle(hTokenThis);
return 0;
}


xp/service2003中开启进程方式:

int create_processs2003( const std::wstring& wstrprocess)
{
STARTUPINFO si;    PROCESS_INFORMATION pi; //进程信息 memset(&si, 0, sizeof(si));  si.cb = sizeof(si);  memset(&pi, 0, sizeof(pi));  if(!CreateProcessW( NULL, (LPWSTR)wstrprocess.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ProcessGroupLog().WriteError(L"%s 启动进程%s 失败错误码:%X",strfunc::StrA2W(__FUNCTION__).c_str(),wstrprocess.c_str(),::GetLastError());
continue;}
}