2010-09-04 102 views
-1

當我使用CreateProcess創建進程adb.exe時,它將在ReadFile中阻塞。Android的adb啓動服務器的CreateProcess?

void KillAdbProcess() 
{ 
    DWORD aProcesses[1024], cbNeeded, cProcesses; 
    unsigned int i; 

    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) 
     return; 

    cProcesses = cbNeeded/sizeof(DWORD); 

    for (i = 0; i < cProcesses; i++) 
     if(aProcesses[i] != 0){ 
      bool shouldKill =false; 
      wchar_t szProcessName[MAX_PATH] = L"<unknown>"; 

       //Get a handle to the process. 
       HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | 
            PROCESS_VM_READ | PROCESS_TERMINATE, 
            FALSE, aProcesses[i]); 
       if (NULL != hProcess) 
       { 
        HMODULE hMod; 
        DWORD cbNeeded; 

        if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), 
         &cbNeeded)) 
        { 
         GetModuleFileNameExW(hProcess, hMod, szProcessName, 
              sizeof(szProcessName)/sizeof(TCHAR)); 
         int len = wcslen(szProcessName); 
         if(!wcscmp(L"\\adb.exe",szProcessName+len-8)){ 
          shouldKill = true; 
         } 

        } 
       } 

       if(shouldKill) TerminateProcess(hProcess,0); 
       CloseHandle(hProcess); 
     } 

} 

int testadb(){ 
    KillAdbProcess(); 
    char buff[4096] = {0}; 
    int len = sizeof(buff); 
    DWORD exitCode = 0; 

    SECURITY_ATTRIBUTES sa; 
    ZeroMemory(&sa, sizeof(sa)); 
    sa.bInheritHandle = TRUE; 
    sa.lpSecurityDescriptor = NULL; 
    sa.nLength = sizeof(sa); 

    HANDLE hOutputReadTmp,hOutputRead,hOutputWrite; 
    // Create the child output pipe. 
    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0)) 
     return false; 

    // Create new output read handle and the input write handles. Set 
    // the Properties to FALSE. Otherwise, the child inherits the 
    // properties and, as a result, non-closeable handles to the pipes 
    // are created. 
    if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp, 
     GetCurrentProcess(), 
     &hOutputRead, // Address of new handle. 
     0,FALSE, // Make it uninheritable. 
     DUPLICATE_SAME_ACCESS)) 
     return false; 

    // Close inheritable copies of the handles you do not want to be 
    // inherited. 
    if (!CloseHandle(hOutputReadTmp)) return false; 


    PROCESS_INFORMATION pi; 
    ZeroMemory(&pi, sizeof(pi)); 
    STARTUPINFOW si; 
    GetStartupInfoW(&si); 

    si.cb = sizeof(STARTUPINFO); 
    si.dwFlags = STARTF_USESTDHANDLES; 
    si.wShowWindow = SW_HIDE; 
    si.hStdInput = NULL; 
    if(buff) { 
     si.hStdOutput = hOutputWrite; 
     si.hStdError = hOutputWrite; 
    } else { 
     si.hStdOutput = NULL; 
     si.hStdError = NULL; 
    } 

    wchar_t cmdBuf[512] = L"adb.exe start-server"; 
    if(!::CreateProcessW(NULL, cmdBuf, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) 
    { 
     exitCode = -1; 
     goto exit; 
    } 

    ::CloseHandle(hOutputWrite); 
    hOutputWrite = NULL; 

    len--; //keep it for string end char. 
    DWORD dwBytes = 0; 
    DWORD dwHasRead = 0; 
    while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL)) 
    { 
     printf("read byte=%d\n",dwBytes); 
     if(0 == dwBytes) break; 
     dwHasRead += dwBytes; 
     //GetExitCodeProcess(pi.hProcess, &exitCode); 
     //if(STILL_ACTIVE != exitCode) break; 
     if(dwHasRead >= len) break; 
    } 
    buff[dwHasRead] = 0; 


    ::GetExitCodeProcess(pi.hProcess, &exitCode); 

exit: 


    if(hOutputRead) ::CloseHandle(hOutputRead); 
    if(hOutputWrite) ::CloseHandle(hOutputWrite); 

    ::CloseHandle(pi.hProcess); 
    ::CloseHandle(pi.hThread); 
    return 0; 
} 

如果我改變代碼

while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL)) 
{ 
    printf("read byte=%d\n",dwBytes); 
    if(0 == dwBytes) break; 
    dwHasRead += dwBytes; 
    GetExitCodeProcess(pi.hProcess, &exitCode); 
    if(STILL_ACTIVE != exitCode) break; 
    if(dwHasRead >= len) break; 
} 

它的工作原理,但是當我刪除printf的代碼,它將再次阻止。

while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL)) 
{ 
    if(0 == dwBytes) break; 
    dwHasRead += dwBytes; 
    GetExitCodeProcess(pi.hProcess, &exitCode); 
    if(STILL_ACTIVE != exitCode) break; 
    if(dwHasRead >= len) break; 
} 

在adb.exe的代碼,我看到像初級講座一些代碼:

