node-pty 1.1.0-beta5 → 1.1.0-beta7

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.
package/src/win/conpty.cc CHANGED
@@ -8,24 +8,20 @@
8
8
  * with pseudo-terminal file descriptors.
9
9
  */
10
10
 
11
- // node versions lower than 10 define this as 0x502 which disables many of the definitions needed to compile
12
- #include <node_version.h>
13
- #if NODE_MODULE_VERSION <= 57
14
- #define _WIN32_WINNT 0x600
15
- #endif
11
+ #define _WIN32_WINNT 0x600
16
12
 
17
- #include <iostream>
18
- #include <nan.h>
13
+ #define NODE_ADDON_API_DISABLE_DEPRECATED
14
+ #include <napi.h>
15
+ #include <assert.h>
19
16
  #include <Shlwapi.h> // PathCombine, PathIsRelative
20
17
  #include <sstream>
21
18
  #include <string>
19
+ #include <thread>
22
20
  #include <vector>
23
21
  #include <Windows.h>
24
22
  #include <strsafe.h>
25
23
  #include "path_util.h"
26
24
 
27
- extern "C" void init(v8::Local<v8::Object>);
28
-
29
25
  // Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17134
30
26
  #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
31
27
  #define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE \
@@ -46,10 +42,6 @@ struct pty_baton {
46
42
  HPCON hpc;
47
43
 
48
44
  HANDLE hShell;
49
- HANDLE hWait;
50
- Nan::Callback cb;
51
- uv_async_t async;
52
- uv_thread_t tid;
53
45
 
54
46
  pty_baton(int _id, HANDLE _hIn, HANDLE _hOut, HPCON _hpc) : id(_id), hIn(_hIn), hOut(_hOut), hpc(_hpc) {};
55
47
  };
@@ -67,19 +59,65 @@ static pty_baton* get_pty_baton(int id) {
67
59
  return nullptr;
68
60
  }
69
61
 
70
- template <typename T>
71
- std::vector<T> vectorFromString(const std::basic_string<T> &str) {
72
- return std::vector<T>(str.begin(), str.end());
62
+ 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
+ }
70
+ }
71
+ return false;
72
+ }
73
+
74
+ struct ExitEvent {
75
+ int exit_code = 0;
76
+ };
77
+
78
+ void SetupExitCallback(Napi::Env env, Napi::Function cb, pty_baton* baton) {
79
+ std::thread *th = new std::thread;
80
+ // Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE.
81
+ auto tsfn = Napi::ThreadSafeFunction::New(
82
+ env,
83
+ cb, // JavaScript function called asynchronously
84
+ "SetupExitCallback_resource", // Name
85
+ 0, // Unlimited queue
86
+ 1, // Only one thread will use this initially
87
+ [th](Napi::Env) { // Finalizer used to clean threads up
88
+ th->join();
89
+ delete th;
90
+ });
91
+ *th = std::thread([tsfn = std::move(tsfn), baton] {
92
+ auto callback = [](Napi::Env env, Napi::Function cb, ExitEvent *exit_event) {
93
+ cb.Call({Napi::Number::New(env, exit_event->exit_code)});
94
+ delete exit_event;
95
+ };
96
+
97
+ ExitEvent *exit_event = new ExitEvent;
98
+ // Wait for process to complete.
99
+ WaitForSingleObject(baton->hShell, INFINITE);
100
+ // Get process exit code.
101
+ GetExitCodeProcess(baton->hShell, (LPDWORD)(&exit_event->exit_code));
102
+ // 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);
108
+
109
+ auto status = tsfn.BlockingCall(exit_event, callback); // In main thread
110
+ assert(status == napi_ok);
111
+
112
+ tsfn.Release();
113
+ });
73
114
  }
74
115
 
