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

本文记录了一个老数据转发软件的守护进程实现,选择以Windows服务形式运行,以应对进程挂掉时自动重启。在Windows 7及以后版本中,由于Session 0隔离,服务无法直接启动用户进程,需使用CreateProcessAsUserW。而在Windows Server 2003和XP中,需处理CreateProcessAsUserW不支持的问题,通过版本判断来适配不同系统的启动方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

开始时候一直纠结是做成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;
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值