node-pty 1.0.0 → 1.1.0-beta10

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.
@@ -4,18 +4,41 @@
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
- unsigned int sizeOfStr = MultiByteToWideChar(CP_UTF8, 0, bytes, -1, NULL, 0);
17
- wchar_t *output = new wchar_t[sizeOfStr];
18
- MultiByteToWideChar(CP_UTF8, 0, bytes, -1, output, sizeOfStr);
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 "";
22
+ }
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 "";
26
+ }
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;
30
+ }
31
+
32
+ const char* from_wstring(const wchar_t* wstr) {
33
+ int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
34
+ if (bufferSize <= 0) {
35
+ return "";
36
+ }
37
+ char *output = new char[bufferSize];
38
+ int status = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, output, bufferSize, NULL, NULL);
39
+ if (status == 0) {
40
+ return "";
41
+ }
19
42
  return output;
20
43
  }
21
44
 
@@ -7,13 +7,17 @@
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);
20
+ const char* from_wstring(const wchar_t* wstr);
17
21
  bool file_exists(std::wstring filename);
18
22
  std::wstring get_shell_path(std::wstring filename);
19
23
 
package/src/win/winpty.cc CHANGED
@@ -8,8 +8,11 @@
8
8
  * with pseudo-terminal file descriptors.
9
9
  */
10
10
 
11
+ #define NODE_ADDON_API_DISABLE_DEPRECATED
12
+ #include <napi.h>
11
13
  #include <iostream>
12
- #include <nan.h>
14
+ #include <assert.h>
15
+ #include <map>
13
16
  #include <Shlwapi.h> // PathCombine, PathIsRelative
14
17
  #include <sstream>
15
18
  #include <stdlib.h>
@@ -23,8 +26,6 @@
23
26
  /**
24
27
  * Misc
25
28
  */
26
- extern "C" void init(v8::Local<v8::Object>);
27
-
28
29
  #define WINPTY_DBG_VARIABLE TEXT("WINPTYDBG")
29
30
 
30
31
  /**
@@ -37,21 +38,25 @@ static volatile LONG ptyCounter;
37
38
  * Helpers
38
39
  */
39
40
 
