node-pty 1.1.0-beta9 → 1.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +2 -0
  2. package/binding.gyp +7 -4
  3. package/deps/winpty/src/agent/Agent.cc +11 -3
  4. package/deps/winpty/src/winpty.gyp +29 -1
  5. package/lib/conpty_console_list_agent.js +3 -7
  6. package/lib/conpty_console_list_agent.js.map +1 -1
  7. package/lib/index.js +2 -1
  8. package/lib/index.js.map +1 -1
  9. package/lib/terminal.js.map +1 -1
  10. package/lib/terminal.test.js.map +1 -1
  11. package/lib/unixTerminal.js +78 -37
  12. package/lib/unixTerminal.js.map +1 -1
  13. package/lib/unixTerminal.test.js +3 -5
  14. package/lib/unixTerminal.test.js.map +1 -1
  15. package/lib/utils.js +23 -1
  16. package/lib/utils.js.map +1 -1
  17. package/lib/windowsConoutConnection.js +6 -3
  18. package/lib/windowsConoutConnection.js.map +1 -1
  19. package/lib/windowsPtyAgent.js +45 -48
  20. package/lib/windowsPtyAgent.js.map +1 -1
  21. package/lib/windowsTerminal.js +19 -21
  22. package/lib/windowsTerminal.js.map +1 -1
  23. package/lib/windowsTerminal.test.js +40 -26
  24. package/lib/windowsTerminal.test.js.map +1 -1
  25. package/lib/worker/conoutSocketWorker.js.map +1 -1
  26. package/package.json +6 -2
  27. package/prebuilds/darwin-arm64/pty.node +0 -0
  28. package/prebuilds/darwin-arm64/spawn-helper +0 -0
  29. package/prebuilds/darwin-x64/pty.node +0 -0
  30. package/prebuilds/darwin-x64/spawn-helper +0 -0
  31. package/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  32. package/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  33. package/prebuilds/win32-arm64/conpty.node +0 -0
  34. package/prebuilds/win32-arm64/conpty.pdb +0 -0
  35. package/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  36. package/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  37. package/prebuilds/win32-arm64/pty.node +0 -0
  38. package/prebuilds/win32-arm64/pty.pdb +0 -0
  39. package/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  40. package/prebuilds/win32-arm64/winpty-agent.pdb +0 -0
  41. package/prebuilds/win32-arm64/winpty.dll +0 -0
  42. package/prebuilds/win32-arm64/winpty.pdb +0 -0
  43. package/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  44. package/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  45. package/prebuilds/win32-x64/conpty.node +0 -0
  46. package/prebuilds/win32-x64/conpty.pdb +0 -0
  47. package/prebuilds/win32-x64/conpty_console_list.node +0 -0
  48. package/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  49. package/prebuilds/win32-x64/pty.node +0 -0
  50. package/prebuilds/win32-x64/pty.pdb +0 -0
  51. package/prebuilds/win32-x64/winpty-agent.exe +0 -0
  52. package/prebuilds/win32-x64/winpty-agent.pdb +0 -0
  53. package/prebuilds/win32-x64/winpty.dll +0 -0
  54. package/prebuilds/win32-x64/winpty.pdb +0 -0
  55. package/scripts/gen-compile-commands.js +8 -0
  56. package/scripts/{publish.js → increment-version.js} +10 -18
  57. package/scripts/post-install.js +48 -10
  58. package/scripts/prebuild.js +34 -0
  59. package/src/conpty_console_list_agent.ts +2 -6
  60. package/src/index.ts +2 -1
  61. package/src/interfaces.ts +2 -1
  62. package/src/native.d.ts +5 -5
  63. package/src/terminal.test.ts +1 -1
  64. package/src/terminal.ts +2 -2
  65. package/src/unix/pty.cc +28 -11
  66. package/src/unixTerminal.test.ts +3 -6
  67. package/src/unixTerminal.ts +101 -34
  68. package/src/utils.ts +20 -0
  69. package/src/win/conpty.cc +141 -55
  70. package/src/win/conpty.h +41 -0
  71. package/src/win/path_util.cc +23 -24
  72. package/src/windowsConoutConnection.ts +6 -3
  73. package/src/windowsPtyAgent.ts +42 -43
  74. package/src/windowsTerminal.test.ts +46 -33
  75. package/src/windowsTerminal.ts +24 -29
  76. package/src/worker/conoutSocketWorker.ts +2 -2
  77. package/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  78. package/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  79. package/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  80. package/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  81. package/typings/node-pty.d.ts +9 -1
