Domanda

Quando uso CreateProcess per creare processi adb.exe, bloccherà in 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;
}

Se il codice modifica

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;
}

funziona, ma quando elimino il codice printf, bloccherà di nuovo.

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;
}

Nel codice di adb.exe, vedo qualche codice come 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

Credo che il processo figlio della adb.exe eredita la maniglia della adb.exe, se il processo figlio di adb.exe non esce, ReadFile bloccherà per sempre. Ma quando ho exec "adb.exe start-server" al comando, tutto è ok. Così come fa finestre comando CreateProcess chiamata e ReadFile?

È stato utile?

Soluzione

Ho trovato la risposta: reindirizzamento di una arbitraria del Console Input / Output - CodeProject .

  

La tecnica di reindirizzare l'ingresso / uscita di un processo di console è molto campione: The CreateProcess () API attraverso la struttura STARTUPINFO ci consente di reindirizzare i manici standard di un processo basato console bambino. Così possiamo impostare queste maniglie per un handle pipe, handle di file, o qualsiasi manico che siamo in grado di leggere e scrivere. Il dettaglio di questa tecnica è stata descritta in modo chiaro in MSDN: HOWTO:. Spawn Console Processi con maniglie reindirizzate standard

     

Tuttavia, il codice di esempio di MSDN ha due grosso problema. In primo luogo, essa assume il processo figlio invierà in uscita in un primo momento, quindi attendere ingresso, quindi svuotare il buffer di uscita e di uscita. Se il processo figlio non si comporta così, il processo padre sarà riattaccato. La ragione di questo è il ReadFile () resti bloccato funzione fino al processo secondario invia un output, o uscite.

     

In secondo luogo, Ha problema per reindirizzare una console a 16 bit su Windows 9x, resti ReadFile bloccato anche dopo che il processo figlio è terminato (comprese le applicazioni basate console MS-DOS.); In Windows NT / XP, ReadFile restituisce sempre FALSO con set di codici di errore per ERROR_BROKEN_PIPE se il processo figlio è un'applicazione DOS.

     

Risolvere il problema del blocco di ReadFile

     

Per evitare che il processo padre di essere bloccato da ReadFile, si può semplicemente passare un handle di file come stdout al processo figlio, quindi monitorare questo file. Un modo più semplice è quello di chiamare la funzione PeekNamedPipe () prima di chiamare ReadFile (). Le informazioni funzione controlla PeekNamedPipe sui dati nel tubo, poi ritorna immediatamente. Se non ci sono dati disponibili nel tubo, non chiamare ReadFile.

     

Chiamando PeekNamedPipe prima ReadFile, abbiamo anche risolvere il problema del blocco di reindirizzare una console a 16 bit su Windows 9x.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top