40
- static winpty_t *get_pipe_handle(int handle) {
41
+ /** Keeps track of the handles created by PtyStartProcess */
42
+ static std::map<DWORD, HANDLE> createdHandles;
43
+
44
+ static winpty_t *get_pipe_handle(DWORD pid) {
41
45
  for (size_t i = 0; i < ptyHandles.size(); ++i) {
42
46
  winpty_t *ptyHandle = ptyHandles[i];
43
- int current = (int)winpty_agent_process(ptyHandle);
44
- if (current == handle) {
47
+ HANDLE current = winpty_agent_process(ptyHandle);
48
+ if (GetProcessId(current) == pid) {
45
49
  return ptyHandle;
46
50
  }
47
51
  }
48
52
  return nullptr;
49
53
  }
50
54
 
51
- static bool remove_pipe_handle(int handle) {
55
+ static bool remove_pipe_handle(DWORD pid) {
52
56
  for (size_t i = 0; i < ptyHandles.size(); ++i) {
53
57
  winpty_t *ptyHandle = ptyHandles[i];
54
- if ((int)winpty_agent_process(ptyHandle) == handle) {
58
+ HANDLE current = winpty_agent_process(ptyHandle);
59
+ if (GetProcessId(current) == pid) {
55
60
  winpty_free(ptyHandle);
56
61
  ptyHandles.erase(ptyHandles.begin() + i);
57
62
  ptyHandle = nullptr;
@@ -61,113 +66,117 @@ static bool remove_pipe_handle(int handle) {
61
66
  return false;
62
67
  }
63
68
 
64
- void throw_winpty_error(const char *generalMsg, winpty_error_ptr_t error_ptr) {
65
- std::stringstream why;
66
- std::wstring msg(winpty_error_msg(error_ptr));
67
- std::string msg_(msg.begin(), msg.end());
68
- why << generalMsg << ": " << msg_;
69
- Nan::ThrowError(why.str().c_str());
69
+ Napi::Error error_with_winpty_msg(const char *generalMsg, winpty_error_ptr_t error_ptr, Napi::Env env) {
70
+ std::string why;
71
+ why += generalMsg;
72
+ why += ": ";
73
+ why += path_util::wstring_to_string(winpty_error_msg(error_ptr));
70
74
  winpty_error_free(error_ptr);
75
+ return Napi::Error::New(env, why);
71
76
  }
72
77
 
73
- static NAN_METHOD(PtyGetExitCode) {
74
- Nan::HandleScope scope;
78
+ static Napi::Value PtyGetExitCode(const Napi::CallbackInfo& info) {
79
+ Napi::Env env(info.Env());
80
+ Napi::HandleScope scope(env);
75
81
 
76
82
  if (info.Length() != 1 ||
77
- !info[0]->IsNumber()) {
78
- Nan::ThrowError("Usage: pty.getExitCode(pidHandle)");
79
- return;
83
+ !info[0].IsNumber()) {
84
+ throw Napi::Error::New(env, "Usage: pty.getExitCode(pid)");
85
+ }
86
+
87
+ DWORD pid = info[0].As<Napi::Number>().Uint32Value();
88
+ HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
89
+ if (handle == NULL) {
90
+ return Napi::Number::New(env, -1);
80
91
  }
81
92
 
82
93
  DWORD exitCode = 0;
83
- GetExitCodeProcess((HANDLE)info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(), &exitCode);
94
+ BOOL success = GetExitCodeProcess(handle, &exitCode);
95
+ if (success == FALSE) {
96
+ exitCode = -1;
97
+ }
84
98
 
85
- info.GetReturnValue().Set(Nan::New<v8::Number>(exitCode));
99
+ CloseHandle(handle);
100
+ return Napi::Number::New(env, exitCode);
86
101
  }
87
102
 
88
- static NAN_METHOD(PtyGetProcessList) {
89
- Nan::HandleScope scope;
103
+ static Napi::Value PtyGetProcessList(const Napi::CallbackInfo& info) {
104
+ Napi::Env env(info.Env());
105
+ Napi::HandleScope scope(env);
90
106
 
91
107
  if (info.Length() != 1 ||
92
- !info[0]->IsNumber()) {
93
- Nan::ThrowError("Usage: pty.getProcessList(pid)");
94
- return;
108
+ !info[0].IsNumber()) {
109
+ throw Napi::Error::New(env, "Usage: pty.getProcessList(pid)");
95
110
  }
96
111
 
97
- int pid = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
98
-
112
+ DWORD pid = info[0].As<Napi::Number>().Uint32Value();
99
113
  winpty_t *pc = get_pipe_handle(pid);
100
114
  if (pc == nullptr) {
101
- info.GetReturnValue().Set(Nan::New<v8::Array>(0));
102
- return;
115
+ return Napi::Number::New(env, 0);
103
116
  }
104
117
  int processList[64];
105
118
  const int processCount = 64;
106
119
  int actualCount = winpty_get_console_process_list(pc, processList, processCount, nullptr);
107
-
108
- v8::Local<v8::Array> result = Nan::New<v8::Array>(actualCount);
109
- for (uint32_t i = 0; i < actualCount; i++) {
110
- Nan::Set(result, i, Nan::New<v8::Number>(processList[i]));
120
+ if (actualCount <= 0) {
121
+ return Napi::Number::New(env, 0);
122
+ }
123
+ Napi::Array result = Napi::Array::New(env, actualCount);
124
+ for (int i = 0; i < actualCount; i++) {
125
+ result.Set(i, Napi::Number::New(env, processList[i]));
111
126
  }
112
- info.GetReturnValue().Set(result);
127
+ return result;
113
128
  }
114
129
 
115
- static NAN_METHOD(PtyStartProcess) {
116
- Nan::HandleScope scope;
130
+ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
131
+ Napi::Env env(info.Env());
132
+ Napi::HandleScope scope(env);
117
133
 
118
134
  if (info.Length() != 7 ||
119
- !info[0]->IsString() ||
120
- !info[1]->IsString() ||
121
- !info[2]->IsArray() ||
122
- !info[3]->IsString() ||
123
- !info[4]->IsNumber() ||
124
- !info[5]->IsNumber() ||
125
- !info[6]->IsBoolean()) {
126
- Nan::ThrowError("Usage: pty.startProcess(file, cmdline, env, cwd, cols, rows, debug)");
127
- return;
135
+ !info[0].IsString() ||
136
+ !info[1].IsString() ||
137
+ !info[2].IsArray() ||
138
+ !info[3].IsString() ||
139
+ !info[4].IsNumber() ||
140
+ !info[5].IsNumber() ||
141
+ !info[6].IsBoolean()) {
142
+ throw Napi::Error::New(env, "Usage: pty.startProcess(file, cmdline, env, cwd, cols, rows, debug)");
128
143
  }
129
144
 
130
- std::stringstream why;
131
-
132
- const wchar_t *filename = path_util::to_wstring(Nan::Utf8String(info[0]));
133
- const wchar_t *cmdline = path_util::to_wstring(Nan::Utf8String(info[1]));
134
- const wchar_t *cwd = path_util::to_wstring(Nan::Utf8String(info[3]));
145
+ std::wstring filename(path_util::to_wstring(info[0].As<Napi::String>()));
146
+ std::wstring cmdline(path_util::to_wstring(info[1].As<Napi::String>()));
147
+ std::wstring cwd(path_util::to_wstring(info[3].As<Napi::String>()));
135
148
 
136
149
  // create environment block
137
- std::wstring env;
138
- const v8::Local<v8::Array> envValues = v8::Local<v8::Array>::Cast(info[2]);
150
+ std::wstring envStr;
151
+ const Napi::Array envValues = info[2].As<Napi::Array>();
139
152
  if (!envValues.IsEmpty()) {
140
-
141
- std::wstringstream envBlock;
142
-
143
- for(uint32_t i = 0; i < envValues->Length(); i++) {
144
- std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked())));
145
- envBlock << envValue << L'\0';
153
+ std::wstring envBlock;
154
+ for(uint32_t i = 0; i < envValues.Length(); i++) {
155
+ envBlock += path_util::to_wstring(envValues.Get(i).As<Napi::String>());
156
+ envBlock += L'\0';
146
157
  }
147
-
148
- env = envBlock.str();
158
+ envStr = std::move(envBlock);
149
159
  }
150
160
 
151
161
  // use environment 'Path' variable to determine location of
152
162
  // the relative path that we have recieved (e.g cmd.exe)
153
163
  std::wstring shellpath;
154
- if (::PathIsRelativeW(filename)) {
164
+ if (::PathIsRelativeW(filename.c_str())) {
155
165
  shellpath = path_util::get_shell_path(filename);
156
166
  } else {
157
167
  shellpath = filename;
158
168
  }
159
169
 
160
- std::string shellpath_(shellpath.begin(), shellpath.end());
161
-
162
170
  if (shellpath.empty() || !path_util::file_exists(shellpath)) {
163
- why << "File not found: " << shellpath_;
164
- Nan::ThrowError(why.str().c_str());
165
- goto cleanup;
171
+ std::string why;
172
+ why += "File not found: ";
173
+ why += path_util::wstring_to_string(shellpath);
174
+ throw Napi::Error::New(env, why);
166
175
  }
167
176
 
168
- int cols = info[4]->Int32Value(Nan::GetCurrentContext()).FromJust();
169
- int rows = info[5]->Int32Value(Nan::GetCurrentContext()).FromJust();
170
- bool debug = Nan::To<bool>(info[6]).FromJust();
177
+ int cols = info[4].As<Napi::Number>().Int32Value();
178
+ int rows = info[5].As<Napi::Number>().Int32Value();
179
+ bool debug = info[6].As<Napi::Boolean>().Value();
171
180
 
172
181
  // Enable/disable debugging
173
182
  SetEnvironmentVariable(WINPTY_DBG_VARIABLE, debug ? "1" : NULL); // NULL = deletes variable
@@ -176,8 +185,7 @@ static NAN_METHOD(PtyStartProcess) {
176
185
  winpty_error_ptr_t error_ptr = nullptr;
177
186
  winpty_config_t* winpty_config = winpty_config_new(0, &error_ptr);
178
187
  if (winpty_config == nullptr) {
179
- throw_winpty_error("Error creating WinPTY config", error_ptr);
180
- goto cleanup;
188
+ throw error_with_winpty_msg("Error creating WinPTY config", error_ptr, env);
181
189
  }
182
190
  winpty_error_free(error_ptr);
183
191
 
@@ -188,19 +196,15 @@ static NAN_METHOD(PtyStartProcess) {
188
196
  winpty_t *pc = winpty_open(winpty_config, &error_ptr);
189
197
  winpty_config_free(winpty_config);
190
198
  if (pc == nullptr) {
191
- throw_winpty_error("Error launching WinPTY agent", error_ptr);
192
- goto cleanup;
199
+ throw error_with_winpty_msg("Error launching WinPTY agent", error_ptr, env);
193
200
  }
194
201
  winpty_error_free(error_ptr);
195
202
 
196
- // Save pty struct for later use
197
- ptyHandles.insert(ptyHandles.end(), pc);
198
-
199
203
  // Create winpty spawn config
200
- winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline, cwd, env.c_str(), &error_ptr);
204
+ winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline.c_str(), cwd.c_str(), envStr.c_str(), &error_ptr);
201
205
  if (config == nullptr) {
202
- throw_winpty_error("Error creating WinPTY spawn config", error_ptr);
203
- goto cleanup;
206
+ winpty_free(pc);
207
+ throw error_with_winpty_msg("Error creating WinPTY spawn config", error_ptr, env);
204
208
  }
205
209
  winpty_error_free(error_ptr);
206
210
 
@@ -209,104 +213,121 @@ static NAN_METHOD(PtyStartProcess) {
209
213
  BOOL spawnSuccess = winpty_spawn(pc, config, &handle, nullptr, nullptr, &error_ptr);
210
214
  winpty_spawn_config_free(config);
211
215
  if (!spawnSuccess) {
212
- throw_winpty_error("Unable to start terminal process", error_ptr);
213
- goto cleanup;
216
+ if (handle) {
217
+ CloseHandle(handle);
218
+ }
219
+ winpty_free(pc);
220
+ throw error_with_winpty_msg("Unable to start terminal process", error_ptr, env);
214
221
  }
215
222
  winpty_error_free(error_ptr);
216
223
 
217
- // Set return values
218
- v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
219
- Nan::Set(marshal, Nan::New<v8::String>("innerPid").ToLocalChecked(), Nan::New<v8::Number>((int)GetProcessId(handle)));
220
- Nan::Set(marshal, Nan::New<v8::String>("innerPidHandle").ToLocalChecked(), Nan::New<v8::Number>((int)handle));
221
- Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>((int)winpty_agent_process(pc)));
222
- Nan::Set(marshal, Nan::New<v8::String>("pty").ToLocalChecked(), Nan::New<v8::Number>(InterlockedIncrement(&ptyCounter)));
223
- Nan::Set(marshal, Nan::New<v8::String>("fd").ToLocalChecked(), Nan::New<v8::Number>(-1));
224
- {
225
- LPCWSTR coninPipeName = winpty_conin_name(pc);
226
- std::wstring coninPipeNameWStr(coninPipeName);
227
- std::string coninPipeNameStr(coninPipeNameWStr.begin(), coninPipeNameWStr.end());
228
- Nan::Set(marshal, Nan::New<v8::String>("conin").ToLocalChecked(), Nan::New<v8::String>(coninPipeNameStr).ToLocalChecked());
229
- LPCWSTR conoutPipeName = winpty_conout_name(pc);
230
- std::wstring conoutPipeNameWStr(conoutPipeName);
231
- std::string conoutPipeNameStr(conoutPipeNameWStr.begin(), conoutPipeNameWStr.end());
232
- Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(conoutPipeNameStr).ToLocalChecked());
224
+ LPCWSTR coninPipeName = winpty_conin_name(pc);
225
+ std::string coninPipeNameStr(path_util::from_wstring(coninPipeName));
226
+ if (coninPipeNameStr.empty()) {
227
+ CloseHandle(handle);
228
+ winpty_free(pc);
229
+ throw Napi::Error::New(env, "Failed to initialize winpty conin");
233
230
  }
234
- info.GetReturnValue().Set(marshal);
235
231
 
236
- goto cleanup;
232
+ LPCWSTR conoutPipeName = winpty_conout_name(pc);
233
+ std::string conoutPipeNameStr(path_util::from_wstring(conoutPipeName));
234
+ if (conoutPipeNameStr.empty()) {
235
+ CloseHandle(handle);
236
+ winpty_free(pc);
237
+ throw Napi::Error::New(env, "Failed to initialize winpty conout");
238
+ }
237
239
 
238
- cleanup:
239
- delete filename;
240
- delete cmdline;
241
- delete cwd;
240
+ DWORD innerPid = GetProcessId(handle);
241
+ if (createdHandles[innerPid]) {
242
+ CloseHandle(handle);
243
+ winpty_free(pc);
244
+ std::stringstream why;
245
+ why << "There is already a process with innerPid " << innerPid;
246
+ throw Napi::Error::New(env, why.str());
247
+ }
248
+ createdHandles[innerPid] = handle;
249
+
250
+ // Save pty struct for later use
251
+ ptyHandles.push_back(pc);
252
+
253
+ DWORD pid = GetProcessId(winpty_agent_process(pc));
254
+ Napi::Object marshal = Napi::Object::New(env);
255
+ marshal.Set("innerPid", Napi::Number::New(env, (int)innerPid));
256
+ marshal.Set("pid", Napi::Number::New(env, (int)pid));
257
+ marshal.Set("pty", Napi::Number::New(env, InterlockedIncrement(&ptyCounter)));
258
+ marshal.Set("fd", Napi::Number::New(env, -1));
259
+ marshal.Set("conin", Napi::String::New(env, coninPipeNameStr));
260
+ marshal.Set("conout", Napi::String::New(env, conoutPipeNameStr));
261
+
262
+ return marshal;
242
263
  }
243
264
 
244
- static NAN_METHOD(PtyResize) {
245
- Nan::HandleScope scope;
265
+ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
266
+ Napi::Env env(info.Env());
267
+ Napi::HandleScope scope(env);
246
268
 
247
269
  if (info.Length() != 3 ||
248
- !info[0]->IsNumber() ||
249
- !info[1]->IsNumber() ||
250
- !info[2]->IsNumber()) {
251
- Nan::ThrowError("Usage: pty.resize(pid, cols, rows)");
252
- return;
270
+ !info[0].IsNumber() ||
271
+ !info[1].IsNumber() ||
272
+ !info[2].IsNumber()) {
273
+ throw Napi::Error::New(env, "Usage: pty.resize(pid, cols, rows)");
253
274
  }
254
275
 
255
- int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
256
- int cols = info[1]->Int32Value(Nan::GetCurrentContext()).FromJust();
257
- int rows = info[2]->Int32Value(Nan::GetCurrentContext()).FromJust();
276
+ DWORD pid = info[0].As<Napi::Number>().Uint32Value();
277
+ int cols = info[1].As<Napi::Number>().Int32Value();
278
+ int rows = info[2].As<Napi::Number>().Int32Value();
258
279
 
259
- winpty_t *pc = get_pipe_handle(handle);
280
+ winpty_t *pc = get_pipe_handle(pid);
260
281
 
261
282
  if (pc == nullptr) {
262
- Nan::ThrowError("The pty doesn't appear to exist");
263
- return;
283
+ throw Napi::Error::New(env, "The pty doesn't appear to exist");
264
284
  }
265
285
  BOOL success = winpty_set_size(pc, cols, rows, nullptr);
266
286
  if (!success) {
267
- Nan::ThrowError("The pty could not be resized");
268
- return;
287
+ throw Napi::Error::New(env, "The pty could not be resized");
269
288
  }
270
289
 
271
- return info.GetReturnValue().SetUndefined();
290
+ return env.Undefined();
272
291
  }
273
292
 
274
- static NAN_METHOD(PtyKill) {
275
- Nan::HandleScope scope;
293
+ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
294
+ Napi::Env env(info.Env());
295
+ Napi::HandleScope scope(env);
276
296
 
277
297
  if (info.Length() != 2 ||
278
- !info[0]->IsNumber() ||
279
- !info[1]->IsNumber()) {
280
- Nan::ThrowError("Usage: pty.kill(pid, innerPidHandle)");
281
- return;
298
+ !info[0].IsNumber() ||
299
+ !info[1].IsNumber()) {
300
+ throw Napi::Error::New(env, "Usage: pty.kill(pid, innerPid)");
282
301
  }
283
302
 
284
- int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
285
- HANDLE innerPidHandle = (HANDLE)info[1]->Int32Value(Nan::GetCurrentContext()).FromJust();
303
+ DWORD pid = info[0].As<Napi::Number>().Uint32Value();
304
+ DWORD innerPid = info[1].As<Napi::Number>().Uint32Value();
286
305
 
287
- winpty_t *pc = get_pipe_handle(handle);
306
+ winpty_t *pc = get_pipe_handle(pid);
288
307
  if (pc == nullptr) {
289
- Nan::ThrowError("Pty seems to have been killed already");
290
- return;
308
+ throw Napi::Error::New(env, "Pty seems to have been killed already");
291
309
  }
292
310
 
293
- assert(remove_pipe_handle(handle));
311
+ assert(remove_pipe_handle(pid));
312
+
313
+ HANDLE innerPidHandle = createdHandles[innerPid];
314
+ createdHandles.erase(innerPid);
294
315
  CloseHandle(innerPidHandle);
295
316
 
296
- return info.GetReturnValue().SetUndefined();
317
+ return env.Undefined();
297
318
  }
298
319
 
299
320
  /**
300
321
  * Init
301
322
  */
302
323
 
303
- extern "C" void init(v8::Local<v8::Object> target) {
304
- Nan::HandleScope scope;
305
- Nan::SetMethod(target, "startProcess", PtyStartProcess);
306
- Nan::SetMethod(target, "resize", PtyResize);
307
- Nan::SetMethod(target, "kill", PtyKill);
308
- Nan::SetMethod(target, "getExitCode", PtyGetExitCode);
309
- Nan::SetMethod(target, "getProcessList", PtyGetProcessList);
324
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
325
+ exports.Set("startProcess", Napi::Function::New(env, PtyStartProcess));
326
+ exports.Set("resize", Napi::Function::New(env, PtyResize));
327
+ exports.Set("kill", Napi::Function::New(env, PtyKill));
328
+ exports.Set("getExitCode", Napi::Function::New(env, PtyGetExitCode));
329
+ exports.Set("getProcessList", Napi::Function::New(env, PtyGetProcessList));
330
+ return exports;
310
331
  };
311
332
 
312
- NODE_MODULE(pty, init);
333
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
@@ -31,7 +31,6 @@ export class WindowsPtyAgent {
31
31
  private _outSocket: Socket;
32
32
  private _pid: number = 0;
33
33
  private _innerPid: number = 0;
34
- private _innerPidHandle: number = 0;
35
34
  private _closeTimeout: NodeJS.Timer | undefined;
36
35
  private _exitCode: number | undefined;
37
36
  private _conoutSocketWorker: ConoutConnection;
@@ -105,7 +104,6 @@ export class WindowsPtyAgent {
105
104
  term = (this._ptyNative as IWinptyNative).startProcess(file, commandLine, env, cwd, cols, rows, debug);
106
105
  this._pid = (term as IWinptyProcess).pid;
107
106
  this._innerPid = (term as IWinptyProcess).innerPid;
108
- this._innerPidHandle = (term as IWinptyProcess).innerPidHandle;
109
107
  }
110
108
 
111
109
  // Not available on windows.
@@ -174,14 +172,14 @@ export class WindowsPtyAgent {
174
172
  (this._ptyNative as IConptyNative).kill(this._pty);
175
173
  });
176
174
  } else {
177
- (this._ptyNative as IWinptyNative).kill(this._pid, this._innerPidHandle);
178
- // Since pty.kill closes the handle it will kill most processes by itself
179
- // and process IDs can be reused as soon as all handles to them are
180
- // dropped, we want to immediately kill the entire console process list.
175
+ // Because pty.kill closes the handle, it will kill most processes by itself.
176
+ // Process IDs can be reused as soon as all handles to them are
177
+ // dropped, so we want to immediately kill the entire console process list.
181
178
  // If we do not force kill all processes here, node servers in particular
182
179
  // seem to become detached and remain running (see
183
180
  // Microsoft/vscode#26807).
184
181
  const processList: number[] = (this._ptyNative as IWinptyNative).getProcessList(this._pid);
182
+ (this._ptyNative as IWinptyNative).kill(this._pid, this._innerPid);
185
183
  processList.forEach(pid => {
186
184
  try {
187
185
  process.kill(pid);
@@ -212,7 +210,8 @@ export class WindowsPtyAgent {
212
210
  if (this._useConpty) {
213
211
  return this._exitCode;
214
212
  }
215
- return (this._ptyNative as IWinptyNative).getExitCode(this._innerPidHandle);
213
+ const winptyExitCode = (this._ptyNative as IWinptyNative).getExitCode(this._innerPid);
214
+ return winptyExitCode === -1 ? undefined : winptyExitCode;
216
215
  }
217
216
 
218
217
  private _getWindowsBuildNumber(): number {