Subversion Repositories Games.Chess Giants

Rev

Rev 175 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // pipe.cpp -- somewhat portable pipe implementation that resembles POSIX popen()/pclose()
  2.  
  3. #include "common.h"
  4.  
  5.  
  6. // definitions used in this module only
  7. #define RD 0
  8. #define WR 1
  9.  
  10.  
  11. #ifdef _WIN32
  12. typedef struct pipe_s
  13. {
  14.    PROCESS_INFORMATION procinfo;
  15.    int descriptors[2]; // RD and WR
  16. } pipe_t;
  17. #endif // _WIN32
  18.  
  19.  
  20. // function prototypes
  21. FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode);
  22. int pipe_close (FILE *stream);
  23. int pipe_isalive (FILE *stream);
  24. int pipe_hasdata (FILE *stream);
  25. int pipe_read (FILE *stream, void *dstbuf, int nbytes);
  26. int pipe_write (FILE *stream, void *srcbuf, int nbytes);
  27.  
  28.  
  29. FILE *pipe_open (const wchar_t *shell_command, const wchar_t *mode)
  30. {
  31.    // wrapper around popen(), and simple reimplementation of it for systems that don't have it
  32.    // mode shall be "r", "w", or "r+"
  33.  
  34. #ifdef _WIN32
  35.    #define IT_TO_ME 0
  36.    #define ME_TO_IT 1
  37.    SECURITY_ATTRIBUTES security_attributes;
  38.    wchar_t current_path[MAX_PATH];
  39.    wchar_t *command_pathname;
  40.    STARTUPINFO startup_info;
  41.    HANDLE hStdIO[2][2];
  42.    pipe_t *pipe;
  43.  
  44.    (void) mode; // all pipes are open read-write
  45.  
  46.    command_pathname = (wchar_t *) malloc ((MAX_PATH + 4096) * sizeof (wchar_t)); // should be enough
  47.    if (command_pathname == NULL)
  48.       return (NULL);
  49.  
  50.    // figure out command pathname out of full command string
  51.    wcscpy_s (command_pathname, MAX_PATH + 4096, shell_command);
  52.    if ((command_pathname[0] == L'"') && (wcschr (&command_pathname[1], L'"') != NULL))
  53.    {
  54.       memmove (command_pathname, &command_pathname[1], (MAX_PATH + 4096 - 1) * sizeof (wchar_t));
  55.       *wcschr (&command_pathname[1], L'"') = 0;
  56.    }
  57.    else if (wcschr (command_pathname, L' ') != NULL)
  58.       *wcschr (command_pathname, L' ') = 0;
  59.  
  60.    // build the bidirectional I/O pipe
  61.    pipe = (pipe_t *) malloc (sizeof (pipe_t));
  62.    memset (&security_attributes, 0, sizeof (security_attributes)); // prepare the pipes' security attributes
  63.    security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES);
  64.    security_attributes.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited
  65.    hStdIO[0][0] = hStdIO[0][1] = hStdIO[1][0] = hStdIO[1][1] = NULL;
  66.    CreatePipe (&hStdIO[ME_TO_IT][RD], &hStdIO[ME_TO_IT][WR], &security_attributes, 0); // create a pipe for the child process's stdin
  67.    CreatePipe (&hStdIO[IT_TO_ME][RD], &hStdIO[IT_TO_ME][WR], &security_attributes, 0); // create a pipe for the child process's stdout
  68.    SetHandleInformation (hStdIO[ME_TO_IT][WR], HANDLE_FLAG_INHERIT, 0); // ensure the write handle to the pipe for STDIN is not inherited
  69.    SetHandleInformation (hStdIO[IT_TO_ME][RD], HANDLE_FLAG_INHERIT, 0); // ensure the read handle to the pipe for STDOUT is not inherited
  70.    pipe->descriptors[RD] = _open_osfhandle ((intptr_t) hStdIO[IT_TO_ME][RD], _O_BINARY);
  71.    pipe->descriptors[WR] = _open_osfhandle ((intptr_t) hStdIO[ME_TO_IT][WR], _O_BINARY | _O_APPEND);
  72.  
  73.    // spawn the chess engine process with redirected input and output
  74.    memset (&startup_info, 0, sizeof (startup_info));
  75.    startup_info.cb = sizeof (STARTUPINFOA);
  76.    startup_info.dwFlags = STARTF_USESTDHANDLES;
  77.    startup_info.hStdInput = hStdIO[ME_TO_IT][RD];
  78.    startup_info.hStdOutput = hStdIO[IT_TO_ME][WR];
  79.    startup_info.hStdError = hStdIO[IT_TO_ME][WR];
  80.    GetCurrentDirectory (WCHAR_SIZEOF (current_path), current_path);
  81.    if (!CreateProcess (command_pathname, (wchar_t *) shell_command, NULL, NULL, true, CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, NULL, current_path, &startup_info, &pipe->procinfo))
  82.    {
  83.       pipe_close ((FILE *) pipe);
  84.       free (command_pathname);
  85.       return (NULL);
  86.    }
  87.  
  88.    // close the handles we won't use, free the command pathname memory space and return a pointer to our pipe object
  89.    CloseHandle (hStdIO[IT_TO_ME][WR]);
  90.    CloseHandle (hStdIO[ME_TO_IT][RD]);
  91.    #undef IT_TO_ME
  92.    #undef ME_TO_IT
  93.    free (command_pathname);
  94.    return ((FILE *) pipe);
  95. #else // !_WIN32
  96.    return (popen (shell_command, mode));
  97. #endif // _WIN32
  98. }
  99.  
  100.  
  101. int pipe_close (FILE *stream)
  102. {
  103.    // wrapper around pclose(), and simple reimplementation of it for systems that don't have it
  104.  
  105. #ifdef _WIN32
  106.    HANDLE hRemoteThread;
  107.    HANDLE hProcessDup;
  108.    DWORD handle_flags;
  109.    DWORD thread_id;
  110.    DWORD exit_code;
  111.    pipe_t *pipe;
  112.    bool was_duplicated;
  113.  
  114.    pipe = (pipe_t *) stream; // cast FILE * back into our own structure
  115.  
  116.    // close all handles
  117.    if (pipe->descriptors[RD] != -1) _close (pipe->descriptors[RD]); pipe->descriptors[RD] = -1;
  118.    if (pipe->descriptors[WR] != -1) _close (pipe->descriptors[WR]); pipe->descriptors[WR] = -1;
  119.  
  120.    // terminate process if it hasn't terminated by itself yet
  121.    exit_code = 0;
  122.    if (GetExitCodeProcess (pipe->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE))
  123.    {
  124.       // taken from Dr. Dobbs. How to terminate any process cleanly: create a remote thread in it,
  125.       // and make its start address point right into kernel32's ExitProcess() function...
  126.       was_duplicated = (DuplicateHandle (GetCurrentProcess (), pipe->procinfo.hProcess, GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS, FALSE, 0) != 0);
  127.       if (!was_duplicated)
  128.          hProcessDup = pipe->procinfo.hProcess;
  129.       hRemoteThread = CreateRemoteThread (hProcessDup, NULL, 0, (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle (L"Kernel32"), "ExitProcess"), (void *) 0, 0, &thread_id);
  130.       if (hRemoteThread != NULL)
  131.       {
  132.          WaitForSingleObject (hProcessDup, INFINITE); // must wait process to terminate to guarantee that it has exited
  133.          CloseHandle (hRemoteThread);
  134.       }
  135.       else
  136.          TerminateProcess (pipe->procinfo.hProcess, 0); // if process refuses our remote thread, kill it
  137.       if (was_duplicated)
  138.          CloseHandle (hProcessDup);
  139.    }
  140.  
  141.    // close process handles
  142.    if (pipe->procinfo.hProcess != NULL)
  143.       CloseHandle (pipe->procinfo.hProcess);
  144.    if (GetHandleInformation (pipe->procinfo.hThread, &handle_flags))
  145.       CloseHandle (pipe->procinfo.hThread);
  146.  
  147.    // and finally free the pipe structure
  148.    free (pipe);
  149.    return ((int) exit_code);
  150. #else // !_WIN32
  151.    return (pclose (stream));
  152. #endif // _WIN32
  153. }
  154.  
  155.  
  156. int pipe_isalive (FILE *stream)
  157. {
  158.    // wrapper around ioctl() on pipes for systems that don't have it
  159.  
  160. #ifdef _WIN32
  161.    DWORD exit_code;
  162.    return (GetExitCodeProcess (((pipe_t *) stream)->procinfo.hProcess, &exit_code) && (exit_code == STILL_ACTIVE));
  163. #else // !_WIN32
  164.    return (1); // UNIX doesn't provide a portable way for this
  165. #endif // _WIN32
  166. }
  167.  
  168.  
  169. int pipe_hasdata (FILE *stream)
  170. {
  171.    // wrapper around ioctl() on pipes for systems that don't have it
  172.  
  173. #ifdef _WIN32
  174.    return (!_eof (((pipe_t *) stream)->descriptors[RD]));
  175. #else // !_WIN32
  176.    return (feof (stream));
  177. #endif // _WIN32
  178. }
  179.  
  180.  
  181. int pipe_read (FILE *stream, void *dstbuf, int nbytes)
  182. {
  183.    // wrapper around fread() on pipes for systems that don't support it
  184.  
  185. #ifdef _WIN32
  186.    return (_read (((pipe_t *) stream)->descriptors[RD], dstbuf, nbytes));
  187. #else // !_WIN32
  188.    return (fread (dstbuf, 1, nbytes, stream));
  189. #endif // _WIN32
  190. }
  191.  
  192.  
  193. int pipe_write (FILE *stream, void *srcbuf, int nbytes)
  194. {
  195.    // wrapper around fwrite() on pipes for systems that don't support it
  196.  
  197. #ifdef _WIN32
  198.    return (_write (((pipe_t *) stream)->descriptors[WR], srcbuf, nbytes));
  199. #else // !_WIN32
  200.    return (fwrite (srcbuf, 1, nbytes, stream));
  201. #endif // _WIN32
  202. }
  203.