1111#define _WIN32_WINNT 0x600
1212
1313#define NODE_ADDON_API_DISABLE_DEPRECATED
14- #include < napi .h>
14+ #include < node_api .h>
1515#include < assert.h>
1616#include < Shlwapi.h> // PathCombine, PathIsRelative
1717#include < sstream>
18+ #include < iostream>
1819#include < string>
1920#include < thread>
2021#include < vector>
2122#include < Windows.h>
2223#include < strsafe.h>
2324#include " path_util.h"
25+ #include " conpty.h"
2426
2527// Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17134
2628#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
@@ -162,20 +164,46 @@ bool createDataServerPipe(bool write,
162164 return *hServer != INVALID_HANDLE_VALUE;
163165}
164166
165- HRESULT CreateNamedPipesAndPseudoConsole (COORD size,
167+ HANDLE LoadConptyDll (const Napi::CallbackInfo& info,
168+ const bool useConptyDll)
169+ {
170+ if (!useConptyDll) {
171+ return LoadLibraryExW (L" kernel32.dll" , 0 , 0 );
172+ }
173+ wchar_t currentDir[MAX_PATH];
174+ DWORD result = GetCurrentDirectoryW (MAX_PATH, currentDir);
175+ if (result == 0 ) {
176+ throw errorWithCode (info, " Failed to get current directory" );
177+ }
178+ std::wstring currentDirStr (currentDir);
179+
180+ std::wstring conptyDllPath = currentDirStr + L" \\ build\\ Release\\ conpty\\ conpty.dll" ;
181+ if (!path_util::file_exists (conptyDllPath)) {
182+ throw errorWithCode (info, " Cannot find conpty.dll" );
183+ }
184+
185+ return LoadLibraryW (conptyDllPath.c_str ());
186+ }
187+
188+ HRESULT CreateNamedPipesAndPseudoConsole (const Napi::CallbackInfo& info,
189+ COORD size,
166190 DWORD dwFlags,
167191 HANDLE *phInput,
168192 HANDLE *phOutput,
169193 HPCON* phPC,
170194 std::wstring& inName,
171195 std::wstring& outName,
172- const std::wstring& pipeName)
196+ const std::wstring& pipeName,
197+ const bool useConptyDll)
173198{
174- HANDLE hLibrary = LoadLibraryExW (L" kernel32.dll" , 0 , 0 );
199+ HANDLE hLibrary = LoadConptyDll (info, useConptyDll);
200+ DWORD error = GetLastError ();
175201 bool fLoadedDll = hLibrary != nullptr ;
176202 if (fLoadedDll )
177203 {
178- PFNCREATEPSEUDOCONSOLE const pfnCreate = (PFNCREATEPSEUDOCONSOLE)GetProcAddress ((HMODULE)hLibrary, " CreatePseudoConsole" );
204+ PFNCREATEPSEUDOCONSOLE const pfnCreate = (PFNCREATEPSEUDOCONSOLE)GetProcAddress (
205+ (HMODULE)hLibrary,
206+ useConptyDll ? " ConptyCreatePseudoConsole" : " CreatePseudoConsole" );
179207 if (pfnCreate)
180208 {
181209 if (phPC == NULL || phInput == NULL || phOutput == NULL )
@@ -202,6 +230,8 @@ HRESULT CreateNamedPipesAndPseudoConsole(COORD size,
202230 // We should fall back to winpty in this case.
203231 return HRESULT_FROM_WIN32 (GetLastError ());
204232 }
233+ } else {
234+ throw errorWithCode (info, " Failed to load conpty.dll" );
205235 }
206236
207237 // Failed to find kernel32. This is realy unlikely - honestly no idea how
@@ -219,14 +249,15 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
219249 std::unique_ptr<wchar_t []> mutableCommandline;
220250 PROCESS_INFORMATION _piClient{};
221251
222- if (info.Length () != 6 ||
252+ if (info.Length () != 7 ||
223253 !info[0 ].IsString () ||
224254 !info[1 ].IsNumber () ||
225255 !info[2 ].IsNumber () ||
226256 !info[3 ].IsBoolean () ||
227257 !info[4 ].IsString () ||
228- !info[5 ].IsBoolean ()) {
229- throw Napi::Error::New (env, " Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor)" );
258+ !info[5 ].IsBoolean () ||
259+ !info[6 ].IsBoolean ()) {
260+ throw Napi::Error::New (env, " Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor, useConptyDll)" );
230261 }
231262
232263 const std::wstring filename (path_util::to_wstring (info[0 ].As <Napi::String>()));
@@ -235,6 +266,7 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
235266 const bool debug = info[3 ].As <Napi::Boolean>().Value ();
236267 const std::wstring pipeName (path_util::to_wstring (info[4 ].As <Napi::String>()));
237268 const bool inheritCursor = info[5 ].As <Napi::Boolean>().Value ();
269+ const bool useConptyDll = info[6 ].As <Napi::Boolean>().Value ();
238270
239271 // use environment 'Path' variable to determine location of
240272 // the relative path that we have recieved (e.g cmd.exe)
@@ -254,7 +286,7 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
254286
255287 HANDLE hIn, hOut;
256288 HPCON hpc;
257- HRESULT hr = CreateNamedPipesAndPseudoConsole ({cols, rows}, inheritCursor ? 1 /* PSEUDOCONSOLE_INHERIT_CURSOR*/ : 0 , &hIn, &hOut, &hpc, inName, outName, pipeName);
289+ HRESULT hr = CreateNamedPipesAndPseudoConsole (info, {cols, rows}, inheritCursor ? 1 /* PSEUDOCONSOLE_INHERIT_CURSOR*/ : 0 , &hIn, &hOut, &hpc, inName, outName, pipeName, useConptyDll );
258290
259291 // Restore default handling of ctrl+c
260292 SetConsoleCtrlHandler (NULL , FALSE );
@@ -403,25 +435,27 @@ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
403435 Napi::Env env (info.Env ());
404436 Napi::HandleScope scope (env);
405437
406- if (info.Length () != 3 ||
438+ if (info.Length () != 4 ||
407439 !info[0 ].IsNumber () ||
408440 !info[1 ].IsNumber () ||
409- !info[2 ].IsNumber ()) {
410- throw Napi::Error::New (env, " Usage: pty.resize(id, cols, rows)" );
441+ !info[2 ].IsNumber () ||
442+ !info[3 ].IsBoolean ()) {
443+ throw Napi::Error::New (env, " Usage: pty.resize(id, cols, rows, useConptyDll)" );
411444 }
412445
413446 int id = info[0 ].As <Napi::Number>().Int32Value ();
414447 SHORT cols = static_cast <SHORT>(info[1 ].As <Napi::Number>().Uint32Value ());
415448 SHORT rows = static_cast <SHORT>(info[2 ].As <Napi::Number>().Uint32Value ());
449+ const bool useConptyDll = info[3 ].As <Napi::Boolean>().Value ();
416450
417451 const pty_baton* handle = get_pty_baton (id);
418452
419453 if (handle != nullptr ) {
420- HANDLE hLibrary = LoadLibraryExW ( L" kernel32.dll " , 0 , 0 );
454+ HANDLE hLibrary = LoadConptyDll (info, useConptyDll );
421455 bool fLoadedDll = hLibrary != nullptr ;
422456 if (fLoadedDll )
423457 {
424- PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress ((HMODULE)hLibrary, " ResizePseudoConsole " );
458+ PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress ((HMODULE)hLibrary, " ConptyResizePseudoConsole " );
425459 if (pfnResizePseudoConsole)
426460 {
427461 COORD size = {cols, rows};
@@ -466,21 +500,23 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
466500 Napi::Env env (info.Env ());
467501 Napi::HandleScope scope (env);
468502
469- if (info.Length () != 1 ||
470- !info[0 ].IsNumber ()) {
471- throw Napi::Error::New (env, " Usage: pty.kill(id)" );
503+ if (info.Length () != 2 ||
504+ !info[0 ].IsNumber () ||
505+ !info[1 ].IsBoolean ()) {
506+ throw Napi::Error::New (env, " Usage: pty.kill(id, useConptyDll)" );
472507 }
473508
474509 int id = info[0 ].As <Napi::Number>().Int32Value ();
510+ const bool useConptyDll = info[1 ].As <Napi::Boolean>().Value ();
475511
476512 const pty_baton* handle = get_pty_baton (id);
477513
478514 if (handle != nullptr ) {
479- HANDLE hLibrary = LoadLibraryExW ( L" kernel32.dll " , 0 , 0 );
515+ HANDLE hLibrary = LoadConptyDll (info, useConptyDll );
480516 bool fLoadedDll = hLibrary != nullptr ;
481517 if (fLoadedDll )
482518 {
483- PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress ((HMODULE)hLibrary, " ClosePseudoConsole " );
519+ PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress ((HMODULE)hLibrary, " ConptyClosePseudoConsole " );
484520 if (pfnClosePseudoConsole)
485521 {
486522 pfnClosePseudoConsole (handle->hpc );
0 commit comments