本文介绍了CreateProcess cmd.exe读/写管道死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,我正在尝试为cmd.exe制作一个前端GUI,以便可以使其更宽一些,但我被卡住了。

Hello I am trying to make a front end GUI for cmd.exe so I can make it wider but I got stuck.

我尝试设计类似以下的API此

I try to design an API like this

char* Directory = WriteCommand("dir");
printf("- %s\n", Directory);

和输出看起来就像在cmd窗口中一样,除了我在字符串中,

and the output look exactly like it would in a cmd window, except I have it in a string, so it would be

DATE TIME FILESIZE FILENAME
etc etc etc

然后我可以发出

char* Up = WriteCommand ("cd ..");

,它将显示上面的目录列表。因此,我希望通过使用管道进行读取和写入的终端控件。

and it will give me the above directory listing. So I want a terminal control through using pipes to read and write.

我已经基于此MSDN示例代码尝试了许多事情-

I have tried many things based on this MSDN sample code - https://msdn.microsoft.com/en-us/library/ms682499.aspx

但我认为此代码仅是发出一个命令并读取一个响应的好方法,因为紧接在此处,死锁如下所述-

But I think this code is only good to issue one command, and read one response, because right after it deadlocks as described here - https://blogs.msdn.microsoft.com/oldnewthing/20110707-00/?p=10223

我在这里看到了其他几个问题,例如类似的问题-,但是没有解决方案对我有用。

I see several other questions here, like this one with similar problems - How to read output from cmd.exe using CreateProcess() and CreatePipe() but no solutions posted work for me.

所以这是我的代码。

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

#define BUFSIZE 4096

HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

HANDLE g_hInputFile = NULL;

void CreateChildProcess(void);
void WriteToPipe(char* Arg1);
void ReadFromPipe(void);
void ErrorExit(PTSTR);



int _tmain(int argc, TCHAR *argv[])
{
    SECURITY_ATTRIBUTES saAttr;

    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, 0))
        ErrorExit(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))
        ErrorExit(TEXT("Stdout SetHandleInformation"));

    // Create a pipe for the child process's STDIN.

    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        ErrorExit(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))
    ErrorExit(TEXT("Stdin SetHandleInformation"));

    // Create the child process.

    CreateChildProcess();

    // Get a handle to an input file for the parent.
    // This example assumes a plain text file and uses string output to verify data flow.

/*if (argc == 1)
    ErrorExit(TEXT("Please specify an input file.\n"));

g_hInputFile = CreateFile(
    argv[1],
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_READONLY,
    NULL);

if (g_hInputFile == INVALID_HANDLE_VALUE)
    ErrorExit(TEXT("CreateFile"));*/

    // Write to the pipe that is the standard input for a child process.
    // Data is written to the pipe's buffers, so it is not necessary to wait
    // until the child process is running before writing data.



// Read from pipe that is the standard output for child process.


ReadFromPipe();

WriteToPipe("ipconfig");

// THIS IS WHERE DEADLOCK OCCURS, FROM HERE
// PROGRAM BECOMES UNRESPONSIVE - HOW TO FIX THIS?

ReadFromPipe();



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.

return 0;
}

void CreateChildProcess()
// Create a child process that uses the previously created pipes for     STDIN and STDOUT.
{
   TCHAR szCmdline[] = TEXT("cmd.exe /k");
    PROCESS_INFORMATION piProcInfo;
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE;

    // 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 = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.hStdInput = g_hChildStd_IN_Rd;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.

bSuccess = CreateProcess(NULL,
    "cmd.exe",     // 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)
    ErrorExit(TEXT("CreateProcess"));
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);
}
}

void WriteToPipe(char* Command)

// Read from a file and write its contents to the pipe for the    child's STDIN.
// Stop when there is no more data.
   {
   DWORD dwRead, dwWritten;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;

    bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
    if (bSuccess == FALSE)
        printf("write fail\n");

    printf("written = %i\n", dwWritten);


//for (;;)
//{
    //bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
    //if (!bSuccess || dwRead == 0) break;

    //bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
    //if (bSuccess == FALSE)
        //printf("write fail\n");

    //printf("written = %i\n", dwWritten);
//}

// Close the pipe handle so the child process stops reading.

//if (!CloseHandle(g_hChildStd_IN_Wr))
    //ErrorExit(TEXT("StdInWr CloseHandle"));
}