75
- void throwNanError(const Nan::FunctionCallbackInfo<v8::Value>* info, const char* text, const bool getLastError) {
116
+ Napi::Error errorWithCode(const Napi::CallbackInfo& info, const char* text) {
76
117
  std::stringstream errorText;
77
118
  errorText << text;
78
- if (getLastError) {
79
- errorText << ", error code: " << GetLastError();
80
- }
81
- Nan::ThrowError(errorText.str().c_str());
82
- (*info).GetReturnValue().SetUndefined();
119
+ errorText << ", error code: " << GetLastError();
120
+ return Napi::Error::New(info.Env(), errorText.str());
83
121
  }
84
122
 
85
123
  // Returns a new server named pipe. It has not yet been connected.
@@ -158,32 +196,32 @@ HRESULT CreateNamedPipesAndPseudoConsole(COORD size,
158
196
  return HRESULT_FROM_WIN32(GetLastError());
159
197
  }
160
198
 
161
- static NAN_METHOD(PtyStartProcess) {
162
- Nan::HandleScope scope;
199
+ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
200
+ Napi::Env env(info.Env());
201
+ Napi::HandleScope scope(env);
163
202
 
164
- v8::Local<v8::Object> marshal;
203
+ Napi::Object marshal;
165
204
  std::wstring inName, outName;
166
205
  BOOL fSuccess = FALSE;
167
206
  std::unique_ptr<wchar_t[]> mutableCommandline;
168
207
  PROCESS_INFORMATION _piClient{};
169
208
 
170
209
  if (info.Length() != 6 ||
171
- !info[0]->IsString() ||
172
- !info[1]->IsNumber() ||
173
- !info[2]->IsNumber() ||
174
- !info[3]->IsBoolean() ||
175
- !info[4]->IsString() ||
176
- !info[5]->IsBoolean()) {
177
- Nan::ThrowError("Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor)");
178
- return;
210
+ !info[0].IsString() ||
211
+ !info[1].IsNumber() ||
212
+ !info[2].IsNumber() ||
213
+ !info[3].IsBoolean() ||
214
+ !info[4].IsString() ||
215
+ !info[5].IsBoolean()) {
216
+ throw Napi::Error::New(env, "Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor)");
179
217
  }
180
218
 
181
- const std::wstring filename(path_util::to_wstring(Nan::Utf8String(info[0])));
182
- const SHORT cols = static_cast<SHORT>(info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust());
183
- const SHORT rows = static_cast<SHORT>(info[2]->Uint32Value(Nan::GetCurrentContext()).FromJust());
184
- const bool debug = Nan::To<bool>(info[3]).FromJust();
185
- const std::wstring pipeName(path_util::to_wstring(Nan::Utf8String(info[4])));
186
- const bool inheritCursor = Nan::To<bool>(info[5]).FromJust();
219
+ const std::wstring filename(path_util::to_wstring(info[0].As<Napi::String>()));
220
+ const SHORT cols = static_cast<SHORT>(info[1].As<Napi::Number>().Uint32Value());
221
+ const SHORT rows = static_cast<SHORT>(info[2].As<Napi::Number>().Uint32Value());
222
+ const bool debug = info[3].As<Napi::Boolean>().Value();
223
+ const std::wstring pipeName(path_util::to_wstring(info[4].As<Napi::String>()));
224
+ const bool inheritCursor = info[5].As<Napi::Boolean>().Value();
187
225
 
188
226
  // use environment 'Path' variable to determine location of
189
227
  // the relative path that we have recieved (e.g cmd.exe)
@@ -195,10 +233,10 @@ static NAN_METHOD(PtyStartProcess) {
195
233
  }
196
234
 
197
235
  if (shellpath.empty() || !path_util::file_exists(shellpath)) {
198
- std::wstringstream why;
199
- why << "File not found: " << shellpath;
200
- Nan::ThrowError(path_util::from_wstring(why.str().c_str()));
201
- return;
236
+ std::string why;
237
+ why += "File not found: ";
238
+ why += path_util::wstring_to_string(shellpath);
239
+ throw Napi::Error::New(env, why);
202
240
  }
203
241
 
204
242
  HANDLE hIn, hOut;
@@ -209,67 +247,35 @@ static NAN_METHOD(PtyStartProcess) {
209
247
  SetConsoleCtrlHandler(NULL, FALSE);
210
248
 
211
249
  // Set return values
212
- marshal = Nan::New<v8::Object>();
250
+ marshal = Napi::Object::New(env);
213
251
 
214
252
  if (SUCCEEDED(hr)) {
215
253
  // We were able to instantiate a conpty
216
254
  const int ptyId = InterlockedIncrement(&ptyCounter);
217
- Nan::Set(marshal, Nan::New<v8::String>("pty").ToLocalChecked(), Nan::New<v8::Number>(ptyId));
255
+ marshal.Set("pty", Napi::Number::New(env, ptyId));
218
256
  ptyHandles.insert(ptyHandles.end(), new pty_baton(ptyId, hIn, hOut, hpc));
219
257
  } else {
220
- Nan::ThrowError("Cannot launch conpty");
221
- return;
258
+ throw Napi::Error::New(env, "Cannot launch conpty");
222
259
  }
223
260
 
224
- std::string inNameStr(path_util::from_wstring(inName.c_str()));
261
+ std::string inNameStr = path_util::wstring_to_string(inName);
225
262
  if (inNameStr.empty()) {
226
- Nan::ThrowError("Failed to initialize conpty conin");
227
- return;
263
+ throw Napi::Error::New(env, "Failed to initialize conpty conin");
228
264
  }
229
- std::string outNameStr(path_util::from_wstring(outName.c_str()));
265
+ std::string outNameStr = path_util::wstring_to_string(outName);
230
266
  if (outNameStr.empty()) {
231
- Nan::ThrowError("Failed to initialize conpty conout");
232
- return;
267
+ throw Napi::Error::New(env, "Failed to initialize conpty conout");
233
268
  }
234
269
 
235
- Nan::Set(marshal, Nan::New<v8::String>("fd").ToLocalChecked(), Nan::New<v8::Number>(-1));
236
- Nan::Set(marshal, Nan::New<v8::String>("conin").ToLocalChecked(), Nan::New<v8::String>(inNameStr).ToLocalChecked());
237
- Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(outNameStr).ToLocalChecked());
238
- info.GetReturnValue().Set(marshal);
239
- }
240
-
241
- VOID CALLBACK OnProcessExitWinEvent(
242
- _In_ PVOID context,
243
- _In_ BOOLEAN TimerOrWaitFired) {
244
- pty_baton *baton = static_cast<pty_baton*>(context);
245
-
246
- // Fire OnProcessExit
247
- uv_async_send(&baton->async);
270
+ marshal.Set("fd", Napi::Number::New(env, -1));
271
+ marshal.Set("conin", Napi::String::New(env, inNameStr));
272
+ marshal.Set("conout", Napi::String::New(env, outNameStr));
273
+ return marshal;
248
274
  }
249
275
 
250
- static void OnProcessExit(uv_async_t *async) {
251
- Nan::HandleScope scope;
252
- pty_baton *baton = static_cast<pty_baton*>(async->data);
253
-
254
- UnregisterWait(baton->hWait);
255
-
256
- // Get exit code
257
- DWORD exitCode = 0;
258
- GetExitCodeProcess(baton->hShell, &exitCode);
259
-
260
- // Call function
261
- v8::Local<v8::Value> args[1] = {
262
- Nan::New<v8::Number>(exitCode)
263
- };
264
-
265
- Nan::AsyncResource asyncResource("node-pty.callback");
266
- baton->cb.Call(1, args, &asyncResource);
267
- // Clean up
268
- baton->cb.Reset();
269
- }
270
-
271
- static NAN_METHOD(PtyConnect) {
272
- Nan::HandleScope scope;
276
+ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
277
+ Napi::Env env(info.Env());
278
+ Napi::HandleScope scope(env);
273
279
 
274
280
  // If we're working with conpty's we need to call ConnectNamedPipe here AFTER
275
281
  // the Socket has attempted to connect to the other end, then actually
@@ -279,26 +285,24 @@ static NAN_METHOD(PtyConnect) {
279
285
  BOOL fSuccess = FALSE;
280
286
 
281
287
  if (info.Length() != 5 ||
282
- !info[0]->IsNumber() ||
283
- !info[1]->IsString() ||
284
- !info[2]->IsString() ||
285
- !info[3]->IsArray() ||
286
- !info[4]->IsFunction()) {
287
- Nan::ThrowError("Usage: pty.connect(id, cmdline, cwd, env, exitCallback)");
288
- return;
288
+ !info[0].IsNumber() ||
289
+ !info[1].IsString() ||
290
+ !info[2].IsString() ||
291
+ !info[3].IsArray() ||
292
+ !info[4].IsFunction()) {
293
+ throw Napi::Error::New(env, "Usage: pty.connect(id, cmdline, cwd, env, exitCallback)");
289
294
  }
290
295
 
291
- const int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
292
- const std::wstring cmdline(path_util::to_wstring(Nan::Utf8String(info[1])));
293
- const std::wstring cwd(path_util::to_wstring(Nan::Utf8String(info[2])));
294
- const v8::Local<v8::Array> envValues = info[3].As<v8::Array>();
295
- const v8::Local<v8::Function> exitCallback = v8::Local<v8::Function>::Cast(info[4]);
296
+ const int id = info[0].As<Napi::Number>().Int32Value();
297
+ const std::wstring cmdline(path_util::to_wstring(info[1].As<Napi::String>()));
298
+ const std::wstring cwd(path_util::to_wstring(info[2].As<Napi::String>()));
299
+ const Napi::Array envValues = info[3].As<Napi::Array>();
300
+ Napi::Function exitCallback = info[4].As<Napi::Function>();
296
301
 
297
302
  // Fetch pty handle from ID and start process
298
303
  pty_baton* handle = get_pty_baton(id);
299
304
  if (!handle) {
300
- Nan::ThrowError("Invalid pty handle");
301
- return;
305
+ throw Napi::Error::New(env, "Invalid pty handle");
302
306
  }
303
307
 
304
308
  // Prepare command line
@@ -310,17 +314,17 @@ static NAN_METHOD(PtyConnect) {
310
314
  hr = StringCchCopyW(mutableCwd.get(), cwd.length() + 1, cwd.c_str());
311
315
 
312
316
  // Prepare environment
313
- std::wstring env;
317
+ std::wstring envStr;
314
318
  if (!envValues.IsEmpty()) {
315
- std::wstringstream envBlock;
316
- for(uint32_t i = 0; i < envValues->Length(); i++) {
317
- std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked())));
318
- envBlock << envValue << L'\0';
319
+ std::wstring envBlock;
320
+ for(uint32_t i = 0; i < envValues.Length(); i++) {
321
+ envBlock += path_util::to_wstring(envValues.Get(i).As<Napi::String>());
322
+ envBlock += L'\0';
319
323
  }
320
- envBlock << L'\0';
321
- env = envBlock.str();
324
+ envBlock += L'\0';
325
+ envStr = std::move(envBlock);
322
326
  }
323
- auto envV = vectorFromString(env);
327
+ std::vector<wchar_t> envV(envStr.cbegin(), envStr.cend());
324
328
  LPWSTR envArg = envV.empty() ? nullptr : envV.data();
325
329
 
326
330
  ConnectNamedPipe(handle->hIn, nullptr);
@@ -341,7 +345,7 @@ static NAN_METHOD(PtyConnect) {
341
345
 
342
346
  fSuccess = InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size);
343
347
  if (!fSuccess) {
344
- return throwNanError(&info, "InitializeProcThreadAttributeList failed", true);
348
+ throw errorWithCode(info, "InitializeProcThreadAttributeList failed");
345
349
  }
346
350
  fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList,
347
351
  0,
@@ -351,7 +355,7 @@ static NAN_METHOD(PtyConnect) {
351
355
  NULL,
352
356
  NULL);
353
357
  if (!fSuccess) {
354
- return throwNanError(&info, "UpdateProcThreadAttribute failed", true);
358
+ throw errorWithCode(info, "UpdateProcThreadAttribute failed");
355
359
  }
356
360
 
357
361
  PROCESS_INFORMATION piClient{};
@@ -368,40 +372,34 @@ static NAN_METHOD(PtyConnect) {
368
372
  &piClient // lpProcessInformation
369
373
  );
370
374
  if (!fSuccess) {
371
- return throwNanError(&info, "Cannot create process", true);
375
+ throw errorWithCode(info, "Cannot create process");
372
376
  }
373
377
 
374
378
  // Update handle
375
379
  handle->hShell = piClient.hProcess;
376
- handle->cb.Reset(exitCallback);
377
- handle->async.data = handle;
378
-
379
- // Setup OnProcessExit callback
380
- uv_async_init(uv_default_loop(), &handle->async, OnProcessExit);
381
380
 
382
- // Setup Windows wait for process exit event
383
- RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, OnProcessExitWinEvent, (PVOID)handle, INFINITE, WT_EXECUTEONLYONCE);
381
+ SetupExitCallback(env, exitCallback, handle);
384
382
 
385
383
  // Return
386
- v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
387
- Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>(piClient.dwProcessId));
388
- info.GetReturnValue().Set(marshal);
384
+ auto marshal = Napi::Object::New(env);
385
+ marshal.Set("pid", Napi::Number::New(env, piClient.dwProcessId));
386
+ return marshal;
389
387
  }
