I will give you a christmas present. I have a working GUI which works for Windows and Unix. I post the full source for the Windows piping below. It works, while it might not be perfect it will give you a perfect starting point. Just adjust it for your own needs.
Pay close attention to open(), read() and write(). In open(), I established piping and created a child engine process by calling CreateChildProcess().
Code: Select all
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <assert.h>
#include <iostream>
#include <stdlib.h>
#include <strsafe.h>
#include "Adapter\EngineAdapterWindows.hpp"
#include "Adapter\AbstractEngineAdapter.hpp"
using namespace std;
#define BUFFER_SIZE 4096
class EngineAdapterWindows : public AbstractEngineAdapter
{
public:
EngineAdapterWindows(std::string command);
~EngineAdapterWindows();
/* --------------- AbstractAdapter --------------- */
// Inherited from AbstractAdapter
void write(const std::string& line);
// Inherited from AbstractAdapter
void open();
// Inherited from AbstractAdapter
void close();
// Inherited from AbstractAdapter
std::string read();
private:
std::string _command;
// Internal buffer for IO
CHAR buffer[BUFFER_SIZE];
// STDIN redirection read handle
HANDLE hChildStd_IN_Rd;
// STDIN redirection write handle
HANDLE hChildStd_IN_Wr;
// STDOUT redirection read handle
HANDLE hChildStd_OUT_Rd;
// STDOUT redirection write handle
HANDLE hChildStd_OUT_Wr;
// Adapter handle
HANDLE adapterHandle;
PROCESS_INFORMATION engineProcInfo;
};
/* ---------------------------------- Member Methods ---------------------------------- */
EngineAdapterWindows::EngineAdapterWindows(string command) : _command(command)
{
}
EngineAdapterWindows::~EngineAdapterWindows()
{
}
DWORD WINAPI createWindowsAdapter(LPVOID lpParam)
{
((EngineAdapterWindows *) lpParam)->createAdapater();
return TRUE;
}
void ErrorExit(PTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
BOOL WriteToFile(LPCSTR name, CHAR *buffer, const int length)
{
DWORD dwWritten;
HANDLE file = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE)
{
return FALSE;
}
if (!WriteFile(file, buffer, length, &dwWritten, NULL))
{
return FALSE;
}
if (dwWritten != length)
{
return FALSE;
}
CloseHandle(file);
return TRUE;
}
/*
* Read a line from the engine's process pipe.
*/
BOOL ReadLine(CHAR *buffer, const int size, HANDLE hChildStd_OUT_Rd)
{
DWORD dwRead;
string lines;
while (true)
{
if (!ReadFile(hChildStd_OUT_Rd, buffer, size, &dwRead, NULL))
{
int error = GetLastError();
return FALSE;
}
if (dwRead == 0)
{
return FALSE;
}
else
{
bool shouldBreak = false;
// Break if one of the newline character received
if (buffer[dwRead - 1] == '\r' || buffer[dwRead - 1] == '\n')
{
buffer[dwRead - 1] = '\0';
shouldBreak = true;
}
else
{
// Assume no buffer-overflow
buffer[dwRead] = '\0';
}
// Append the new buffer to the previous
lines = lines + string(buffer);
if (shouldBreak)
{
break;
}
}
}
strcpy(buffer, lines.c_str());
CHAR *p = buffer;
// In Windows, newline character is "\r\n", remove them to ensure platform indpendence
for (CHAR *q = buffer; *q != '\0'; q++)
{
if (*q != '\r')
{
*(p++) = *q;
}
}
*p = '\0';
return TRUE;
}
void CreateChildProcess(PROCESS_INFORMATION &engineProcInfo, LPCSTR command, HANDLE hChildStd_OUT_Wr, HANDLE hChildStd_IN_Rd)
{
STARTUPINFO siStartInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&engineProcInfo, sizeof(PROCESS_INFORMATION));
// Specify the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_OUT_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.hStdInput = hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
LPSTR tempCommand = strdup(command);
// Create the child process.
if (!CreateProcess(NULL,
tempCommand, // 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
&engineProcInfo)) // Receives PROCESS_INFORMATION
{
free(tempCommand);
ErrorExit(TEXT("CreateProcess"));
}
free(tempCommand);
}
/*
* Establish inter-process connection to engine.
*/
void EngineAdapterWindows::open()
{
SECURITY_ATTRIBUTES saAttr;
// 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(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))
{
ErrorExit(TEXT("StdoutRd CreatePipe"));
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
ErrorExit(TEXT("Stdout SetHandleInformation"));
}
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0))
{
ErrorExit(TEXT("Stdin CreatePipe"));
}
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
{
ErrorExit(TEXT("Stdin SetHandleInformation"));
}
CreateChildProcess(engineProcInfo, _command.c_str(), hChildStd_OUT_Wr, hChildStd_IN_Rd);
// Sleep for 200ms so that the engine could initialize itself
Sleep(200);
if ((adapterHandle = CreateThread(NULL, 0, &createWindowsAdapter, this, 0, NULL)) == NULL)
{
ErrorExit(TEXT("Failed on creating an adapter thread"));
}
_state = ACTIVE;
}
void EngineAdapterWindows::close()
{
if (_state != TERMINATED && _state != INACTIVE)
{
_state = TERMINATED;
assert(TerminateProcess(engineProcInfo.hProcess, 0));
assert(TerminateThread(adapterHandle, 0) == TRUE);
assert(CloseHandle(hChildStd_OUT_Rd));
assert(CloseHandle(hChildStd_OUT_Wr));
assert(CloseHandle(hChildStd_IN_Rd));
assert(CloseHandle(hChildStd_IN_Wr));
assert(CloseHandle(engineProcInfo.hProcess));
assert(CloseHandle(engineProcInfo.hThread));
_receiver = NULL;
}
}
string EngineAdapterWindows::read()
{
if (!ReadLine(buffer, BUFFER_SIZE, hChildStd_OUT_Rd))
{
throw "ReadLine() failed";
}
return string(buffer);
}
void EngineAdapterWindows::write(const string& command)
{
string commandLine(command + "\n");
DWORD dwWritten;
if (!WriteFile(hChildStd_IN_Wr, commandLine.c_str(), command.size() + 1, &dwWritten, NULL))
{
throw "Failed to write " + command + " in EngineAdapterWindows::write()";
}
}
VirtualAdapter * createAdapterWindows(std::string command)
{
return new EngineAdapterWindows(command);
}