天天看點

Windows下擷取子程序标準輸出

需求來源:項目上有用到需要擷取ffmpeg的指令輸出,進行解析,而後添加成進度條的形式表現出來

是以寫了個擷取子程序輸出的通用工具代碼

實作原理:

  1. 建立可繼承的匿名管道
  2. 将其中的寫管道,作為

    createprocss

    的參數傳入,替換子程序的标準輸出
  3. 調用

    readfile

    從讀管道進行循環讀取内容

代碼如下:

#include <windows.h>
#include <tchar.h>
#include <string>

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,傳遞要調用的子程序指令行和回調函數位址

結果如下

Windows下擷取子程式标準輸出

繼續閱讀