390
388
 
391
- static NAN_METHOD(PtyResize) {
392
- Nan::HandleScope scope;
389
+ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
390
+ Napi::Env env(info.Env());
391
+ Napi::HandleScope scope(env);
393
392
 
394
393
  if (info.Length() != 3 ||
395
- !info[0]->IsNumber() ||
396
- !info[1]->IsNumber() ||
397
- !info[2]->IsNumber()) {
398
- Nan::ThrowError("Usage: pty.resize(id, cols, rows)");
399
- return;
394
+ !info[0].IsNumber() ||
395
+ !info[1].IsNumber() ||
396
+ !info[2].IsNumber()) {
397
+ throw Napi::Error::New(env, "Usage: pty.resize(id, cols, rows)");
400
398
  }
401
399
 
402
- int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
403
- SHORT cols = static_cast<SHORT>(info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust());
404
- SHORT rows = static_cast<SHORT>(info[2]->Uint32Value(Nan::GetCurrentContext()).FromJust());
400
+ int id = info[0].As<Napi::Number>().Int32Value();
401
+ SHORT cols = static_cast<SHORT>(info[1].As<Napi::Number>().Uint32Value());
402
+ SHORT rows = static_cast<SHORT>(info[2].As<Napi::Number>().Uint32Value());
405
403
 
406
404
  const pty_baton* handle = get_pty_baton(id);
407
405
 
@@ -419,19 +417,19 @@ static NAN_METHOD(PtyResize) {
419
417
  }
420
418
  }
