XiZi's Blog

这里记录一个用CreatePipe的坑

1. 问题来源:在Windows上编译了一个cgit, 加了source-filter,代码高亮(使用highlight处理)之后,死活不显示代码。发现是execvp的问题,开始想好好弄,后来放弃了,反正只加个代码高亮,直接dirtry的解决方案算了。所以就参照微软的官方<Creating a Child Process with Redirected Input and Output>, 进行highlight进程的创建。测试小文件可以正常高亮,大文件就会卡住。

2.问题所在和解决方案:经过各种查找,定位到了WriteFile给子线程的Stdin pipe时发生了阻塞。查了查,发现CreatePipes时,有buffersize的设置,增大buffersize, 问题得到了解决。

 

贴出Dirty的核心代码吧:

  1.  

    #define _WIN32_WINNT 0x0600
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <winnt.h>
    #include <winternl.h>
    #include <stdio.h>
    #include <errno.h>
    #include <assert.h>
    #include <process.h>
     
    typedef struct ProcessPrepare_Info
    {
        PROCESS_INFORMATION child_pi;
        HANDLE IN_Rd;
        HANDLE IN_Wr;
        HANDLE OUT_Rd;
        HANDLE OUT_Wr;  
    } PPInfo;
     
    #define BUFSIZE 4096 
    #define PIPE_BUFSIZE 1024*1024*1024
  2.  

    PPInfo prepare_highlight(const char* filename)
    {
    	SECURITY_ATTRIBUTES saAttr;
    	char file_ext[1024];
    	char *fext;
    	PPInfo ppinfo;
    	
    	PROCESS_INFORMATION child_pi;
    	
    	HANDLE g_hChildStd_IN_Rd = NULL;
    	HANDLE g_hChildStd_IN_Wr = NULL;
    	HANDLE g_hChildStd_OUT_Rd = NULL;
    	HANDLE g_hChildStd_OUT_Wr = NULL;
    
    	
    	fext = get_filename_ext(filename);
    	if (!fext)
    		strcpy(file_ext,"txt");
    	else
    		strcpy(file_ext,fext);
    	
    	if (strstr(filename,"Makefile") == filename)
    		strcpy(file_ext,"mk");
    	
    
    	// printf("\n->Start of parent execution.\n");
    
    	// Set the bInheritHandle flag so pipe handles are inherited. 
    
    	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    	saAttr.bInheritHandle = TRUE;
    	saAttr.lpSecurityDescriptor = NULL;
    
    	// Create a pipe for the child process's STDOUT. 
    
    	if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, PIPE_BUFSIZE))
    		debug_log(TEXT("StdoutRd CreatePipe"));
    
    	// Ensure the read handle to the pipe for STDOUT is not inherited.
    
    	if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
    		debug_log(TEXT("Stdout SetHandleInformation"));
    
    	// Create a pipe for the child process's STDIN. 
    
    	if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, PIPE_BUFSIZE))
    		debug_log(TEXT("Stdin CreatePipe"));
    
    	// Ensure the write handle to the pipe for STDIN is not inherited. 
    
    	if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
    		debug_log(TEXT("Stdin SetHandleInformation"));
    
    	// Create the child process. 
    
    	child_pi = CreateChildProcess(file_ext,g_hChildStd_OUT_Wr,g_hChildStd_IN_Rd);
    	
    	debug_log("Create highlight process finished.\n");
    	
    	ppinfo.child_pi = child_pi;
    	ppinfo.IN_Rd = g_hChildStd_IN_Rd;
    	ppinfo.IN_Wr = g_hChildStd_IN_Wr;
    	ppinfo.OUT_Rd = g_hChildStd_OUT_Rd;
    	ppinfo.OUT_Wr = g_hChildStd_OUT_Wr;
    	
    	return ppinfo;
    }

     

  3.  

    void wait_highlight(PPInfo ppinfo)
    {
    	//WaitForSingleObject(ppinfo.child_pi.hProcess, INFINITE);
    	WaitForSingleObject(ppinfo.child_pi.hProcess, 10000);
    	
    	if (!CloseHandle(ppinfo.OUT_Wr))
    		debug_log(TEXT("Child StdOutWr CloseHandle"));
    
    	// Read from pipe that is the standard output for child process. 
    
    	//printf("\n->Contents of child process STDOUT:\n\n", argv[1]);
    	ReadFromPipe(ppinfo.OUT_Rd);
    
    	//printf("\n->End of parent execution.\n");
    
    	// The remaining open handles are cleaned up when this process terminates. 
    	// To avoid resource leaks in a larger application, close handles explicitly. 
    }

     

  4.  

    DWORD WriteBufToPipe(HANDLE wHandle,const char* buf, DWORD bufsize)
    
    // Read from a file and write its contents to the pipe for the child's STDIN.
    // Stop when there is no more data. 
    {
    	int i, dwRead;
    	DWORD dwWritten;
    	CHAR chBuf[BUFSIZE+1];
    	BOOL bSuccess = FALSE;
    	
    	chBuf[BUFSIZE] = 0;
    #if false	
    	
    	bSuccess = WriteFile(wHandle, buf, bufsize, &dwWritten, NULL);
    	
    #else
    	i=0;
    	for (;;)
    	{
    		//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
    		//if (!bSuccess || dwRead == 0) break;
    		dwRead = (bufsize - BUFSIZE*i);
    		if ( dwRead <= 0) break;
    		dwRead = min(dwRead+1,BUFSIZE);
    		memcpy(chBuf,&(buf[BUFSIZE*i]),dwRead);
    		debug_log("Write %d bytes of chbuf %s to handle\n",dwRead,chBuf);
    		bSuccess = WriteFile(wHandle, chBuf, dwRead, &dwWritten, NULL);
    		debug_log("Write buf %d time,bSuccess=%d\n",i,bSuccess);
    		if (!bSuccess) break;
    		FlushFileBuffers(wHandle);
    		debug_log("Flush write handle.\n");
    		i=i+1;
    	}
    #endif
    	
    	debug_log("Write buf finished\n");
    
    	// Close the pipe handle so the child process stops reading. 
    
    	if (!CloseHandle(wHandle))
    		debug_log(TEXT("StdInWr CloseHandle"));
    	
    	return dwWritten;
    }

     

  5.  

    PROCESS_INFORMATION CreateChildProcess(const char* file_ext,HANDLE hOutput, HANDLE hInput)
    // Create a child process that uses the previously created pipes for STDIN and STDOUT.
    {
    	TCHAR szCmdline[1024] = TEXT("D:\\Data\\WebRoot\\cgit\\highlight-3.39-x64\\highlight.exe --force --inline-css -f -I -O xhtml -S ");
    	PROCESS_INFORMATION piProcInfo;
    	STARTUPINFO siStartInfo;
    	BOOL bSuccess = FALSE;
    	
    	//Append file extension
    	strcat(szCmdline,file_ext);
    	
    	debug_log("%s\n",szCmdline);
    
    	// Set up members of the PROCESS_INFORMATION structure. 
    
    	ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    
    	// Set up members of the STARTUPINFO structure. 
    	// This structure specifies the STDIN and STDOUT handles for redirection.
    
    	ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    	siStartInfo.cb = sizeof(STARTUPINFO);
    	siStartInfo.hStdError = hOutput;//g_hChildStd_OUT_Wr;
    	siStartInfo.hStdOutput = hOutput;//g_hChildStd_OUT_Wr;
    	siStartInfo.hStdInput = hInput;//g_hChildStd_IN_Rd;
    	siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
    
    	// Create the child process. 
    
    	bSuccess = CreateProcess(TEXT("D:\\Data\\WebRoot\\cgit\\highlight-3.39-x64\\highlight.exe"),
    		szCmdline,     // command line 
    		NULL,          // process security attributes 
    		NULL,          // primary thread security attributes 
    		TRUE,          // handles are inherited 
    		0,             // creation flags 
    		NULL,          // use parent's environment 
    		NULL,          // use parent's current directory 
    		&siStartInfo,  // STARTUPINFO pointer 
    		&piProcInfo);  // receives PROCESS_INFORMATION 
    
    	// If an error occurs, exit the application. 
    	if (!bSuccess)
    		debug_log(TEXT("CreateProcess Failed."));
    	else
    	{
    		// Close handles to the child process and its primary thread.
    		// Some applications might keep these handles to monitor the status
    		// of the child process, for example. 
    
    		CloseHandle(piProcInfo.hProcess);
    		CloseHandle(piProcInfo.hThread);
    	}
    	return piProcInfo;
    }

     

  6.  

    const char *get_filename_ext(const char *filename) {
        const char *dot = strrchr(filename, '.');
        if(!dot || dot == filename) return NULL;
        return dot + 1;
    }

     

  7. Test Main Code

    int test_process2(const char* filename,const char* buf, DWORD bufsize)
    {
    	PPInfo cIn;
    	
    	int i, dwRead;
    	DWORD dwWritten;
    	CHAR chBuf[BUFSIZE+1];
    	BOOL bSuccess = FALSE;
    	
    	chBuf[BUFSIZE] = 0;
    	
    	debug_log("test_process2 start.\n");
    	
    	cIn=prepare_highlight(filename);
    	
    	//WriteToPipe(cIn.IN_Wr,g_hInputFile);
    	i=0;
    	for (;;)
    	{
    		//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
    		//if (!bSuccess || dwRead == 0) break;
    		dwRead = (bufsize - BUFSIZE*i);
    		if ( dwRead <= 0) break;
    		dwRead = min(dwRead+1,BUFSIZE);
    		memcpy(chBuf,&(buf[BUFSIZE*i]),dwRead);
    		debug_log("Write %d bytes of chbuf %s to handle\n",dwRead,chBuf);
    		bSuccess = WriteFile(cIn.IN_Wr, chBuf, dwRead, &dwWritten, NULL);
    		debug_log("Write buf %d time\n",i);
    		if (!bSuccess) break;
    		i=i+1;
    	}
    	
    	debug_log("Write buf finished\n");
    
    	// Close the pipe handle so the child process stops reading. 
    
    	if (!CloseHandle(cIn.IN_Wr))
    		debug_log(TEXT("StdInWr CloseHandle"));
    	
    	wait_highlight(cIn);
    	
    	debug_log("test_process2 finished.\n");
    	
    	return 0;
    }

     

PS:

 

 这里在顺便提一下,如何在Windows下编译cgit, 下载并安装git-sdk,然后在打开的shell里面make即可。之后拷贝到Apache相关目录并设置即可。




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee