在做应用开发时,通常需要同时启动多个服务,例如,做PHP开发时,需要同时启动Nginx、mysql、php-fpm,Nodejs等多个服务。由于这些服务程序都是原始基于Unix/Linux系统开发的,在Windows下,这些从Unix/Linux移植过来的程序通常就是Windows下的控制台程序,例如Nginx,如果将它们放在windows下的bat(批处理)文件中启动,该启动命令行通常会阻塞批处理文件的执行,导致批处理文件不能同时启动多个控制台服务程序,必须等待当前控制台服务程序退出后才能执行下一条启动语句,这个非常不方便,因为按照惯例,我们不希望为每个服务进程都开启一个控制台终端,这样不好管理。
笔者由于工作的实际需要,编写了一个小的助手程序来解决这个问题。该程序的名称为RunHiddenConsole。
RunHiddenConsole的官方下载地址为(不能直接点击如下链接,需要复制链接到地址栏):
https://github.com/wenshui2008/RunHiddenConsole
RunHiddenConsole 解决了如下问题:
1、将Windows控制台程序后台化(类似于Linux的deamon进程)
2、解决单个bat文件同时启动众多控制台服务的问题
3、解决控制台服务优雅退出的问题
RunHiddenConsole 的命令行阐述如下:
RunHiddenConsole Usage:
RunHiddenConsole.exe [/l] [/w] [/r] [/n name] [/k name] [/o output-file] [/p pidfile] commandline
For example:
RunHiddenConsole.exe /l /r e:\WNMP\PHP\php-cgi.exe -b 127.0.0.1:9000 -c e:\WNMP\php\php.ini
RunHiddenConsole.exe /l /r E:/WNMP/nginx/nginx.exe -p E:/WNMP/nginx
The /l is optional, printing the result of process startup
The /w is optional, waiting for termination of the process
The /o is optional, redirecting the output of the program to a file
The /p is optional, saving the process id to a file
The /r is optional, supervise the child process, if the child process exits, restart the child process
The /n is optional, naming control signals
The /k is optional, kill the daemon according to the specified control signal
参数 | 用途 | 备注 |
/l | 输出控制台服务进程启动的结果 | 可选 |
/w | 等待控制台服务进程退出后再退出,如果控制台服务进程不退出,则RunHiddenConsole自己也不会退出 | 可选 |
/o | 将控制台服务进程的stdout,stderr输出到一个硬盘文件,需要带一个硬盘文件路径 | 可选 |
/p | 保存制台服务进程的进程id(pid)到一个文本文件,类似于nginx的pid文件 | 可选 |
/r | 监控控制台服务进程,如果控制台服务进程退出,则立即重启该进程。这个选项解决了phpfpm进程服务一定数量的请求后就自行退出的问题。还有某些有Bug的服务程序运行一段时间后也许会异常崩溃,该选项也能帮助立即重启。参见(备注2) | 可选 |
/n | 给定一个控制信号名,用于后续批量退出 | 可选 |
/k | 这个开关用于优雅退出一个给定信号名的控制台服务进程 | 可选 |
备注:控制台服务进程指的是类似nginx、phpfpm、nodejs等进程。
备注2:很多程序运行一段时间后就需要重启一下,例如用java或javascript开发的程序,如果长期运转,由于缺乏有效的内存回收机制,可能会导致java或node进程占用的内存开销很大,这个时候,可以让进程定时自动退出,然后重启来解决。phpfpm 这类程序,由于使用了大量的第三方库,为了避免这些第三方库中可能存在的内存泄漏的影响累加,就设置了服务一定数量请求后自动退出,然后再启动一个新进程继续服务的解决办法。
示例:
一个同时启动两个phpfpm与一个nginx的bat文件如下(startweb.bat):
RunHiddenConsole.exe /l /r /n phpfpm-8902 E:/WebWorkroom/php-7.4.12-nts/php-cgi.exe -b 127.0.0.1:8902 -c E:/WebWorkroom/php-7.4.12-nts/php.ini
RunHiddenConsole.exe /l /r /n phpfpm-8904 E:/WebWorkroom/php-7.4.12-nts/php-cgi.exe -b 127.0.0.1:8904 -c E:/WebWorkroom/php-7.4.12-nts/php.ini
RunHiddenConsole.exe /l /r /n nginx-blog E:/WebWorkroom/nginx/nginx.exe -p E:/WebWorkroom/nginx
一个批量关闭nginx与phpfpm的bat文件内容如下(stopweb.bat):
RunHiddenConsole.exe /k /n phpfpm-8902
RunHiddenConsole.exe /k /n phpfpm-8904
RunHiddenConsole.exe /k /n nginx-blog
基于以上启动脚本,用下面的Nginx配置启动一个多站点的Nginx,每个站点对应一个不同端口的phpfpm服务;如果需要频繁的处理php请求,建议用nginx的 upstream fastcgi_proxy 指令配置众多的phpfpm服务端口,同时用RunHiddenConsole.exe 启动多个不同端口的phpfpm进程:
server {
listen 8982;
#listen somename:8080;
server_name www.zzzzzz.com;
location / {
root d:/xxxxxx/public;
index index.php index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
location ~ \.php$ {
root d:/xxxxxx/public;
fastcgi_pass 127.0.0.1:8902;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
server {
listen 8984;
#listen somename:8080;
server_name www.xxxxxxx.com;
location / {
root d:/xxxxxx/public;
index index.php index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
location ~ \.php$ {
root d:/xxxxxx/public;
fastcgi_pass 127.0.0.1:8904;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
RunHiddenConsole.exe 实现文件的C语言源代码如下:
//RunHiddenConsole.cpp
//copyright http://www.iavcast.com
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <io.h>
#include <locale.h>
HANDLE g_hStdin_Child_Rd = NULL;
HANDLE g_hStdin_Parent_Wr = NULL;
HANDLE g_hStdout_Parent_Rd = NULL;
HANDLE g_hStdout_Child_Wr = NULL;
#define MAX_FILEPATH 4096
TCHAR g_szMyPath[MAX_FILEPATH] = TEXT("");
size_t g_iMyPathLen = 0;
#define ErrorExit(s) do { printf(s); printf("\r\n"); return 0; } while(0)
#define CLOSEHANDLE(h) if(h) { CloseHandle(h); h = NULL; }
#define CloseAllPipes() do { CLOSEHANDLE(g_hStdin_Parent_Wr); CLOSEHANDLE(g_hStdout_Parent_Rd); } while(0)
#define MAX_COMMAND_LINE 65536
#define DEFAULT_CONSOLE_COLOR ( FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
static BOOL CreateStdioPipe()
{
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CloseAllPipes();
// Create a pipe for reading the child process's STDOUT.
if (! CreatePipe(&g_hStdout_Parent_Rd, &g_hStdout_Child_Wr, &saAttr, 0))
ErrorExit("Stdout pipe creation failed\n");
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (! SetHandleInformation(g_hStdout_Parent_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit("Stdout SetHandleInformation");
// Create a pipe for writing to the child process's STDIN.
if (! CreatePipe(&g_hStdin_Child_Rd, &g_hStdin_Parent_Wr, &saAttr, 0))
ErrorExit("Stdin CreatePipe");
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hStdin_Parent_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit("Stdin SetHandleInformation");
return 1;
}
//
//
// Remove ..\ from path
// ie: c:\abc\bcd\cde\efg\..\..\..\fgh\ghi.ext => c:\abc\fgh\ghi.ext
// nPathLength - the length of return string
// the return string is allocated by malloc
static LPTSTR FixRelativePath(LPCTSTR pszPath, int &nOutputLength)
{
LPCTSTR pszPathEnd,pszNameBegin,pszNameEnd;
LPTSTR pszReturn = NULL;
int nPathLength;
int nFileNameLength ;
pszPathEnd = _tcsstr(pszPath,_TEXT("..\\"));
if (!pszPathEnd) {
pszReturn = _tcsdup(pszPath);
nOutputLength = (int)_tcslen(pszReturn);
return pszReturn;
}
pszNameBegin = pszPathEnd;
pszPathEnd --;
nPathLength = (int) (pszPathEnd - pszPath + 1);
nFileNameLength = (int)_tcslen(pszNameBegin);
pszNameEnd = pszNameBegin + nFileNameLength;
while(pszNameBegin < pszNameEnd) {
//check "..\"
if (*pszNameBegin != _T('.') ||
*(pszNameBegin + 1) != _T('.') ||
*(pszNameBegin + 2) != _T('\\'))
break;
pszNameBegin += 3;
pszPathEnd --;
while(pszPathEnd > pszPath) {
if (*pszPathEnd == _T('\\'))
break;
pszPathEnd --;
}
}
if (pszPathEnd > pszPath && pszNameBegin <= pszNameEnd) {
nPathLength = int(pszPathEnd - pszPath + 1);
nFileNameLength = int(pszNameEnd - pszNameBegin + 1);
pszReturn = (TCHAR*) malloc( sizeof (TCHAR) * (nPathLength + 1 + nFileNameLength + 1));
if (!pszReturn) return NULL;
memcpy(pszReturn,pszPath, sizeof (TCHAR) * nPathLength);
if (pszReturn[nPathLength - 1] != _T('\\')) {
pszReturn[nPathLength] = _T('\\');
nPathLength ++;
}
memcpy(pszReturn + nPathLength,pszNameBegin, sizeof (TCHAR) * nFileNameLength);
nOutputLength = nPathLength + nFileNameLength;
pszReturn[nOutputLength] = _T('\0');
}
return pszReturn;
}
static HANDLE process_job_handle = NULL;
int CreateChildProcessJob()
{
process_job_handle = CreateJobObject(NULL, NULL);
if (process_job_handle) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
BOOL set_auto_kill_ok;
memset(&limit_info, 0x0, sizeof(limit_info));
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
set_auto_kill_ok = SetInformationJobObject(
process_job_handle,
JobObjectExtendedLimitInformation,
&limit_info,
sizeof(limit_info)
);
if (!set_auto_kill_ok) {
CloseHandle(process_job_handle);
process_job_handle = NULL;
return -1;
}
}
return 0;
}
static
void DestroyChildProcessJob()
{
if (process_job_handle) {
CloseHandle(process_job_handle);
process_job_handle = NULL;
}
}
//
//
// return 0: no error
// otherwise: error code
//
static
BOOL CreateChildProcess(HANDLE *pChildHandle, DWORD *pid, BOOL bPrintLog, LPTSTR pszCommandLine, LPCTSTR pszOutputFile, LPCTSTR pszCurrentDirectory)
{
BOOL bReturn;
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPTSTR pszEvnVar;
if (!CreateStdioPipe()) {
return FALSE;
}
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
pszEvnVar = (LPTSTR)GetEnvironmentStrings();
HANDLE hFileStdOut = g_hStdout_Child_Wr;
if (pszOutputFile) {
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
hFileStdOut = CreateFile(pszOutputFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, &sa, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
if (hFileStdOut == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("Create output file %s failed\n"),pszOutputFile);
return FALSE;
}
}
si.cb = sizeof(STARTUPINFO);
si.hStdError = hFileStdOut;
si.hStdOutput = hFileStdOut;
si.hStdInput = g_hStdin_Child_Rd;
si.dwFlags = STARTF_USESTDHANDLES;
if (bPrintLog) {
_tprintf(TEXT("Starting %s"),pszCommandLine);
}
bReturn = CreateProcess(NULL, pszCommandLine, NULL, NULL, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, pszEvnVar, pszCurrentDirectory, &si, &pi);
FreeEnvironmentStrings(pszEvnVar);
// Close handles to the stdin and stdout pipes no longer needed by the child process.
CloseHandle(g_hStdout_Child_Wr);
CloseHandle(g_hStdin_Child_Rd);
CloseHandle(pi.hThread);
if (hFileStdOut != g_hStdout_Child_Wr)
CloseHandle(hFileStdOut);
if (process_job_handle) {
BOOL b = AssignProcessToJobObject(process_job_handle, pi.hProcess);
if (!b) {
_tprintf(TEXT(" AssignProcessToJobObject Failed!"));
}
}
*pChildHandle = pi.hProcess;
*pid = pi.dwProcessId;
return TRUE;
}
static
void SavePidToFile(LPCTSTR pszPidFile, DWORD dwPid)
{
if (pszPidFile) {
HANDLE hFilePid = CreateFile(pszPidFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFilePid != INVALID_HANDLE_VALUE) {
char szBuff[64];
//DWORD dwPid = GetProcessId(pi.hProcess);
DWORD dwWrite = 0;
int len = sprintf_s(szBuff, ARRAYSIZE(szBuff), "%u", dwPid);
WriteFile(hFilePid, szBuff, len, &dwWrite, NULL);
CloseHandle(hFilePid);
}
}
}
static
BOOL Fork()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPTSTR pszEvnVar;
BOOL bReturn;
HANDLE hStdOutRead, hStdoutWrite;
SECURITY_ATTRIBUTES saAttr;
LPTSTR pszCommandLine = GetCommandLine();
LPTSTR pszPos = _tcsstr(pszCommandLine, TEXT("/r"));
if (!pszPos) _tcsstr(pszCommandLine, TEXT("-r"));
if (!pszPos)
return FALSE;
pszPos[1] = 'R';
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (CreatePipe(&hStdOutRead, &hStdoutWrite, &saAttr, 256)) {
SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0);
}
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.hStdError = hStdoutWrite;
si.hStdOutput = hStdoutWrite;
si.hStdInput = NULL;
si.dwFlags = STARTF_USESTDHANDLES;
pszEvnVar = (LPTSTR)GetEnvironmentStrings();
bReturn = CreateProcess(NULL, pszCommandLine, NULL, NULL, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, pszEvnVar, NULL, &si, &pi);
if (bReturn) {
#define BUFSIZE 4096
BYTE chBuf[BUFSIZE];
BOOL bSuccess;
DWORD dwRead = 0, dwAvail = 0;
DWORD dwWait = WaitForSingleObject(pi.hProcess, 200);
if (dwWait == 0)
bReturn = FALSE;
if (PeekNamedPipe(hStdOutRead, NULL, NULL, &dwRead, &dwAvail, NULL)) {
if ( dwAvail > 0) {
if (dwAvail > BUFSIZE)
dwAvail = BUFSIZE;
bSuccess = ReadFile( hStdOutRead, chBuf, dwAvail, &dwRead, NULL);
if (bSuccess) {
DWORD dwWritten;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(hStdOut, chBuf, dwRead, &dwWritten, NULL);
}
}
}
}
FreeEnvironmentStrings(pszEvnVar);
CloseHandle(hStdoutWrite);
CloseHandle(hStdOutRead);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return bReturn;
}
void Usage() {
printf("RunHiddenConsole Usage:\n"
"RunHiddenConsole.exe [/l] [/w] [/r] [/n name] [/k name] [/o output-file] [/p pidfile] commandline\n"
"For example:\n"
"RunHiddenConsole.exe /l /r e:\\WNMP\\PHP\\php-cgi.exe -b 127.0.0.1:9000 -c e:\\WNMP\\php\\php.ini\n"
"RunHiddenConsole.exe /l /r E:/WNMP/nginx/nginx.exe -p E:/WNMP/nginx\n"
"The /l is optional, printing the result of process startup\n"
"The /w is optional, waiting for termination of the process\n"
"The /o is optional, redirecting the output of the program to a file\n"
"The /p is optional, saving the process id to a file\n"
"The /r is optional, supervise the child process, if the child process exits, restart the child process\n"
"The /n is optional, naming control signals\n"
"The /k is optional, kill the daemon according to the specified control signal\n");
}
int _tmain(int nArgc, _TCHAR ** ppArgv)
{
TCHAR * pch;
TCHAR * pszExePath, szExePath[MAX_FILEPATH], szCurrentDirectory[MAX_FILEPATH];
TCHAR * pszCommandLine = NULL, *pszOutputFile = NULL, *pszPidFile = NULL, *pszSignalName = NULL;
BOOL bHasSpace;
BOOL bReturn;
BOOL bWaitExit = 0, bPrintLog = 0, bResume = 0, bFork = 0, bKill = 0;
int iCmdLinePos = 1, i;
HANDLE hStdOut;
HANDLE hChildProcess = NULL;
DWORD dwChildPid = 0;
HANDLE hEventExit = NULL;
GetModuleFileName(NULL,g_szMyPath, ARRAYSIZE(g_szMyPath));
if (_tcsstr(g_szMyPath,TEXT("..\\"))) {
int nTextLength = (int)_tcslen(g_szMyPath);
LPTSTR pszDir = FixRelativePath(g_szMyPath, nTextLength);
if (pszDir) {
_tcscpy(g_szMyPath, pszDir);
free(pszDir);
}
}
pch = _tcsrchr(g_szMyPath,'\\');
if (pch) {
pch ++;
*pch = 0;
}
else {
//Never arrived here!
return -2;
}
g_iMyPathLen = pch - g_szMyPath;
if (nArgc < 2) {
Usage();
//
return -1;
}
for (i=1;i <nArgc;i++){
if ( (ppArgv[i][0] == '-') || (ppArgv[i][0] == '/') ) {
if (_tcslen(ppArgv[i]) == 2) {
switch (tolower(ppArgv[i][1])){
case 'l':
bPrintLog = 1;
break;
case 'w':
bWaitExit = 1;
break;
case 'o':
if (i <nArgc -1) {
i++;
iCmdLinePos ++;
pszOutputFile = ppArgv[i];
}
else {
_tprintf(TEXT("No output file!\n"));
Usage();
return -1;
}
break;
case 'p':
if (i <nArgc -1) {
i++;
iCmdLinePos ++;
pszPidFile = ppArgv[i];
}
else {
_tprintf(TEXT("No process id file!\n"));
Usage();
return -1;
}
break;
case 'n':
if (i <nArgc -1) {
i++;
iCmdLinePos ++;
pszSignalName = ppArgv[i];
if (_tcslen(pszSignalName) > 32) {
_tprintf(TEXT("The signal name is too long, it should be less than 32 characters!\n"));
Usage();
return -1;
}
}
else {
_tprintf(TEXT("No signal name specified!\n"));
Usage();
return -1;
}
break;
case 'r':
bFork = 1; // fork a child process as deamon
break;
case 'R':
bResume = 1;
break;
case 'k':
bKill = 1;
break;
}
}
iCmdLinePos ++;
}
else {
break;
}
}
if (bKill) {
TCHAR szSignalName[64];
if (!pszSignalName) {
printf("Kill without signal name.\n");
return -1;
}
_stprintf_s(szSignalName, ARRAYSIZE(szSignalName), TEXT("Global\\rhc_exit_%s"), pszSignalName);
hEventExit = OpenEvent(EVENT_MODIFY_STATE, FALSE, szSignalName);
if (hEventExit) {
SetEvent(hEventExit);
}
return 0;
}
if (iCmdLinePos >= nArgc) {
return -1;
}
if (bResume && pszSignalName) {
TCHAR szSignalName[64];
_stprintf_s(szSignalName, ARRAYSIZE(szSignalName), TEXT("Global\\rhc_exit_%s"), pszSignalName);
hEventExit = OpenEvent(0, FALSE, szSignalName);
if (hEventExit) {
_tprintf(TEXT("Process with signal name %s is already existed.\n"), pszSignalName);
CloseHandle(hEventExit);
return 0;
}
}
if (bFork) {
if (Fork())
return 0;
bResume = 1;
}
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetACP() == 936) {
setlocale(LC_ALL, "chs");
}
pszExePath = ppArgv[iCmdLinePos];
if (pszExePath[1] != ':') {
_tcscpy(szExePath,g_szMyPath);
_tcscpy(&szExePath[g_iMyPathLen],pszExePath);
}
else {
_tcscpy(szExePath,pszExePath);
}
pszExePath = szExePath;
pch = pszExePath;
while(*pch) {
if (*pch == '/') {
*pch = '\\';
}
pch ++;
}
_tcscpy(szCurrentDirectory,pszExePath);
pch = _tcsrchr(szCurrentDirectory,'\\');
*pch = 0;
pszCommandLine = (TCHAR *)malloc(sizeof (TCHAR) * MAX_COMMAND_LINE);
if (!pszCommandLine) {
return -3;
}
pch = pszCommandLine;
bHasSpace = _tcschr(pszExePath, ' ') != NULL;
if (bHasSpace) *pch++ = '\"';
_tcscpy(pch,pszExePath);
pch += _tcslen(pszExePath);
if (bHasSpace) *pch++ = '\"';
for (i = iCmdLinePos + 1; i < nArgc; i ++) {
_TCHAR * argv = ppArgv[i];
*pch++ = ' ';
bHasSpace = _tcschr(argv, ' ') != NULL;
if (bHasSpace) *pch++ = '\"';
_tcscpy(pch, argv);
pch += _tcslen(argv);
if (bHasSpace) *pch++ = '\"';
}
*pch = 0;
if (bResume) {
CreateChildProcessJob();
}
bReturn = CreateChildProcess(&hChildProcess, &dwChildPid, bPrintLog, pszCommandLine, pszOutputFile, szCurrentDirectory);
if (!bReturn) {
DWORD dwError = GetLastError();
if (bPrintLog) {
SetConsoleTextAttribute(hStdOut, FOREGROUND_RED );
_tprintf(TEXT(" Failed!"), dwError);
SetConsoleTextAttribute(hStdOut, DEFAULT_CONSOLE_COLOR );
_tprintf(TEXT(",Error Code:%u\n"), dwError);
}
return -5;
}
if (bPrintLog) {
SetConsoleTextAttribute(hStdOut, FOREGROUND_GREEN );
_tprintf(TEXT(" Success!\n"));
SetConsoleTextAttribute(hStdOut, DEFAULT_CONSOLE_COLOR);
}
if (bPrintLog) {
fflush(stdout);
}
SavePidToFile(pszPidFile, dwChildPid);
if (bWaitExit || bResume) {
if (pszSignalName) {
TCHAR szSignalName[64];
_stprintf_s(szSignalName, ARRAYSIZE(szSignalName), TEXT("Global\\rhc_exit_%s"), pszSignalName);
hEventExit = CreateEvent(NULL, TRUE, FALSE, szSignalName);
}
// Supervise the child process, if the child process exits, restart the child process
while(TRUE) {
HANDLE handles[2];
DWORD dwWait, nCount = 1;
handles[0] = hChildProcess;
if (hEventExit) {
handles[1] = hEventExit;
nCount++;
}
dwWait = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
if (dwWait == WAIT_OBJECT_0) {
CloseHandle(hChildProcess);
if (!bResume)
break;
bReturn = CreateChildProcess(&hChildProcess, &dwChildPid, FALSE, pszCommandLine, pszOutputFile, szCurrentDirectory);
if (bReturn)
SavePidToFile(pszPidFile, dwChildPid);
else
break;
}
else if (dwWait == WAIT_OBJECT_0 + 1) {
// Received exit signal
break;
}
else {
// System error
break;
}
}
}
free(pszCommandLine);
if (hEventExit) {
CloseHandle(hEventExit);
}
DestroyChildProcessJob();
return 0;
}
最后:如果同学们恰恰需要这个功能,请从如下位置下载(请复制URL):
https://github.com/wenshui2008/RunHiddenConsole
或者在 github上搜索 RunHiddenConsole
已经用Visual C++ 2010 同时编译成 x86与 x64版本,如果不想自己编译,直接用编译好的就可。