diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b83789..954e92f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.10) project(uciadapter) # Configure Process -add_definitions(-DUNIX) SET(process src/ProcessLinux.cpp) +SET(COMPILE_PLATFORM UNIX) if(WIN32) - remove_definitions(-DUNIX) - message(FATAL_ERROR "uciadapter is not yet compatible with Windows") + SET(process src/ProcessWindows.cpp) + SET(COMPILE_PLATFORM WIN32) endif() add_library(uciadapter SHARED src/UCI.cpp ${process}) @@ -14,7 +14,7 @@ add_library(uciadapter SHARED src/UCI.cpp ${process}) set(UCIADAPTER_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/includes) # For conveniance set(UCIADAPTER_INCLUDE_DIR ${UCIADAPTER_INCLUDE_DIR} PARENT_SCOPE) # To be used by other projects with add_subdirectory() file(MAKE_DIRECTORY ${UCIADAPTER_INCLUDE_DIR}) -configure_file(src/UCI.hpp ${UCIADAPTER_INCLUDE_DIR} COPYONLY) +configure_file(src/UCI.hpp.in ${UCIADAPTER_INCLUDE_DIR}/UCI.hpp) configure_file(src/Process.hpp ${UCIADAPTER_INCLUDE_DIR} COPYONLY) configure_file(src/ProcessLinux.hpp ${UCIADAPTER_INCLUDE_DIR} COPYONLY) include_directories(${UCIADAPTER_INCLUDE_DIR}) diff --git a/README.md b/README.md index af03e51..a0341ae 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # uciadapter *uciadapter* is a C++ library that allows you to communicate with any chess engines that follows the [UCI Protocol](http://wbec-ridderkerk.nl/html/UCIProtocol.html). -It aims to work on Linux and Windows (not yet on windows). +It works on both platforms, Linux and Windows. # How to use it ? PGNP can be used as a shared library in your project. diff --git a/src/Process.hpp b/src/Process.hpp index 3265cc9..e1f71ad 100644 --- a/src/Process.hpp +++ b/src/Process.hpp @@ -1,5 +1,4 @@ #include - #define ENGINE_TIMEOUT 5 // In seconds #define BUFFER_SIZE 1024 @@ -12,7 +11,8 @@ public: virtual void Kill() = 0; /// @brief Start the engine from file path virtual void Start(std::string) = 0; - /// @brief Read one line from the stdout of the engine (could raise a ReadTimeoutExpire) + /// @brief Read one line from the stdout of the engine (could raise a + /// ReadTimeoutExpire) virtual std::string ReadLine() = 0; /// @brief Write to engine stdin virtual void Write(std::string) = 0; diff --git a/src/ProcessWindows.cpp b/src/ProcessWindows.cpp new file mode 100644 index 0000000..77ca13c --- /dev/null +++ b/src/ProcessWindows.cpp @@ -0,0 +1,148 @@ +#include "ProcessWindows.hpp" +#include + +namespace uciadapter { + +void ProcessWindows::CreateChildProcess(std::string engine_path) +// Create a child process that uses the previously created pipes for STDIN and +// STDOUT. +{ + TCHAR szCmdline[1024]; + _tcscpy_s(szCmdline, CA2T(engine_path.c_str())); + 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, + szCmdline, // 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); + + // Close handles to the stdin and stdout pipes no longer needed by the child + // process. If they are not explicitly closed, there is no way to recognize + // that the child process has ended. + + CloseHandle(g_hChildStd_OUT_Wr); + CloseHandle(g_hChildStd_IN_Rd); + } +} + +void ProcessWindows::ErrorExit(PTSTR lpszFunction) + +// Format a readable error message, display a message box, +// and exit from the application. +{ + 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); +} + +ProcessWindows::ProcessWindows() { + + printf("\n->Start of parent execution.\n"); + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + 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")); +} + +void ProcessWindows::Kill() {} + +void ProcessWindows::Start(std::string path) { CreateChildProcess(path); } + +std::string ProcessWindows::ReadLine() { + DWORD dwRead, dwWritten; + CHAR chBuf[2014]; + BOOL bSuccess = FALSE; + HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + std::string line = ""; + for (;;) { + CHAR c; + bSuccess = ReadFile(g_hChildStd_OUT_Rd, &c, 1, &dwRead, NULL); + line += c; + if (!bSuccess || dwRead == 0) + break; + if (c == '\n') + break; + } + + printf("ejeh\n"); + return (line); +} + +void ProcessWindows::Write(std::string data) { + DWORD dwRead, dwWritten; + CHAR chBuf[1024]; + BOOL bSuccess = FALSE; + + bSuccess = + WriteFile(g_hChildStd_IN_Wr, data.c_str(), data.size(), &dwWritten, NULL); +} +} // namespace uciadapter \ No newline at end of file diff --git a/src/ProcessWindows.hpp b/src/ProcessWindows.hpp new file mode 100644 index 0000000..6f9f212 --- /dev/null +++ b/src/ProcessWindows.hpp @@ -0,0 +1,27 @@ +#include "Process.hpp" +#include + +#include +#include +#include +#include + +namespace uciadapter { + +class ProcessWindows : public Process { + HANDLE g_hChildStd_IN_Rd = NULL; + HANDLE g_hChildStd_IN_Wr = NULL; + HANDLE g_hChildStd_OUT_Rd = NULL; + HANDLE g_hChildStd_OUT_Wr = NULL; + SECURITY_ATTRIBUTES saAttr; + void ProcessWindows::ErrorExit(PTSTR lpszFunction); + void ProcessWindows::CreateChildProcess(std::string); + +public: + ProcessWindows(); + void Kill(); + void Start(std::string); + std::string ReadLine(); + void Write(std::string); +}; +}; // namespace uciadapter \ No newline at end of file diff --git a/src/UCI.hpp b/src/UCI.hpp.in similarity index 93% rename from src/UCI.hpp rename to src/UCI.hpp.in index 19cc1e9..86925c5 100644 --- a/src/UCI.hpp +++ b/src/UCI.hpp.in @@ -1,9 +1,13 @@ +#define @COMPILE_PLATFORM@ #ifdef UNIX #include "ProcessLinux.hpp" #define INIT_PROCESS(p) \ { p = static_cast(new ProcessLinux()); } -#else +#endif +#ifdef WIN32 #include "ProcessWindows.hpp" +#define INIT_PROCESS(p) \ + { p = static_cast(new ProcessWindows()); } #endif #include #include