void ReadFromPipe(void)

// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

int i;

for (i = 0; i < 4; i++)
{

    /*DWORD dwAvail = 0;
    if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &dwAvail, NULL)) {
        // error, the child process might have ended
        break;
    }
    if (!dwAvail) {
        // no data available in the pipe
        break;
    }*/

    bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
    if (!bSuccess || dwRead == 0) break;

    /*bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
    if (!bSuccess) break;*/

    chBuf[dwRead] = '\0';

    printf("%i - %s\n", i, chBuf);
}

printf("done\n");
}

我发出初始的 cmd.exe命令,该命令使我开始命令提示符。我现在想发出 ipconfig(或任何其他命令)以获取网络信息。程序陷入僵局,无法响应。我无法再读取子进程的输出。我怎样才能解决这个问题?谢谢您的帮助。

I issue the initial "cmd.exe" command which gives me the start of the command prompt. I now want to issue "ipconfig" (or any other command) to get networking info. The program deadlocks and becomes unresponsive. I can no longer read output of child process. How can I fix this? Thanks for your help.

推荐答案

避免死锁的最强大,最有效的解决方案-使用异步io。永远不要等待IO(读取,写入,ioctl)完成,而要在回调中处理。

the most power and effective solution for avoid any deadlocks - use asynchronous io. never wait for IO (read,write,ioctl) complete in place, but handle this in callbacks.

还请注意使用管道进行重定向输出-非常常见的错误,我们需要为 STDIN STDOUT ,并需要创建2个不同的管道对-一个用于 STDIN ,另一个用于 STDOUT 。这是错误的。我们可以对 STDIN STDOUT (和 STDERROR )使用单个管道句柄。

