Windows下获取子进程标准输出

需求来源:项目上有用到需要获取ffmpeg的命令输出,进行解析,而后添加成进度条的形式表现出来

因此写了个获取子进程输出的通用工具代码

实现原理:

  1. 创建可继承的匿名管道
  2. 将其中的写管道,作为createprocss的参数传入,替换子进程的标准输出
  3. 调用readfile从读管道进行循环读取内容

代码如下:

#include 
#include 
#include 

typedef void (* FPTGETPROCESSOUT)(_In_ const char* szLineData, _In_ VOID *lpUser);

BOOL CreateProcessAndOutput(_In_ LPCTSTR szCommandLine, _In_ FPTGETPROCESSOUT CallBackFun, _In_ VOID *lpUser)
{
	if (NULL == szCommandLine)
	{
		return FALSE;
	}

	BOOL bError = FALSE;
	errno_t ErrorCode = 0;

	LPTSTR szProcessCommand = NULL;
	HANDLE hPipRead = NULL;
	HANDLE hPipWrite = NULL;

	SECURITY_ATTRIBUTES sa = { 0 };

	STARTUPINFO Si = { 0 };
	PROCESS_INFORMATION pi = { 0 };

	char szRecvData[512] = { 0 };
	DWORD dwRecvSize = 0;
	std::string szOutputLine;
	
	
	szProcessCommand = new TCHAR[_tcslen(szCommandLine) + 1];
	ErrorCode = _tcscpy_s(szProcessCommand, _tcslen(szCommandLine) + 1, szCommandLine);
	if (0 != ErrorCode)
	{
		goto FUN_CLEANUP;
	}

	sa.nLength = sizeof(sa);
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;

	bError = CreatePipe(&hPipRead, &hPipWrite, &sa, 0);
	if (FALSE == bError || NULL == hPipRead || NULL == hPipWrite)
	{
		goto FUN_CLEANUP;
	}

	Si.cb = sizeof(Si);
	Si.hStdOutput = hPipWrite;
	Si.hStdError = hPipWrite;
	Si.wShowWindow = SW_HIDE;
	Si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;

	bError = CreateProcess(NULL, szProcessCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &Si, &pi);
	if (FALSE == bError)
	{
		goto FUN_CLEANUP;
	}
	
	if (NULL != hPipWrite)
	{
		CloseHandle(hPipWrite);
		hPipWrite = NULL;
	}
	
	while (FALSE != ReadFile(hPipRead, szRecvData, _countof(szRecvData) -1, &dwRecvSize, NULL))
	{
		szRecvData[dwRecvSize] = '\0';

		char* szLinesBegin = szRecvData;
		char* szLinesEnd = NULL;
		
		while (true)
		{
			char* szFound = szLinesBegin + strcspn(szLinesBegin, "\r\n");
			szLinesEnd = *szFound != '\0' ? szFound : NULL;

			if (NULL == szLinesEnd)
			{
				szOutputLine += szLinesBegin;
				break;
			}

			*szLinesEnd = '\0';

			if (false == szOutputLine.empty())
			{
				szOutputLine += szLinesBegin;
				CallBackFun(szOutputLine.c_str());
				szOutputLine.clear();
			}
			else
			{
				if (*szLinesBegin != '\0')
				{
					CallBackFun(szLinesBegin);
				}
				
			}
			
			szLinesBegin = szLinesEnd + 1;
		}

	}

	WaitForSingleObject(pi.hProcess, INFINITE);
	bError = TRUE;

FUN_CLEANUP:
	if (NULL != hPipRead)
	{
		CloseHandle(hPipRead);
		hPipRead = NULL;
	}

	if (NULL != hPipWrite)
	{
		CloseHandle(hPipWrite);
		hPipWrite = NULL;
	}

	if (NULL != szProcessCommand)
	{
		delete[] szProcessCommand;
		szProcessCommand = NULL;
	}

	if (NULL != pi.hProcess)
	{
		CloseHandle(pi.hProcess);
		pi.hProcess = NULL;
	}

	if (NULL != pi.hThread)
	{
		CloseHandle(pi.hThread);
		pi.hThread = NULL;
	}

	return bError;
}

使用方法:使用回调的形式,向回调函数返回出每一行的输出内容

1. 先定义一个回调函数,原型如下 (lpUser为用户自定义指针,用于传递额外的参数)

void CallBackGetOutput(const char * szLineData, VOID *lpUser)
{
	DWORD* Num = (DWORD*)lpUser;
	printf("[line:%02d] %s\n", (*Num)++, szLineData);
}

2. 调用CreateProcessAndOutput,传递要调用的子进程命令行和回调函数地址

CreateProcessAndOutput(_T("cmd.exe /c dir d:"), CallBackGetOutput);

结果如下

Windows下获取子进程标准输出_第1张图片

你可能感兴趣的:(Windows,编程,工具)