node-pty 1.1.0-beta1 → 1.1.0-beta11
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/README.md +5 -5
- package/binding.gyp +4 -15
- package/lib/unixTerminal.js +2 -1
- package/lib/unixTerminal.js.map +1 -1
- package/lib/unixTerminal.test.js +23 -0
- package/lib/unixTerminal.test.js.map +1 -1
- package/package.json +3 -2
- package/src/native.d.ts +1 -2
- package/src/unix/pty.cc +258 -289
- package/src/unixTerminal.test.ts +35 -0
- package/src/unixTerminal.ts +2 -1
- package/src/win/conpty.cc +182 -174
- package/src/win/conpty_console_list.cc +16 -15
- package/src/win/path_util.cc +16 -12
- package/src/win/path_util.h +5 -2
- package/src/win/winpty.cc +108 -151
package/src/unixTerminal.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import * as tty from 'tty';
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import { constants } from 'os';
|
|
13
13
|
import { pollUntil } from './testUtils.test';
|
|
14
|
+
import { pid } from 'process';
|
|
14
15
|
|
|
15
16
|
const FIXTURES_PATH = path.normalize(path.join(__dirname, '..', 'fixtures', 'utf8-character.txt'));
|
|
16
17
|
|
|
@@ -262,6 +263,40 @@ if (process.platform !== 'win32') {
|
|
|
262
263
|
term.on('exit', () => done());
|
|
263
264
|
term.destroy();
|
|
264
265
|
});
|
|
266
|
+
it('should return the name of the sub process', (done) => {
|
|
267
|
+
const data = `
|
|
268
|
+
var pty = require('./lib/index');
|
|
269
|
+
var ptyProcess = pty.spawn('zsh', ['-c', 'python3'], {
|
|
270
|
+
env: process.env
|
|
271
|
+
});
|
|
272
|
+
ptyProcess.on('data', function (data) {
|
|
273
|
+
if (ptyProcess.process === 'Python') {
|
|
274
|
+
console.log('title', ptyProcess.process);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
setTimeout(() => null, 500);
|
|
278
|
+
console.log('ready', ptyProcess.pid);
|
|
279
|
+
`;
|
|
280
|
+
const p = cp.spawn('node', ['-e', data]);
|
|
281
|
+
let sub = '';
|
|
282
|
+
let pid = '';
|
|
283
|
+
p.stdout.on('data', (data) => {
|
|
284
|
+
if (!data.toString().indexOf('title')) {
|
|
285
|
+
sub = data.toString().split(' ')[1].slice(0, -1);
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
process.kill(parseInt(pid), 'SIGINT');
|
|
288
|
+
p.kill('SIGINT');
|
|
289
|
+
}, 200);
|
|
290
|
+
} else if (!data.toString().indexOf('ready')) {
|
|
291
|
+
pid = data.toString().split(' ')[1].slice(0, -1);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
p.on('exit', () => {
|
|
295
|
+
assert.notStrictEqual(pid, '');
|
|
296
|
+
assert.strictEqual(sub, 'Python');
|
|
297
|
+
done();
|
|
298
|
+
});
|
|
299
|
+
});
|
|
265
300
|
it('should close on exec', (done) => {
|
|
266
301
|
const data = `
|
|
267
302
|
var pty = require('./lib/index');
|
package/src/unixTerminal.ts
CHANGED
|
@@ -263,7 +263,8 @@ export class UnixTerminal extends Terminal {
|
|
|
263
263
|
*/
|
|
264
264
|
public get process(): string {
|
|
265
265
|
if (process.platform === 'darwin') {
|
|
266
|
-
|
|
266
|
+
const title = pty.process(this._fd);
|
|
267
|
+
return (title !== 'kernel_task' ) ? title : this._file;
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
return pty.process(this._fd, this._pty) || this._file;
|
package/src/win/conpty.cc
CHANGED
|
@@ -8,24 +8,20 @@
|
|
|
8
8
|
* with pseudo-terminal file descriptors.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
#
|
|
18
|
-
#include <
|
|
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,78 @@ static pty_baton* get_pty_baton(int id) {
|
|
|
67
59
|
return nullptr;
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
|
|
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
|
+
switch (status) {
|
|
111
|
+
case napi_closing:
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case napi_queue_full:
|
|
115
|
+
Napi::Error::Fatal("SetupExitCallback", "Queue was full");
|
|
116
|
+
|
|
117
|
+
case napi_ok:
|
|
118
|
+
if (tsfn.Release() != napi_ok) {
|
|
119
|
+
Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.Release() failed");
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
|
|
123
|
+
default:
|
|
124
|
+
Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.BlockingCall() failed");
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Napi::Error errorWithCode(const Napi::CallbackInfo& info, const char* text) {
|
|
76
130
|
std::stringstream errorText;
|
|
77
131
|
errorText << text;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
Nan::ThrowError(errorText.str().c_str());
|
|
82
|
-
(*info).GetReturnValue().SetUndefined();
|
|
132
|
+
errorText << ", error code: " << GetLastError();
|
|
133
|
+
return Napi::Error::New(info.Env(), errorText.str());
|
|
83
134
|
}
|
|
84
135
|
|
|
85
136
|
// Returns a new server named pipe. It has not yet been connected.
|
|
@@ -158,32 +209,32 @@ HRESULT CreateNamedPipesAndPseudoConsole(COORD size,
|
|
|
158
209
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
159
210
|
}
|
|
160
211
|
|
|
161
|
-
static
|
|
162
|
-
|
|
212
|
+
static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
|
|
213
|
+
Napi::Env env(info.Env());
|
|
214
|
+
Napi::HandleScope scope(env);
|
|
163
215
|
|
|
164
|
-
|
|
216
|
+
Napi::Object marshal;
|
|
165
217
|
std::wstring inName, outName;
|
|
166
218
|
BOOL fSuccess = FALSE;
|
|
167
219
|
std::unique_ptr<wchar_t[]> mutableCommandline;
|
|
168
220
|
PROCESS_INFORMATION _piClient{};
|
|
169
221
|
|
|
170
222
|
if (info.Length() != 6 ||
|
|
171
|
-
!info[0]
|
|
172
|
-
!info[1]
|
|
173
|
-
!info[2]
|
|
174
|
-
!info[3]
|
|
175
|
-
!info[4]
|
|
176
|
-
!info[5]
|
|
177
|
-
|
|
178
|
-
return;
|
|
223
|
+
!info[0].IsString() ||
|
|
224
|
+
!info[1].IsNumber() ||
|
|
225
|
+
!info[2].IsNumber() ||
|
|
226
|
+
!info[3].IsBoolean() ||
|
|
227
|
+
!info[4].IsString() ||
|
|
228
|
+
!info[5].IsBoolean()) {
|
|
229
|
+
throw Napi::Error::New(env, "Usage: pty.startProcess(file, cols, rows, debug, pipeName, inheritCursor)");
|
|
179
230
|
}
|
|
180
231
|
|
|
181
|
-
const std::wstring filename(path_util::to_wstring(
|
|
182
|
-
const SHORT cols = static_cast<SHORT>(info[1]
|
|
183
|
-
const SHORT rows = static_cast<SHORT>(info[2]
|
|
184
|
-
const bool debug =
|
|
185
|
-
const std::wstring pipeName(path_util::to_wstring(
|
|
186
|
-
const bool inheritCursor =
|
|
232
|
+
const std::wstring filename(path_util::to_wstring(info[0].As<Napi::String>()));
|
|
233
|
+
const SHORT cols = static_cast<SHORT>(info[1].As<Napi::Number>().Uint32Value());
|
|
234
|
+
const SHORT rows = static_cast<SHORT>(info[2].As<Napi::Number>().Uint32Value());
|
|
235
|
+
const bool debug = info[3].As<Napi::Boolean>().Value();
|
|
236
|
+
const std::wstring pipeName(path_util::to_wstring(info[4].As<Napi::String>()));
|
|
237
|
+
const bool inheritCursor = info[5].As<Napi::Boolean>().Value();
|
|
187
238
|
|
|
188
239
|
// use environment 'Path' variable to determine location of
|
|
189
240
|
// the relative path that we have recieved (e.g cmd.exe)
|
|
@@ -195,10 +246,10 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
195
246
|
}
|
|
196
247
|
|
|
197
248
|
if (shellpath.empty() || !path_util::file_exists(shellpath)) {
|
|
198
|
-
std::
|
|
199
|
-
why
|
|
200
|
-
|
|
201
|
-
|
|
249
|
+
std::string why;
|
|
250
|
+
why += "File not found: ";
|
|
251
|
+
why += path_util::wstring_to_string(shellpath);
|
|
252
|
+
throw Napi::Error::New(env, why);
|
|
202
253
|
}
|
|
203
254
|
|
|
204
255
|
HANDLE hIn, hOut;
|
|
@@ -209,67 +260,35 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
209
260
|
SetConsoleCtrlHandler(NULL, FALSE);
|
|
210
261
|
|
|
211
262
|
// Set return values
|
|
212
|
-
marshal =
|
|
263
|
+
marshal = Napi::Object::New(env);
|
|
213
264
|
|
|
214
265
|
if (SUCCEEDED(hr)) {
|
|
215
266
|
// We were able to instantiate a conpty
|
|
216
267
|
const int ptyId = InterlockedIncrement(&ptyCounter);
|
|
217
|
-
|
|
268
|
+
marshal.Set("pty", Napi::Number::New(env, ptyId));
|
|
218
269
|
ptyHandles.insert(ptyHandles.end(), new pty_baton(ptyId, hIn, hOut, hpc));
|
|
219
270
|
} else {
|
|
220
|
-
|
|
221
|
-
return;
|
|
271
|
+
throw Napi::Error::New(env, "Cannot launch conpty");
|
|
222
272
|
}
|
|
223
273
|
|
|
224
|
-
std::string inNameStr
|
|
274
|
+
std::string inNameStr = path_util::wstring_to_string(inName);
|
|
225
275
|
if (inNameStr.empty()) {
|
|
226
|
-
|
|
227
|
-
return;
|
|
276
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conin");
|
|
228
277
|
}
|
|
229
|
-
std::string outNameStr
|
|
278
|
+
std::string outNameStr = path_util::wstring_to_string(outName);
|
|
230
279
|
if (outNameStr.empty()) {
|
|
231
|
-
|
|
232
|
-
return;
|
|
280
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conout");
|
|
233
281
|
}
|
|
234
282
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
283
|
+
marshal.Set("fd", Napi::Number::New(env, -1));
|
|
284
|
+
marshal.Set("conin", Napi::String::New(env, inNameStr));
|
|
285
|
+
marshal.Set("conout", Napi::String::New(env, outNameStr));
|
|
286
|
+
return marshal;
|
|
239
287
|
}
|
|
240
288
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
pty_baton *baton = static_cast<pty_baton*>(context);
|
|
245
|
-
|
|
246
|
-
// Fire OnProcessExit
|
|
247
|
-
uv_async_send(&baton->async);
|
|
248
|
-
}
|
|
249
|
-
|
|
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;
|
|
289
|
+
static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
|
|
290
|
+
Napi::Env env(info.Env());
|
|
291
|
+
Napi::HandleScope scope(env);
|
|
273
292
|
|
|
274
293
|
// If we're working with conpty's we need to call ConnectNamedPipe here AFTER
|
|
275
294
|
// the Socket has attempted to connect to the other end, then actually
|
|
@@ -279,26 +298,24 @@ static NAN_METHOD(PtyConnect) {
|
|
|
279
298
|
BOOL fSuccess = FALSE;
|
|
280
299
|
|
|
281
300
|
if (info.Length() != 5 ||
|
|
282
|
-
!info[0]
|
|
283
|
-
!info[1]
|
|
284
|
-
!info[2]
|
|
285
|
-
!info[3]
|
|
286
|
-
!info[4]
|
|
287
|
-
|
|
288
|
-
return;
|
|
301
|
+
!info[0].IsNumber() ||
|
|
302
|
+
!info[1].IsString() ||
|
|
303
|
+
!info[2].IsString() ||
|
|
304
|
+
!info[3].IsArray() ||
|
|
305
|
+
!info[4].IsFunction()) {
|
|
306
|
+
throw Napi::Error::New(env, "Usage: pty.connect(id, cmdline, cwd, env, exitCallback)");
|
|
289
307
|
}
|
|
290
308
|
|
|
291
|
-
const int id = info[0]
|
|
292
|
-
const std::wstring cmdline(path_util::to_wstring(
|
|
293
|
-
const std::wstring cwd(path_util::to_wstring(
|
|
294
|
-
const
|
|
295
|
-
|
|
309
|
+
const int id = info[0].As<Napi::Number>().Int32Value();
|
|
310
|
+
const std::wstring cmdline(path_util::to_wstring(info[1].As<Napi::String>()));
|
|
311
|
+
const std::wstring cwd(path_util::to_wstring(info[2].As<Napi::String>()));
|
|
312
|
+
const Napi::Array envValues = info[3].As<Napi::Array>();
|
|
313
|
+
Napi::Function exitCallback = info[4].As<Napi::Function>();
|
|
296
314
|
|
|
297
315
|
// Fetch pty handle from ID and start process
|
|
298
316
|
pty_baton* handle = get_pty_baton(id);
|
|
299
317
|
if (!handle) {
|
|
300
|
-
|
|
301
|
-
return;
|
|
318
|
+
throw Napi::Error::New(env, "Invalid pty handle");
|
|
302
319
|
}
|
|
303
320
|
|
|
304
321
|
// Prepare command line
|
|
@@ -310,17 +327,17 @@ static NAN_METHOD(PtyConnect) {
|
|
|
310
327
|
hr = StringCchCopyW(mutableCwd.get(), cwd.length() + 1, cwd.c_str());
|
|
311
328
|
|
|
312
329
|
// Prepare environment
|
|
313
|
-
std::wstring
|
|
330
|
+
std::wstring envStr;
|
|
314
331
|
if (!envValues.IsEmpty()) {
|
|
315
|
-
std::
|
|
316
|
-
for(uint32_t i = 0; i < envValues
|
|
317
|
-
|
|
318
|
-
envBlock
|
|
332
|
+
std::wstring envBlock;
|
|
333
|
+
for(uint32_t i = 0; i < envValues.Length(); i++) {
|
|
334
|
+
envBlock += path_util::to_wstring(envValues.Get(i).As<Napi::String>());
|
|
335
|
+
envBlock += L'\0';
|
|
319
336
|
}
|
|
320
|
-
envBlock
|
|
321
|
-
|
|
337
|
+
envBlock += L'\0';
|
|
338
|
+
envStr = std::move(envBlock);
|
|
322
339
|
}
|
|
323
|
-
|
|
340
|
+
std::vector<wchar_t> envV(envStr.cbegin(), envStr.cend());
|
|
324
341
|
LPWSTR envArg = envV.empty() ? nullptr : envV.data();
|
|
325
342
|
|
|
326
343
|
ConnectNamedPipe(handle->hIn, nullptr);
|
|
@@ -341,7 +358,7 @@ static NAN_METHOD(PtyConnect) {
|
|
|
341
358
|
|
|
342
359
|
fSuccess = InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size);
|
|
343
360
|
if (!fSuccess) {
|
|
344
|
-
|
|
361
|
+
throw errorWithCode(info, "InitializeProcThreadAttributeList failed");
|
|
345
362
|
}
|
|
346
363
|
fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList,
|
|
347
364
|
0,
|
|
@@ -351,7 +368,7 @@ static NAN_METHOD(PtyConnect) {
|
|
|
351
368
|
NULL,
|
|
352
369
|
NULL);
|
|
353
370
|
if (!fSuccess) {
|
|
354
|
-
|
|
371
|
+
throw errorWithCode(info, "UpdateProcThreadAttribute failed");
|
|
355
372
|
}
|
|
356
373
|
|
|
357
374
|
PROCESS_INFORMATION piClient{};
|
|
@@ -368,40 +385,34 @@ static NAN_METHOD(PtyConnect) {
|
|
|
368
385
|
&piClient // lpProcessInformation
|
|
369
386
|
);
|
|
370
387
|
if (!fSuccess) {
|
|
371
|
-
|
|
388
|
+
throw errorWithCode(info, "Cannot create process");
|
|
372
389
|
}
|
|
373
390
|
|
|
374
391
|
// Update handle
|
|
375
392
|
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
393
|
|
|
382
|
-
|
|
383
|
-
RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, OnProcessExitWinEvent, (PVOID)handle, INFINITE, WT_EXECUTEONLYONCE);
|
|
394
|
+
SetupExitCallback(env, exitCallback, handle);
|
|
384
395
|
|
|
385
396
|
// Return
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
397
|
+
auto marshal = Napi::Object::New(env);
|
|
398
|
+
marshal.Set("pid", Napi::Number::New(env, piClient.dwProcessId));
|
|
399
|
+
return marshal;
|
|
389
400
|
}
|
|
390
401
|
|
|
391
|
-
static
|
|
392
|
-
|
|
402
|
+
static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
403
|
+
Napi::Env env(info.Env());
|
|
404
|
+
Napi::HandleScope scope(env);
|
|
393
405
|
|
|
394
406
|
if (info.Length() != 3 ||
|
|
395
|
-
!info[0]
|
|
396
|
-
!info[1]
|
|
397
|
-
!info[2]
|
|
398
|
-
|
|
399
|
-
return;
|
|
407
|
+
!info[0].IsNumber() ||
|
|
408
|
+
!info[1].IsNumber() ||
|
|
409
|
+
!info[2].IsNumber()) {
|
|
410
|
+
throw Napi::Error::New(env, "Usage: pty.resize(id, cols, rows)");
|
|
400
411
|
}
|
|
401
412
|
|
|
402
|
-
int id = info[0]
|
|
403
|
-
SHORT cols = static_cast<SHORT>(info[1]
|
|
404
|
-
SHORT rows = static_cast<SHORT>(info[2]
|
|
413
|
+
int id = info[0].As<Napi::Number>().Int32Value();
|
|
414
|
+
SHORT cols = static_cast<SHORT>(info[1].As<Napi::Number>().Uint32Value());
|
|
415
|
+
SHORT rows = static_cast<SHORT>(info[2].As<Napi::Number>().Uint32Value());
|
|
405
416
|
|
|
406
417
|
const pty_baton* handle = get_pty_baton(id);
|
|
407
418
|
|
|
@@ -419,48 +430,48 @@ static NAN_METHOD(PtyResize) {
|
|
|
419
430
|
}
|
|
420
431
|
}
|
|
421
432
|
|
|
422
|
-
return
|
|
433
|
+
return env.Undefined();
|
|
423
434
|
}
|
|
424
435
|
|
|
425
|
-
static
|
|
426
|
-
|
|
436
|
+
static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
|
|
437
|
+
Napi::Env env(info.Env());
|
|
438
|
+
Napi::HandleScope scope(env);
|
|
427
439
|
|
|
428
440
|
if (info.Length() != 1 ||
|
|
429
|
-
!info[0]
|
|
430
|
-
|
|
431
|
-
return;
|
|
441
|
+
!info[0].IsNumber()) {
|
|
442
|
+
throw Napi::Error::New(env, "Usage: pty.clear(id)");
|
|
432
443
|
}
|
|
433
444
|
|
|
434
|
-
int id = info[0]
|
|
445
|
+
// int id = info[0].As<Napi::Number>().Int32Value();
|
|
435
446
|
|
|
436
|
-
const pty_baton* handle = get_pty_baton(id);
|
|
447
|
+
// const pty_baton* handle = get_pty_baton(id);
|
|
437
448
|
|
|
438
|
-
if (handle != nullptr) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
449
|
+
// if (handle != nullptr) {
|
|
450
|
+
// HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
|
|
451
|
+
// bool fLoadedDll = hLibrary != nullptr;
|
|
452
|
+
// if (fLoadedDll)
|
|
453
|
+
// {
|
|
454
|
+
// PFNCLEARPSEUDOCONSOLE const pfnClearPseudoConsole = (PFNCLEARPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClearPseudoConsole");
|
|
455
|
+
// if (pfnClearPseudoConsole)
|
|
456
|
+
// {
|
|
457
|
+
// pfnClearPseudoConsole(handle->hpc);
|
|
458
|
+
// }
|
|
459
|
+
// }
|
|
460
|
+
// }
|
|
450
461
|
|
|
451
|
-
return
|
|
462
|
+
return env.Undefined();
|
|
452
463
|
}
|
|
453
464
|
|
|
454
|
-
static
|
|
455
|
-
|
|
465
|
+
static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
|
|
466
|
+
Napi::Env env(info.Env());
|
|
467
|
+
Napi::HandleScope scope(env);
|
|
456
468
|
|
|
457
469
|
if (info.Length() != 1 ||
|
|
458
|
-
!info[0]
|
|
459
|
-
|
|
460
|
-
return;
|
|
470
|
+
!info[0].IsNumber()) {
|
|
471
|
+
throw Napi::Error::New(env, "Usage: pty.kill(id)");
|
|
461
472
|
}
|
|
462
473
|
|
|
463
|
-
int id = info[0]
|
|
474
|
+
int id = info[0].As<Napi::Number>().Int32Value();
|
|
464
475
|
|
|
465
476
|
const pty_baton* handle = get_pty_baton(id);
|
|
466
477
|
|
|
@@ -476,27 +487,24 @@ static NAN_METHOD(PtyKill) {
|
|
|
476
487
|
}
|
|
477
488
|
}
|
|
478
489
|
|
|
479
|
-
DisconnectNamedPipe(handle->hIn);
|
|
480
|
-
DisconnectNamedPipe(handle->hOut);
|
|
481
|
-
CloseHandle(handle->hIn);
|
|
482
|
-
CloseHandle(handle->hOut);
|
|
483
490
|
CloseHandle(handle->hShell);
|
|
491
|
+
assert(remove_pty_baton(id));
|
|
484
492
|
}
|
|
485
493
|
|
|
486
|
-
return
|
|
494
|
+
return env.Undefined();
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
/**
|
|
490
498
|
* Init
|
|
491
499
|
*/
|
|
492
500
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
501
|
+
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
502
|
+
exports.Set("startProcess", Napi::Function::New(env, PtyStartProcess));
|
|
503
|
+
exports.Set("connect", Napi::Function::New(env, PtyConnect));
|
|
504
|
+
exports.Set("resize", Napi::Function::New(env, PtyResize));
|
|
505
|
+
exports.Set("clear", Napi::Function::New(env, PtyClear));
|
|
506
|
+
exports.Set("kill", Napi::Function::New(env, PtyKill));
|
|
507
|
+
return exports;
|
|
500
508
|
};
|
|
501
509
|
|
|
502
|
-
|
|
510
|
+
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
|
-
#
|
|
5
|
+
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
|
6
|
+
#include <napi.h>
|
|
6
7
|
#include <windows.h>
|
|
7
8
|
|
|
8
|
-
static
|
|
9
|
+
static Napi::Value ApiConsoleProcessList(const Napi::CallbackInfo& info) {
|
|
10
|
+
Napi::Env env(info.Env());
|
|
9
11
|
if (info.Length() != 1 ||
|
|
10
|
-
!info[0]
|
|
11
|
-
|
|
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]
|
|
16
|
+
const DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
16
17
|
|
|
17
18
|
if (!FreeConsole()) {
|
|
18
|
-
|
|
19
|
+
throw Napi::Error::New(env, "FreeConsole failed");
|
|
19
20
|
}
|
|
20
21
|
if (!AttachConsole(pid)) {
|
|
21
|
-
|
|
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
|
-
|
|
32
|
+
Napi::Array result = Napi::Array::New(env);
|
|
32
33
|
for (DWORD i = 0; i < processCount; i++) {
|
|
33
|
-
|
|
34
|
+
result.Set(i, Napi::Number::New(env, processList[i]));
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
return result;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
|