421
419
 
422
- return info.GetReturnValue().SetUndefined();
420
+ return env.Undefined();
423
421
  }
424
422
 
425
- static NAN_METHOD(PtyClear) {
426
- Nan::HandleScope scope;
423
+ static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
424
+ Napi::Env env(info.Env());
425
+ Napi::HandleScope scope(env);
427
426
 
428
427
  if (info.Length() != 1 ||
429
- !info[0]->IsNumber()) {
430
- Nan::ThrowError("Usage: pty.clear(id)");
431
- return;
428
+ !info[0].IsNumber()) {
429
+ throw Napi::Error::New(env, "Usage: pty.clear(id)");
432
430
  }
433
431
 
434
- int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
432
+ int id = info[0].As<Napi::Number>().Int32Value();
435
433
 
436
434
  const pty_baton* handle = get_pty_baton(id);
437
435
 
@@ -448,19 +446,19 @@ static NAN_METHOD(PtyClear) {
448
446
  }
449
447
  }
450
448
 
451
- return info.GetReturnValue().SetUndefined();
449
+ return env.Undefined();
452
450
  }
453
451
 
454
- static NAN_METHOD(PtyKill) {
455
- Nan::HandleScope scope;
452
+ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
453
+ Napi::Env env(info.Env());
454
+ Napi::HandleScope scope(env);
456
455
 
457
456
  if (info.Length() != 1 ||
458
- !info[0]->IsNumber()) {
459
- Nan::ThrowError("Usage: pty.kill(id)");
460
- return;
457
+ !info[0].IsNumber()) {
458
+ throw Napi::Error::New(env, "Usage: pty.kill(id)");
461
459
  }
462
460
 
463
- int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
461
+ int id = info[0].As<Napi::Number>().Int32Value();
464
462
 
465
463
  const pty_baton* handle = get_pty_baton(id);
466
464
 
@@ -476,27 +474,24 @@ static NAN_METHOD(PtyKill) {
476
474
  }
477
475
  }