#if ADB_HOST 
int launch_server() 
{ 
#ifdef HAVE_WIN32_PROC 
    /* we need to start the server in the background     */ 
    /* we create a PIPE that will be used to wait for the server's "OK" */ 
    /* message since the pipe handles must be inheritable, we use a  */ 
    /* security attribute            */ 
    HANDLE    pipe_read, pipe_write; 
    SECURITY_ATTRIBUTES sa; 
    STARTUPINFO   startup; 
    PROCESS_INFORMATION pinfo; 
    char     program_path[ MAX_PATH ]; 
    int     ret; 

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

    /* create pipe, and ensure its read handle isn't inheritable */ 
    ret = CreatePipe(&pipe_read, &pipe_write, &sa, 0); 
    if (!ret) { 
     fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError()); 
     return -1; 
    } 

    SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0); 

    ZeroMemory(&startup, sizeof(startup)); 
    startup.cb = sizeof(startup); 
    startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 
    startup.hStdOutput = pipe_write; 
    startup.hStdError = GetStdHandle(STD_ERROR_HANDLE); 
    startup.dwFlags = STARTF_USESTDHANDLES; 

    ZeroMemory(&pinfo, sizeof(pinfo)); 

    /* get path of current program */ 
    GetModuleFileName(NULL, program_path, sizeof(program_path)); 

    ret = CreateProcess(
      program_path,        /* program path */ 
      "adb fork-server server", 
            /* the fork-server argument will set the 
             debug = 2 in the child   */ 
      NULL,     /* process handle is not inheritable */ 
      NULL,     /* thread handle is not inheritable */ 
      TRUE,       /* yes, inherit some handles */ 
      DETACHED_PROCESS, /* the new process doesn't have a console */ 
      NULL,      /* use parent's environment block */ 
      NULL,     /* use parent's starting directory */ 
      &startup,     /* startup info, i.e. std handles */ 
      &pinfo); 

    CloseHandle(pipe_write); 

    if (!ret) { 
     fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError()); 
     CloseHandle(pipe_read); 
     return -1; 
    } 

    CloseHandle(pinfo.hProcess); 
    CloseHandle(pinfo.hThread); 

    /* wait for the "OK\n" message */ 
    { 
     char temp[3]; 
     DWORD count; 

     ret = ReadFile(pipe_read, temp, 3, &count, NULL); 
     CloseHandle(pipe_read); 
     if (!ret) { 
      fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError()); 
      return -1; 
     } 
     if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { 
      fprintf(stderr, "ADB server didn't ACK\n"); 
      return -1; 
     } 
    } 
#elif defined(HAVE_FORKEXEC) 
    char path[PATH_MAX]; 
    int  fd[2]; 

    // set up a pipe so the child can tell us when it is ready. 
    // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child. 
    if (pipe(fd)) { 
     fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); 
     return -1; 
    } 
    get_my_path(path); 
    pid_t pid = fork(); 
    if(pid < 0) return -1; 

    if (pid == 0) { 
     // child side of the fork 

     // redirect stderr to the pipe 
     // we use stderr instead of stdout due to stdout's buffering behavior. 
     adb_close(fd[0]); 
     dup2(fd[1], STDERR_FILENO); 
     adb_close(fd[1]); 

     // child process 
     int result = execl(path, "adb", "fork-server", "server", NULL); 
     // this should not return 
     fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); 
    } else { 
     // parent side of the fork 

     char temp[3]; 

     temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C'; 
     // wait for the "OK\n" message 
     adb_close(fd[1]); 
     int ret = adb_read(fd[0], temp, 3); 
     adb_close(fd[0]); 
     if (ret < 0) { 
      fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno); 
      return -1; 
     } 
     if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { 
      fprintf(stderr, "ADB server didn't ACK\n"); 
      return -1; 
     } 

     setsid(); 
    } 
#else 
#error "cannot implement background server start on this platform" 
#endif 
    return 0; 
} 
#endif 

我認爲adb.exe子進程繼承adb.exe的手柄,如果子進程的adb.exe不會退出,ReadFile將永遠阻止。但是當我執行命令「adb.exe start-server」時,一切正常。那麼windows如何調用CreateProcess和ReadFile呢?

回答

0

我找到了答案:Redirecting an arbitrary Console's Input/Output - CodeProject

重定向控制檯處理的輸入/輸出的技術是非常樣品:通過STARTUPINFO結構的CreateProcess()API使我們能夠重定向基於子控制檯進程的標準手柄。因此,我們可以將這些句柄設置爲管道句柄,文件句柄或任何我們可以讀寫的句柄。此技術的細節已在MSDN中清楚地描述:HOWTO:產生重定向標準句柄的控制檯進程。

但是,MSDN的示例代碼有兩個大問題。首先,假設子進程首先發送輸出,然後等待輸入,然後刷新輸出緩衝區並退出。如果子進程的行爲不像那樣,父進程將被掛起。這是因爲ReadFile()函數保持阻塞狀態,直到子進程發送一些輸出或退出。其次,在重定向16位控制檯(包括基於控制檯的MS-DOS應用程序)方面存在問題。在Windows 9x上,即使子進程終止,ReadFile仍然被阻止;在Windows NT/XP上,如果子進程是DOS應用程序,則ReadFile始終返回FALSE,並將錯誤代碼設置爲ERROR_BROKEN_PIPE。

解決ReadFile的

塊問題要防止阻塞的ReadFile父進程,我們可以簡單地通過一個文件句柄作爲標準輸出的子進程,然後監視此文件。更簡單的方法是在調用ReadFile()之前調用PeekNamedPipe()函數。 PeekNamedPipe函數檢查有關管道中數據的信息,然後立即返回。如果管道中沒有可用數據,請不要調用ReadFile。

通過在ReadFile之前調用PeekNamedPipe,我們還解決了在Windows 9x上重定向16位控制檯的塊問題。