also note about use pipes for redirect output - very common errancy that we need use different handles for STDIN and STDOUT and need create 2 different pipes pair - one for STDIN and another for STDOUT. this is false. we can use single pipe handle for both STDIN and STDOUT (and STDERROR).


  1. 我们需要使用和
    PIPE_ACCESS_DUPLEX | FILE_READ_DATA | FILE_WRITE_DATA | FILE_FLAG_OVERLAPPED
    标志。通过使用 PIPE_ACCESS_DUPLEX ,我们创建了双向管道,
    ,因此服务器和客户端进程都可以从管道中读取和写入
    。和 FILE_FLAG_OVERLAPPED 设为异步
    模式。同样,我们也不使该句柄可继承,因此不需要在其上调用
    SetHandleInformation

  2. 我们由 CreateFileW 还具有
    FILE_GENERIC_READ | FILE_GENERIC_WRITE 的访问权限-这使能力
    都将其都分配给 stdin stdout 。因为客户端(例如
    cmd.exe)通常采用同步io-我们在这里不使用
    FILE_FLAG_OVERLAPPED 。也通过使用 lpSecurityAttributes 我们
    只是使该句柄可继承。

  3. 我们需要将服务器句柄绑定到某些IOCP,以便在io $ b $时调用回调b结束。这里我们有3种变体-使用
    -最简单的方法或使用
    。我们也可以自己创建IOCP并拥有
    线程池,但是对于重定向子进程输出,这种方式通常不需要

  4. 创建子进程后-需要关闭客户端管道句柄
    (我们复制到子对象),只需在我们的管道
    句柄上调用 ReadFile 即可。当此 ReadFile 完成时-我们需要从回调中再次调用
    ReadFile ,依此类推-直到没有得到来自
    ReadFile 完成的错误(通常 ERROR_BROKEN_PIPE )。因此,我们一直需要
    一直有来自管道的活动读取请求,直到断开连接为止。

  5. ,我们可以在任意位置免费调用 WriteFile 时间和任何地方-永远不会
    导致死锁,因为我们使用异步io。

  6. 如果需要对读取
    数据进行复杂处理,则需要花费一些时间(非常少) (基于先前的结果和状态),并且在普通过程中使用此
    更加轻松,但在回调中不易处理,我们可以为此任务创建光纤
    ()并从工作线程回调中读取完成后的
    -首先调用(如果我们
    对于同一个工作线程多次调用此命令,则会出错
    在第二个和下一个呼叫中 ERROR_ALREADY_FIBER ,但是可以。 t
    所有这些工作仅从Vista开始。关于XP错误)。记住
    当前的光纤,到需要退回的位置()和
    呼叫(我们供读者阅读光纤)-我们可以在其中处理
    的读取结果,并在此之后通过调用
    返回a (使用光纤作为工作线程)。但是在非常罕见和特定的情况下,确实需要所有这些
    。通常
    句柄全部是与管道句柄相关的对象中具有状态的回调-绰绰有余。

  1. we need create server pipe handle by using CreateNamedPipeW withPIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPEDflags. by using PIPE_ACCESS_DUPLEX we create bi-directional pipe,as result both server and client processes can read from and writeto the pipe. and FILE_FLAG_OVERLAPPED give to as asynchronousmode. also we not make this handle inheritable, so not need callSetHandleInformation on it
  2. client handle we create by CreateFileW also withFILE_GENERIC_READ|FILE_GENERIC_WRITE access - this give abilityassign it both to stdin and stdout. because clients (likecmd.exe) usually assume synchronous io - we not useFILE_FLAG_OVERLAPPED here. also by using lpSecurityAttributes wejust make this handle inheritable.
  3. we need bind server handle to some IOCP, for callback called when iois ended. here we have 3 variants - useBindIoCompletionCallback - the most simply way or useCreateThreadpoolIo. also we can create IOCP yourself and ownthread pool, but for redirect child process output, this way usuallynot need.
  4. after we create child process - we need close client pipe handle(which we duplicate to child) and just call ReadFile on our pipehandle. when this ReadFile complete - we need again callReadFile from callback and so on - until we not got error fromReadFile in completion (usually ERROR_BROKEN_PIPE). so we needall time have active read request from pipe, until disconnect.
  5. and we free call WriteFile at any time and any place - this nevercause deadlock, because we use asynchronous io.
  6. some time (very very rarely) if we need complex processing on readdata(based on previous results and state) and this much more easyhandle in plain procedure but not in callbacks, we can create fiberfor this task (CreateFiber) and from working thread callback,when read complete - first call ConvertThreadToFiber (if wecall this more than once for same working thread - will be errorERROR_ALREADY_FIBER on second and next calls, but this is ok. butall this work begin from vista only. on xp error here). remembercurrent fiber, to where need retirn (GetCurrentFiber()) andcall SwitchToFiber (with our dedicated for read fiber)- wherewe can handle read result and after this return back by callSwitchToFiber (with fiber for worked thread). but all thisreally can be need in in very rare and specific scenarios. usuallyhandle all is callbacks with state in object related to pipe handle - more than enough.

简单示例为cmd

#define _XP_SUPPORT_

struct IO_COUNT
{
    HANDLE _hFile;
    HANDLE _hEvent;
    LONG _dwIoCount;

    IO_COUNT()
    {
        _dwIoCount = 1;
        _hEvent = 0;
    }

    ~IO_COUNT()
    {
        if (_hEvent)
        {
            CloseHandle(_hEvent);
        }
    }

    ULONG Create(HANDLE hFile);

    void BeginIo()
    {
        InterlockedIncrement(&_dwIoCount);
    }

    void EndIo()
    {
        if (!InterlockedDecrement(&_dwIoCount))
        {
            SetEvent(_hEvent);
        }
    }

    void Wait()
    {
        WaitForSingleObject(_hEvent, INFINITE);
    }
};


struct U_IRP : OVERLAPPED
{
    enum { read, write };

    IO_COUNT* _pIoObject;
    ULONG _code;
    LONG _dwRef;
    char _buffer[256];

    void AddRef()
    {
        InterlockedIncrement(&_dwRef);
    }