package/src/win/conpty.cc CHANGED
@@ -11,16 +11,18 @@
11
11
  #define _WIN32_WINNT 0x600
12
12
 
13
13
  #define NODE_ADDON_API_DISABLE_DEPRECATED
14
- #include <napi.h>
14
+ #include <node_api.h>
15
15
  #include <assert.h>
16
16
  #include <Shlwapi.h> // PathCombine, PathIsRelative
17
17
  #include <sstream>
18
+ #include <iostream>
18
19
  #include <string>
19
20
  #include <thread>
20
21
  #include <vector>
21
22
  #include <Windows.h>
22
23
  #include <strsafe.h>
23
24
  #include "path_util.h"
25
+ #include "conpty.h"
24
26
 
25
27
  // Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17134
26
28
  #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
@@ -32,6 +34,7 @@ typedef HRESULT (__stdcall *PFNCREATEPSEUDOCONSOLE)(COORD c, HANDLE hIn, HANDLE
32
34
  typedef HRESULT (__stdcall *PFNRESIZEPSEUDOCONSOLE)(HPCON hpc, COORD newSize);
33
35
  typedef HRESULT (__stdcall *PFNCLEARPSEUDOCONSOLE)(HPCON hpc);
34
36
  typedef void (__stdcall *PFNCLOSEPSEUDOCONSOLE)(HPCON hpc);
37
+ typedef void (__stdcall *PFNRELEASEPSEUDOCONSOLE)(HPCON hpc);
35
38
 
36
39
  #endif
37
40
 
@@ -46,27 +49,26 @@ struct pty_baton {
46
49
  pty_baton(int _id, HANDLE _hIn, HANDLE _hOut, HPCON _hpc) : id(_id), hIn(_hIn), hOut(_hOut), hpc(_hpc) {};
47
50
  };
48
51
 
49
- static std::vector<pty_baton*> ptyHandles;
52
+ static std::vector<std::unique_ptr<pty_baton>> ptyHandles;
50
53
  static volatile LONG ptyCounter;
51
54
 
52
55
  static pty_baton* get_pty_baton(int id) {
53
- for (size_t i = 0; i < ptyHandles.size(); ++i) {
54
- pty_baton* ptyHandle = ptyHandles[i];
55
- if (ptyHandle->id == id) {
56
- return ptyHandle;
57
- }
56
+ auto it = std::find_if(ptyHandles.begin(), ptyHandles.end(), [id](const auto& ptyHandle) {
57
+ return ptyHandle->id == id;
58
+ });
59
+ if (it != ptyHandles.end()) {
60
+ return it->get();
58
61
  }
59
62
  return nullptr;
60
63
  }
61
64
 
62
65
  static bool remove_pty_baton(int id) {
63
- for (size_t i = 0; i < ptyHandles.size(); ++i) {
64
- pty_baton* ptyHandle = ptyHandles[i];
65
- if (ptyHandle->id == id) {
66
- ptyHandles.erase(ptyHandles.begin() + i);
67
- ptyHandle = nullptr;
68
- return true;
69
- }
66
+ auto it = std::remove_if(ptyHandles.begin(), ptyHandles.end(), [id](const auto& ptyHandle) {
67
+ return ptyHandle->id == id;
68
+ });
69
+ if (it != ptyHandles.end()) {
70
+ ptyHandles.erase(it);
71
+ return true;
70
72
  }
71
73
  return false;
72
74
  }
@@ -100,16 +102,26 @@ void SetupExitCallback(Napi::Env env, Napi::Function cb, pty_baton* baton) {
100
102
  // Get process exit code.
101
103
  GetExitCodeProcess(baton->hShell, (LPDWORD)(&exit_event->exit_code));
102
104
  // Clean up handles
103
- // Calling DisconnectNamedPipes here or in PtyKill results in a crash,
104
- // ref https://github.com/microsoft/node-pty/issues/512,
105
- // so we only call CloseHandle for now.
106
- CloseHandle(baton->hIn);
107
- CloseHandle(baton->hOut);
105
+ CloseHandle(baton->hShell);
106
+ assert(remove_pty_baton(baton->id));
108
107
 
109
108
  auto status = tsfn.BlockingCall(exit_event, callback); // In main thread
110
- assert(status == napi_ok);
109
+ switch (status) {
110
+ case napi_closing:
111
+ break;
111
112
 
112
- tsfn.Release();
113
+ case napi_queue_full:
114
+ Napi::Error::Fatal("SetupExitCallback", "Queue was full");
115
+
116
+ case napi_ok:
117
+ if (tsfn.Release() != napi_ok) {
118
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.Release() failed");
119
+ }
120
+ break;
121
+
122
+ default:
123
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.BlockingCall() failed");
124
+ }
113
125
  });
114
126
  }
115
127
 
@@ -141,28 +153,60 @@ bool createDataServerPipe(bool write,
141
153
  /*dwOpenMode=*/winOpenMode,
142
154
  /*dwPipeMode=*/PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
143
155
  /*nMaxInstances=*/1,
144
- /*nOutBufferSize=*/0,
145
- /*nInBufferSize=*/0,
156
+ /*nOutBufferSize=*/128 * 1024,
157
+ /*nInBufferSize=*/128 * 1024,
146
158
  /*nDefaultTimeOut=*/30000,
147
159
  &sa);
148
160
 
149
161
  return *hServer != INVALID_HANDLE_VALUE;
150
162
  }