478
476
 
479
- DisconnectNamedPipe(handle->hIn);
480
- DisconnectNamedPipe(handle->hOut);
481
- CloseHandle(handle->hIn);
482
- CloseHandle(handle->hOut);
483
477
  CloseHandle(handle->hShell);
478
+ assert(remove_pty_baton(id));
484
479
  }
485
480
 
486
- return info.GetReturnValue().SetUndefined();
481
+ return env.Undefined();
487
482
  }
488
483
 
489
484
  /**
490
485
  * Init
491
486
  */
492
487
 
493
- extern "C" void init(v8::Local<v8::Object> target) {
494
- Nan::HandleScope scope;
495
- Nan::SetMethod(target, "startProcess", PtyStartProcess);
496
- Nan::SetMethod(target, "connect", PtyConnect);
497
- Nan::SetMethod(target, "resize", PtyResize);
498
- Nan::SetMethod(target, "clear", PtyClear);
499
- Nan::SetMethod(target, "kill", PtyKill);
488
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
489
+ exports.Set("startProcess", Napi::Function::New(env, PtyStartProcess));
490
+ exports.Set("connect", Napi::Function::New(env, PtyConnect));
491
+ exports.Set("resize", Napi::Function::New(env, PtyResize));
492
+ exports.Set("clear", Napi::Function::New(env, PtyClear));
493
+ exports.Set("kill", Napi::Function::New(env, PtyKill));
494
+ return exports;
500
495
  };
