summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Guegan <manzerbredes@mailbox.org>2022-02-03 07:10:32 +0100
committerLoic Guegan <manzerbredes@mailbox.org>2022-02-03 07:10:32 +0100
commitdb4d104ed965592c1d89c31d96301f0a44640df7 (patch)
tree89aa5b8db92a890ac0ea03a1f69f2250957d5391
parent053b44639f28f018a51a5aada6548f212ad1b372 (diff)
Integrate windows process
-rw-r--r--CMakeLists.txt8
-rw-r--r--README.md2
-rw-r--r--src/Process.hpp4
-rw-r--r--src/ProcessWindows.cpp148
-rw-r--r--src/ProcessWindows.hpp27
-rw-r--r--src/UCI.hpp.in (renamed from src/UCI.hpp)6
6 files changed, 187 insertions, 8 deletions
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 <string>
-
#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 <atlstr.h>
+
+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 <chrono>
+
+#include <stdio.h>
+#include <strsafe.h>
+#include <tchar.h>
+#include <windows.h>
+
+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
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<Process *>(new ProcessLinux()); }
-#else
+#endif
+#ifdef WIN32
#include "ProcessWindows.hpp"
+#define INIT_PROCESS(p) \
+ { p = static_cast<Process *>(new ProcessWindows()); }
#endif
#include <chrono>
#include <sstream>