151
163
 
152
- HRESULT CreateNamedPipesAndPseudoConsole(COORD size,
164
+ HANDLE LoadConptyDll(const Napi::CallbackInfo& info,
165
+ const bool useConptyDll)
166
+ {
167
+ if (!useConptyDll) {
168
+ return LoadLibraryExW(L"kernel32.dll", 0, 0);
169
+ }
170
+ wchar_t currentDir[MAX_PATH];
171
+ HMODULE hModule = GetModuleHandleA("conpty.node");
172
+ if (hModule == NULL) {
173
+ throw errorWithCode(info, "Failed to get conpty.node module handle");
174
+ }
175
+ DWORD result = GetModuleFileNameW(hModule, currentDir, MAX_PATH);
176
+ if (result == 0) {
177
+ throw errorWithCode(info, "Failed to get conpty.node module file name");
178
+ }
179
+ PathRemoveFileSpecW(currentDir);
180
+ wchar_t conptyDllPath[MAX_PATH];
181
+ PathCombineW(conptyDllPath, currentDir, L"conpty\\conpty.dll");
182
+ if (!path_util::file_exists(conptyDllPath)) {
183
+ std::wstring errorMessage = L"Cannot find conpty.dll at " + std::wstring(conptyDllPath);
184
+ std::string errorMessageStr = path_util::wstring_to_string(errorMessage);
185
+ throw errorWithCode(info, errorMessageStr.c_str());
186
+ }
187
+
188
+ return LoadLibraryW(conptyDllPath);
189
+ }
190
+
191
+ HRESULT CreateNamedPipesAndPseudoConsole(const Napi::CallbackInfo& info,
192
+ COORD size,
153
193
  DWORD dwFlags,
154
194
  HANDLE *phInput,
155
195
  HANDLE *phOutput,
156
196
  HPCON* phPC,
157
197
  std::wstring& inName,
158
198
  std::wstring& outName,
159
- const std::wstring& pipeName)
199
+ const std::wstring& pipeName,
200
+ const bool useConptyDll)
160
201
  {
161
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
202
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
203
+ DWORD error = GetLastError();
162
204
  bool fLoadedDll = hLibrary != nullptr;
163
205
  if (fLoadedDll)
164
206
  {
165
- PFNCREATEPSEUDOCONSOLE const pfnCreate = (PFNCREATEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "CreatePseudoConsole");
207
+ PFNCREATEPSEUDOCONSOLE const pfnCreate = (PFNCREATEPSEUDOCONSOLE)GetProcAddress(
208
+ (HMODULE)hLibrary,
209
+ useConptyDll ? "ConptyCreatePseudoConsole" : "CreatePseudoConsole");
166
210
  if (pfnCreate)
167
211
  {
168
212
  if (phPC == NULL || phInput == NULL || phOutput == NULL)
@@ -189,6 +233,8 @@ HRESULT CreateNamedPipesAndPseudoConsole(COORD size,
189
233
  // We should fall back to winpty in this case.
190
234
  return HRESULT_FROM_WIN32(GetLastError());
191
235
  }
236
+ } else {
237
+ throw errorWithCode(info, "Failed to load conpty.dll");
192
238
  }
193
239
 
194
240
  // Failed to find kernel32. This is realy unlikely - honestly no idea how
@@ -206,14 +252,15 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
206
252
  std::unique_ptr<wchar_t[]> mutableCommandline;
207
253
  PROCESS_INFORMATION _piClient{};
208
254
 
209
- if (info.Length() != 6 ||
255
+ if (info.Length() != 7 ||
210
256
  !info[0].IsString() ||
211
257
  !info[1].IsNumber() ||
212
258
  !info[2].IsNumber() ||
213
259
  !info[3].IsBoolean() ||
214
260
  !info[4].IsString() ||
215
- !info[5].IsBoolean()) {
216
- throw Napi::Error::New(env, "Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor)");
261
+ !info[5].IsBoolean() ||
262
+ !info[6].IsBoolean()) {
263
+ throw Napi::Error::New(env, "Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor, useConptyDll)");
217
264
  }
218
265
 
219
266
  const std::wstring filename(path_util::to_wstring(info[0].As<Napi::String>()));
@@ -222,6 +269,7 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
222
269
  const bool debug = info[3].As<Napi::Boolean>().Value();
223
270
  const std::wstring pipeName(path_util::to_wstring(info[4].As<Napi::String>()));
224
271
  const bool inheritCursor = info[5].As<Napi::Boolean>().Value();
272
+ const bool useConptyDll = info[6].As<Napi::Boolean>().Value();
225
273
 
226
274
  // use environment 'Path' variable to determine location of
227
275
  // the relative path that we have recieved (e.g cmd.exe)
@@ -241,7 +289,7 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
241
289
 
242
290
  HANDLE hIn, hOut;
243
291
  HPCON hpc;
244
- HRESULT hr = CreateNamedPipesAndPseudoConsole({cols, rows}, inheritCursor ? 1/*PSEUDOCONSOLE_INHERIT_CURSOR*/ : 0, &hIn, &hOut, &hpc, inName, outName, pipeName);
292
+ HRESULT hr = CreateNamedPipesAndPseudoConsole(info, {cols, rows}, inheritCursor ? 1/*PSEUDOCONSOLE_INHERIT_CURSOR*/ : 0, &hIn, &hOut, &hpc, inName, outName, pipeName, useConptyDll);
245
293
 
246
294
  // Restore default handling of ctrl+c
247
295
  SetConsoleCtrlHandler(NULL, FALSE);
@@ -252,8 +300,9 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
252
300
  if (SUCCEEDED(hr)) {
253
301
  // We were able to instantiate a conpty
254
302
  const int ptyId = InterlockedIncrement(&ptyCounter);
255
- marshal.Set("pty", Napi::Number::New(env, ptyId));
256
- ptyHandles.insert(ptyHandles.end(), new pty_baton(ptyId, hIn, hOut, hpc));
303
+ marshal.Set("pty", Napi::Number::New(env, ptyId));
304
+ ptyHandles.emplace_back(
305
+ std::make_unique<pty_baton>(ptyId, hIn, hOut, hpc));
257
306
  } else {
258
307
  throw Napi::Error::New(env, "Cannot launch conpty");
259
308
  }
@@ -284,20 +333,22 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
284
333
  std::stringstream errorText;
285
334
  BOOL fSuccess = FALSE;
286
335
 
287
- if (info.Length() != 5 ||
336
+ if (info.Length() != 6 ||
288
337
  !info[0].IsNumber() ||
289
338
  !info[1].IsString() ||
290
339
  !info[2].IsString() ||
291
340
  !info[3].IsArray() ||
292
- !info[4].IsFunction()) {
293
- throw Napi::Error::New(env, "Usage: pty.connect(id, cmdline, cwd, env, exitCallback)");
341
+ !info[4].IsBoolean() ||
342
+ !info[5].IsFunction()) {
343
+ throw Napi::Error::New(env, "Usage: pty.connect(id, cmdline, cwd, env, useConptyDll, exitCallback)");
294
344
  }
295
345
 
296
346
  const int id = info[0].As<Napi::Number>().Int32Value();
297
347
  const std::wstring cmdline(path_util::to_wstring(info[1].As<Napi::String>()));
298
348
  const std::wstring cwd(path_util::to_wstring(info[2].As<Napi::String>()));
299
349
  const Napi::Array envValues = info[3].As<Napi::Array>();
300
- Napi::Function exitCallback = info[4].As<Napi::Function>();
350
+ const bool useConptyDll = info[4].As<Napi::Boolean>().Value();
351
+ Napi::Function exitCallback = info[5].As<Napi::Function>();
301
352
 
302
353
  // Fetch pty handle from ID and start process
303
354
  pty_baton* handle = get_pty_baton(id);
@@ -375,9 +426,27 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
375
426
  throw errorWithCode(info, "Cannot create process");
376
427
  }
377
428
 
429
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
430
+ bool fLoadedDll = hLibrary != nullptr;
431
+ if (useConptyDll && fLoadedDll)
432
+ {
433
+ PFNRELEASEPSEUDOCONSOLE const pfnReleasePseudoConsole = (PFNRELEASEPSEUDOCONSOLE)GetProcAddress(
434
+ (HMODULE)hLibrary, "ConptyReleasePseudoConsole");
435
+ if (pfnReleasePseudoConsole)
436
+ {
437
+ pfnReleasePseudoConsole(handle->hpc);
438
+ }
439
+ }
440
+
378
441
  // Update handle
379
442
  handle->hShell = piClient.hProcess;
380
443
 
444
+ // Close the thread handle to avoid resource leak
445
+ CloseHandle(piClient.hThread);
446
+ // Close the input read and output write handle of the pseudoconsole
447
+ CloseHandle(handle->hIn);
448
+ CloseHandle(handle->hOut);
449
+
381
450
  SetupExitCallback(env, exitCallback, handle);
382
451
 
383
452
  // Return
@@ -390,25 +459,29 @@ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
390
459
  Napi::Env env(info.Env());
391
460
  Napi::HandleScope scope(env);
392
461
 
393
- if (info.Length() != 3 ||
462
+ if (info.Length() != 4 ||
394
463
  !info[0].IsNumber() ||
395
464
  !info[1].IsNumber() ||
396
- !info[2].IsNumber()) {
397
- throw Napi::Error::New(env, "Usage: pty.resize(id, cols, rows)");
465
+ !info[2].IsNumber() ||
466
+ !info[3].IsBoolean()) {
467
+ throw Napi::Error::New(env, "Usage: pty.resize(id, cols, rows, useConptyDll)");
398
468
  }
399
469
 
400
470
  int id = info[0].As<Napi::Number>().Int32Value();
401
471
  SHORT cols = static_cast<SHORT>(info[1].As<Napi::Number>().Uint32Value());
402
472
  SHORT rows = static_cast<SHORT>(info[2].As<Napi::Number>().Uint32Value());
473
+ const bool useConptyDll = info[3].As<Napi::Boolean>().Value();
403
474
 
404
475
  const pty_baton* handle = get_pty_baton(id);
405
476
 
406
477
  if (handle != nullptr) {
407
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
478
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
408
479
  bool fLoadedDll = hLibrary != nullptr;
409
480
  if (fLoadedDll)
410
481
  {
411
- PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ResizePseudoConsole");
482
+ PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress(
483
+ (HMODULE)hLibrary,
484
+ useConptyDll ? "ConptyResizePseudoConsole" : "ResizePseudoConsole");
412
485
  if (pfnResizePseudoConsole)
413
486
  {
414
487
  COORD size = {cols, rows};
@@ -424,21 +497,30 @@ static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
424
497
  Napi::Env env(info.Env());
425
498
  Napi::HandleScope scope(env);
426
499
 
427
- if (info.Length() != 1 ||
428
- !info[0].IsNumber()) {
429
- throw Napi::Error::New(env, "Usage: pty.clear(id)");
500
+ if (info.Length() != 2 ||
501
+ !info[0].IsNumber() ||
502
+ !info[1].IsBoolean()) {
503
+ throw Napi::Error::New(env, "Usage: pty.clear(id, useConptyDll)");
430
504
  }
431
505
 
432
506
  int id = info[0].As<Napi::Number>().Int32Value();
507
+ const bool useConptyDll = info[1].As<Napi::Boolean>().Value();
508
+
509
+ // This API is only supported for conpty.dll as it was introduced in a later version of Windows.
510
+ // We could hook it up to point at >= a version of Windows only, but the future is conpty.dll
511
+ // anyway.
512
+ if (!useConptyDll) {
513
+ return env.Undefined();
514
+ }
433
515
 
434
516
  const pty_baton* handle = get_pty_baton(id);
435
517
 
436
518
  if (handle != nullptr) {
437
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
519
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
438
520
  bool fLoadedDll = hLibrary != nullptr;
439
521
  if (fLoadedDll)
440
522
  {
441
- PFNCLEARPSEUDOCONSOLE const pfnClearPseudoConsole = (PFNCLEARPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClearPseudoConsole");
523
+ PFNCLEARPSEUDOCONSOLE const pfnClearPseudoConsole = (PFNCLEARPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ConptyClearPseudoConsole");
442
524
  if (pfnClearPseudoConsole)
443
525
  {
444
526
  pfnClearPseudoConsole(handle->hpc);
@@ -453,29 +535,33 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
453
535
  Napi::Env env(info.Env());
454
536
  Napi::HandleScope scope(env);
455
537
 
456
- if (info.Length() != 1 ||
457
- !info[0].IsNumber()) {
458
- throw Napi::Error::New(env, "Usage: pty.kill(id)");
538
+ if (info.Length() != 2 ||
539
+ !info[0].IsNumber() ||
540
+ !info[1].IsBoolean()) {
541
+ throw Napi::Error::New(env, "Usage: pty.kill(id, useConptyDll)");
459
542
  }
460
543
 
461
544
  int id = info[0].As<Napi::Number>().Int32Value();
545
+ const bool useConptyDll = info[1].As<Napi::Boolean>().Value();
462
546
 
463
547
  const pty_baton* handle = get_pty_baton(id);
464
548
 
465
549
  if (handle != nullptr) {
466
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
550
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
467
551
  bool fLoadedDll = hLibrary != nullptr;
468
552
  if (fLoadedDll)
469
553
  {
470
- PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClosePseudoConsole");
554
+ PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress(
555
+ (HMODULE)hLibrary,
556
+ useConptyDll ? "ConptyClosePseudoConsole" : "ClosePseudoConsole");
471
557
  if (pfnClosePseudoConsole)
472
558
  {
473
559
  pfnClosePseudoConsole(handle->hpc);
474
560
  }
475
561
  }
476
-
477
- CloseHandle(handle->hShell);
478
- assert(remove_pty_baton(id));
562
+ if (useConptyDll) {
563
+ TerminateProcess(handle->hShell, 1);
564
+ }
479
565
  }
480
566
 
481
567
  return env.Undefined();
@@ -0,0 +1,41 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+
4
+ // This header prototypes the Pseudoconsole symbols from conpty.lib with their original names.
5
+ // This is required because we cannot import __imp_CreatePseudoConsole from a static library
6
+ // as it doesn't produce an import lib.
7
+ // We can't use an /ALTERNATENAME trick because it seems that that name is only resolved when the
8
+ // linker cannot otherwise find the symbol.
9
+
10
+ #pragma once
11
+
12
+ #include <consoleapi.h>
13
+
14
+ #ifndef CONPTY_IMPEXP
15
+ #define CONPTY_IMPEXP __declspec(dllimport)
16
+ #endif
17
+
18
+ #ifndef CONPTY_EXPORT
19
+ #ifdef __cplusplus
20
+ #define CONPTY_EXPORT extern "C" CONPTY_IMPEXP
21
+ #else
22
+ #define CONPTY_EXPORT extern CONPTY_IMPEXP
23
+ #endif
24
+ #endif
25
+
26
+ #define PSEUDOCONSOLE_RESIZE_QUIRK (2u)
27
+ #define PSEUDOCONSOLE_PASSTHROUGH_MODE (8u)
28
+
29
+ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
30
+ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
31
+
32
+ CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
33
+ CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);
34
+ CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show);
35
+ CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
36
+ CONPTY_EXPORT HRESULT WINAPI ConptyReleasePseudoConsole(HPCON hPC);
37
+
38
+ CONPTY_EXPORT VOID WINAPI ConptyClosePseudoConsole(HPCON hPC);
39
+ CONPTY_EXPORT VOID WINAPI ConptyClosePseudoConsoleTimeout(HPCON hPC, DWORD dwMilliseconds);
40
+
41
+ CONPTY_EXPORT HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC);
@@ -58,38 +58,37 @@ std::wstring get_shell_path(std::wstring filename) {
58
58
  return shellpath;
59
59
  }
60
60
 
61
- wchar_t buffer_[MAX_ENV];
61
+ wchar_t* buffer_ = new wchar_t[MAX_ENV];
62
62
  int read = ::GetEnvironmentVariableW(L"Path", buffer_, MAX_ENV);
63
- if (!read) {
64
- return shellpath;
65
- }
66
-
67
- std::wstring delimiter = L";";
68
- size_t pos = 0;
69
- std::vector<std::wstring> paths;
70
- std::wstring buffer(buffer_);
71
- while ((pos = buffer.find(delimiter)) != std::wstring::npos) {
72
- paths.push_back(buffer.substr(0, pos));
73
- buffer.erase(0, pos + delimiter.length());
74
- }
63
+ if (read) {
64
+ std::wstring delimiter = L";";
65
+ size_t pos = 0;
66
+ std::vector<std::wstring> paths;
67
+ std::wstring buffer(buffer_);
68
+ while ((pos = buffer.find(delimiter)) != std::wstring::npos) {
69
+ paths.push_back(buffer.substr(0, pos));
70
+ buffer.erase(0, pos + delimiter.length());
71
+ }
75
72
 
76
- const wchar_t *filename_ = filename.c_str();
73
+ const wchar_t *filename_ = filename.c_str();
77
74
 
78
- for (size_t i = 0; i < paths.size(); ++i) {
79
- std::wstring path = paths[i];
80
- wchar_t searchPath[MAX_PATH];
81
- ::PathCombineW(searchPath, const_cast<wchar_t*>(path.c_str()), filename_);
75
+ for (size_t i = 0; i < paths.size(); ++i) {
76
+ std::wstring path = paths[i];
77
+ wchar_t searchPath[MAX_PATH];
78
+ ::PathCombineW(searchPath, const_cast<wchar_t*>(path.c_str()), filename_);
82
79
 
83
- if (searchPath == NULL) {
84
- continue;
85
- }
80
+ if (searchPath == NULL) {
81
+ continue;
82
+ }
86
83
 
87
- if (file_exists(searchPath)) {
88
- shellpath = searchPath;
89
- break;
84
+ if (file_exists(searchPath)) {
85
+ shellpath = searchPath;
86
+ break;
87
+ }
90
88
  }
91
89
  }
92
90
 
91
+ delete[] buffer_;
93
92
  return shellpath;
94
93
  }
95
94
 
@@ -37,9 +37,12 @@ export class ConoutConnection implements IDisposable {
37
37
  public get onReady(): IEvent<void> { return this._onReady.event; }
38
38
 
39
39
  constructor(
40
- private _conoutPipeName: string
40
+ private _conoutPipeName: string,
41
+ private _useConptyDll: boolean
41
42
  ) {
42
- const workerData: IWorkerData = { conoutPipeName: _conoutPipeName };
43
+ const workerData: IWorkerData = {
44
+ conoutPipeName: _conoutPipeName
45
+ };
43
46
  const scriptPath = __dirname.replace('node_modules.asar', 'node_modules.asar.unpacked');
44
47
  this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
45
48
  this._worker.on('message', (message: ConoutWorkerMessage) => {
@@ -54,7 +57,7 @@ export class ConoutConnection implements IDisposable {
54
57
  }
55
58
 
56
59
  dispose(): void {
57
- if (this._isDisposed) {
60
+ if (!this._useConptyDll && this._isDisposed) {
58
61
  return;
59
62
  }
60
63
  this._isDisposed = true;