/ src / connection_win.cpp
connection_win.cpp
  1  #include "connection.h"
  2  
  3  #define WIN32_LEAN_AND_MEAN
  4  #define NOMCX
  5  #define NOSERVICE
  6  #define NOIME
  7  #include <assert.h>
  8  #include <windows.h>
  9  
 10  int GetProcessId()
 11  {
 12      return (int)::GetCurrentProcessId();
 13  }
 14  
 15  struct BaseConnectionWin : public BaseConnection {
 16      HANDLE pipe{INVALID_HANDLE_VALUE};
 17  };
 18  
 19  static BaseConnectionWin Connection;
 20  
 21  /*static*/ BaseConnection* BaseConnection::Create()
 22  {
 23      return &Connection;
 24  }
 25  
 26  /*static*/ void BaseConnection::Destroy(BaseConnection*& c)
 27  {
 28      auto self = reinterpret_cast<BaseConnectionWin*>(c);
 29      self->Close();
 30      c = nullptr;
 31  }
 32  
 33  bool BaseConnection::Open()
 34  {
 35      wchar_t pipeName[]{L"\\\\?\\pipe\\discord-ipc-0"};
 36      const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2;
 37      pipeName[pipeDigit] = L'0';
 38      auto self = reinterpret_cast<BaseConnectionWin*>(this);
 39      for (;;) {
 40          self->pipe = ::CreateFileW(
 41            pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
 42          if (self->pipe != INVALID_HANDLE_VALUE) {
 43              self->isOpen = true;
 44              return true;
 45          }
 46  
 47          auto lastError = GetLastError();
 48          if (lastError == ERROR_FILE_NOT_FOUND) {
 49              if (pipeName[pipeDigit] < L'9') {
 50                  pipeName[pipeDigit]++;
 51                  continue;
 52              }
 53          }
 54          else if (lastError == ERROR_PIPE_BUSY) {
 55              if (!WaitNamedPipeW(pipeName, 10000)) {
 56                  return false;
 57              }
 58              continue;
 59          }
 60          return false;
 61      }
 62  }
 63  
 64  bool BaseConnection::Close()
 65  {
 66      auto self = reinterpret_cast<BaseConnectionWin*>(this);
 67      ::CloseHandle(self->pipe);
 68      self->pipe = INVALID_HANDLE_VALUE;
 69      self->isOpen = false;
 70      return true;
 71  }
 72  
 73  bool BaseConnection::Write(const void* data, size_t length)
 74  {
 75      if (length == 0) {
 76          return true;
 77      }
 78      auto self = reinterpret_cast<BaseConnectionWin*>(this);
 79      assert(self);
 80      if (!self) {
 81          return false;
 82      }
 83      if (self->pipe == INVALID_HANDLE_VALUE) {
 84          return false;
 85      }
 86      assert(data);
 87      if (!data) {
 88          return false;
 89      }
 90      const DWORD bytesLength = (DWORD)length;
 91      DWORD bytesWritten = 0;
 92      return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE &&
 93        bytesWritten == bytesLength;
 94  }
 95  
 96  bool BaseConnection::Read(void* data, size_t length)
 97  {
 98      assert(data);
 99      if (!data) {
100          return false;
101      }
102      auto self = reinterpret_cast<BaseConnectionWin*>(this);
103      assert(self);
104      if (!self) {
105          return false;
106      }
107      if (self->pipe == INVALID_HANDLE_VALUE) {
108          return false;
109      }
110      DWORD bytesAvailable = 0;
111      if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
112          if (bytesAvailable >= length) {
113              DWORD bytesToRead = (DWORD)length;
114              DWORD bytesRead = 0;
115              if (::ReadFile(self->pipe, data, bytesToRead, &bytesRead, nullptr) == TRUE) {
116                  assert(bytesToRead == bytesRead);
117                  return true;
118              }
119              else {
120                  Close();
121              }
122          }
123      }
124      else {
125          Close();
126      }
127      return false;
128  }