2010-09-04 108 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位控制台的块问题。