501
496
 
502
- NODE_MODULE(pty, init);
497
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
@@ -2,23 +2,24 @@
2
2
  * Copyright (c) 2019, Microsoft Corporation (MIT License).
3
3
  */
4
4
 
5
- #include <nan.h>
5
+ #define NODE_ADDON_API_DISABLE_DEPRECATED
6
+ #include <napi.h>
6
7
  #include <windows.h>
7
8
 
8
- static NAN_METHOD(ApiConsoleProcessList) {
9
+ static Napi::Value ApiConsoleProcessList(const Napi::CallbackInfo& info) {
10
+ Napi::Env env(info.Env());
9
11
  if (info.Length() != 1 ||
10
- !info[0]->IsNumber()) {
11
- Nan::ThrowError("Usage: getConsoleProcessList(shellPid)");
12
- return;
12
+ !info[0].IsNumber()) {
13
+ throw Napi::Error::New(env, "Usage: getConsoleProcessList(shellPid)");
13
14
  }
14
15
 
15
- const DWORD pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();
16
+ const DWORD pid = info[0].As<Napi::Number>().Uint32Value();
16
17
 
17
18
  if (!FreeConsole()) {
18
- Nan::ThrowError("FreeConsole failed");
19
+ throw Napi::Error::New(env, "FreeConsole failed");
19
20
  }
20
21
  if (!AttachConsole(pid)) {
21
- Nan::ThrowError("AttachConsole failed");
22
+ throw Napi::Error::New(env, "AttachConsole failed");
22
23
  }
23
24
  auto processList = std::vector<DWORD>(64);
24
25
  auto processCount = GetConsoleProcessList(&processList[0], static_cast<DWORD>(processList.size()));
@@ -28,16 +29,16 @@ static NAN_METHOD(ApiConsoleProcessList) {
28
29
  }
29
30
  FreeConsole();
30
31
 
31
- v8::Local<v8::Array> result = Nan::New<v8::Array>();
32
+ Napi::Array result = Napi::Array::New(env);
32
33
  for (DWORD i = 0; i < processCount; i++) {
33
- Nan::Set(result, i, Nan::New<v8::Number>(processList[i]));
34
+ result.Set(i, Napi::Number::New(env, processList[i]));
34
35
  }
35
- info.GetReturnValue().Set(result);
36
+ return result;
36
37
  }
37
38
 
38
- extern "C" void init(v8::Local<v8::Object> target) {
39
- Nan::HandleScope scope;
40
- Nan::SetMethod(target, "getConsoleProcessList", ApiConsoleProcessList);
39
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
40
+ exports.Set("getConsoleProcessList", Napi::Function::New(env, ApiConsoleProcessList));
41
+ return exports;
41
42
  };
42
43
 
43
- NODE_MODULE(pty, init);
44
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
@@ -4,25 +4,29 @@
4
4
  * Copyright (c) 2018, Microsoft Corporation (MIT License).
5
5
  */
6
6
 
7
- #include <nan.h>
7
+ #include <stdexcept>
8
8
  #include <Shlwapi.h> // PathCombine
9
-
9
+ #include <Windows.h>
10
10
  #include "path_util.h"
11
11
 
12
12
  namespace path_util {
13
13
 
14
- const wchar_t* to_wstring(const Nan::Utf8String& str) {
15
- const char *bytes = *str;
16
- int sizeOfStr = MultiByteToWideChar(CP_UTF8, 0, bytes, -1, NULL, 0);
17
- if (sizeOfStr <= 0) {
18
- return L"";
14
+ std::wstring to_wstring(const Napi::String& str) {
15
+ const std::u16string & u16 = str.Utf16Value();
16
+ return std::wstring(u16.begin(), u16.end());
17
+ }
18
+
19
+ std::string wstring_to_string(const std::wstring &wide_string) {
20
+ if (wide_string.empty()) {
21
+ return "";
19
22
  }
20
- wchar_t *output = new wchar_t[sizeOfStr];
21
- int status = MultiByteToWideChar(CP_UTF8, 0, bytes, -1, output, sizeOfStr);
22
- if (status == 0) {
23
- return L"";
23
+ const auto size_needed = WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0), (int)wide_string.size(), nullptr, 0, nullptr, nullptr);
24
+ if (size_needed <= 0) {
25
+ return "";
24
26
  }
25
- return output;
27
+ std::string result(size_needed, 0);
28
+ WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0), (int)wide_string.size(), &result.at(0), size_needed, nullptr, nullptr);
29
+ return result;
26
30
  }
27
31
 
28
32
  const char* from_wstring(const wchar_t* wstr) {
@@ -7,13 +7,16 @@
7
7
  #ifndef NODE_PTY_PATH_UTIL_H_
8
8
  #define NODE_PTY_PATH_UTIL_H_
9
9
 
10
- #include <nan.h>
10
+ #define NODE_ADDON_API_DISABLE_DEPRECATED
11
+ #include <napi.h>
12
+ #include <string>
11
13
 
12
14
  #define MAX_ENV 65536
13
15
 
14
16
  namespace path_util {
15
17
 
16
- const wchar_t* to_wstring(const Nan::Utf8String& str);
18
+ std::wstring to_wstring(const Napi::String& str);
19
+ std::string wstring_to_string(const std::wstring &wide_string);
17
20
  const char* from_wstring(const wchar_t* wstr);
18
21
  bool file_exists(std::wstring filename);
19
22
  std::wstring get_shell_path(std::wstring filename);