Вопрос

Когда я использую CreateProcess для создания процесса ADB.EXE, он будет блокировать в ReadFile.

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 я вижу какой-то код, как Belows:

#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 CommandProcess и ReadFile?

Это было полезно?

Решение

Я нашел ответ: Перенаправление произвольной консоли ввода / вывода - CodeProject.

Техника перенаправления ввода / вывода процесса консоли очень образена: API CreateProcess () через структуру StartUpInfo позволяет перенаправлять стандартные ручки процесса на основе дочернего консоли. Таким образом, мы можем установить эти ручки на ручку трубы, ручку файла или любую ручку, которую мы можем прочитать и запись. Деталь этой методики была четко описана в MSDN: HOWTO: PAWN COUSOLE процессы с перенаправленными стандартными ручками.

Тем не менее, код образца MSDN имеет две большие проблемы. Во-первых, он предполагает, что дочерний процесс сначала отправит вывод, а затем ждать ввода, затем промойте выходной буфер и выход. Если детский процесс не ведет себя так, родительский процесс будет зависеть. Причина этого является функция readfile () остается заблокированной дочерним процессом, не отправляет некоторое вывод или выходы.

Во-вторых, у него есть проблема для перенаправления 16-битной консоли (включая приложения MS-DOS на основе консоли.) В Windows 9X ReadFile остается заблокированным даже после окончания дочернего процесса; В Windows NT / XP, readfile всегда возвращает false с кодом ошибки, установленным на error_brone_pipe, если детский процесс - это приложение DOS.

Решение блока задачи readfile

Чтобы предотвратить заблокируемый родительский процесс с помощью ReadFile, мы можем просто передать дескриптор файла в качестве Stdout к дочерному процессу, затем отслеживать этот файл. Более простой способ - звонить в функцию PeeknamedPipe () перед вызовом readfile (). Функция PeeknamedPipe проверяет информацию о данных в трубе, затем немедленно возвращает. Если в трубе нет данных, не звоните ReadFile.

Позвонив PeeknamedPipe перед ReadFile, мы также решаем блок-задачу перенаправления 16-битной консоли на Windows 9x.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top