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.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #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.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    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.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    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.  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    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.  

    1
    2
    3
    4
    5
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    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