    void Release()
    {
        if (!InterlockedDecrement(&_dwRef)) delete this;
    }

    U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
    {
        _dwRef = 1;
        pIoObject->BeginIo();
        RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
    }

    ~U_IRP()
    {
        _pIoObject->EndIo();
    }

    ULONG CheckIoResult(BOOL fOk)
    {
        if (fOk)
        {
#ifndef _XP_SUPPORT_
            OnIoComplete(NOERROR, InternalHigh);
#endif
            return NOERROR;
        }

        ULONG dwErrorCode = GetLastError();

        if (dwErrorCode != ERROR_IO_PENDING)
        {
            OnIoComplete(dwErrorCode, 0);
        }

        return dwErrorCode;
    }

    ULONG Read()
    {
        _code = read;

        AddRef();

        return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
    }

    ULONG Write(const void* pvBuffer, ULONG cbBuffer)
    {
        _code = write;

        AddRef();

        return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
    }

    VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
    {
        switch (_code)
        {
        case read:
            if (dwErrorCode == NOERROR)
            {
                if (dwNumberOfBytesTransfered)
                {
                    if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
                    {
                        PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));

                        if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
                        {
                            if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
                            {
                                PSTR sz = (PSTR)alloca(cbMultiByte);

                                if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
                                {
                                    DbgPrint("%.*s", cbMultiByte, sz);
                                }
                            }
                        }
                    }
                }
                Read();
            }
            break;
        case write:
            break;
        default:
            __debugbreak();
        }

        Release();

        if (dwErrorCode)
        {
            DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
        }
    }

    static VOID WINAPI _OnIoComplete(
        DWORD dwErrorCode,
        DWORD_PTR dwNumberOfBytesTransfered,
        LPOVERLAPPED lpOverlapped
        )
    {
        static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
    }
};

ULONG IO_COUNT::Create(HANDLE hFile)
{
    _hFile = hFile;
    // error in declaration LPOVERLAPPED_COMPLETION_ROUTINE :
    // second parameter must be DWORD_PTR but not DWORD
    return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
#ifndef _XP_SUPPORT_
        SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
#endif
        (_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}

void ChildTest()
{
    static const WCHAR name[] = L"\\\\?\\pipe\\somename";

    HANDLE hFile = CreateNamedPipeW(name,
        PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
        PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        IO_COUNT obj;

        if (obj.Create(hFile) == NOERROR)
        {
            BOOL fOk = FALSE;

            SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };

            STARTUPINFOW si = { sizeof(si) };
            PROCESS_INFORMATION pi;

            si.dwFlags = STARTF_USESTDHANDLES;

            si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
                FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);

            if (si.hStdError != INVALID_HANDLE_VALUE)
            {
                si.hStdInput = si.hStdOutput = si.hStdError;

                WCHAR ApplicationName[MAX_PATH];
                if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
                {
                    if (CreateProcessW(ApplicationName, 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
                    {
                        CloseHandle(pi.hThread);
                        CloseHandle(pi.hProcess);
                        fOk = TRUE;
                    }
                }

                CloseHandle(si.hStdError);
            }

            if (fOk)
            {
                STATIC_ASTRING(help_and_exit, "help\r\nexit\r\n");

                U_IRP* p;

                if (p = new U_IRP(&obj))
                {
                    p->Read();
                    p->Release();
                }

                obj.EndIo();

                //++ simulate user commands
                static PCSTR commands[] = { "help\r\n", "ver\r\n", "dir\r\n", "exit\r\n" };
                ULONG n = RTL_NUMBER_OF(commands);
                PCSTR* psz = commands;
                do
                {
                    if (MessageBoxW(0,0, L"force close ?", MB_YESNO) == IDYES)
                    {
                        DisconnectNamedPipe(hFile);
                        break;
                    }
                    if (p = new U_IRP(&obj))
                    {
                        PCSTR command = *psz++;
                        p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
                        p->Release();
                    }
                } while (--n);
                //--

                obj.Wait();
            }
        }

        CloseHandle(hFile);
    }
}

这篇关于CreateProcess cmd.exe读/写管道死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 04:19