node-pty 1.1.0-beta1 → 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.
- 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 +245 -289
- package/src/unixTerminal.test.ts +35 -0
- package/src/unixTerminal.ts +2 -1
- package/src/win/conpty.cc +169 -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,65 @@ 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;
|
|
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
|
-
|
|
116
|
+
Napi::Error errorWithCode(const Napi::CallbackInfo& info, const char* text) {
|
|
76
117
|
std::stringstream errorText;
|
|
77
118
|
errorText << text;
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
162
|
-
|
|
199
|
+
static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
|
|
200
|
+
Napi::Env env(info.Env());
|
|
201
|
+
Napi::HandleScope scope(env);
|
|
163
202
|
|
|
164
|
-
|
|
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]
|
|
172
|
-
!info[1]
|
|
173
|
-
!info[2]
|
|
174
|
-
!info[3]
|
|
175
|
-
!info[4]
|
|
176
|
-
!info[5]
|
|
177
|
-
|
|
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(
|
|
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 =
|
|
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::
|
|
199
|
-
why
|
|
200
|
-
|
|
201
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
return;
|
|
258
|
+
throw Napi::Error::New(env, "Cannot launch conpty");
|
|
222
259
|
}
|
|
223
260
|
|
|
224
|
-
std::string inNameStr
|
|
261
|
+
std::string inNameStr = path_util::wstring_to_string(inName);
|
|
225
262
|
if (inNameStr.empty()) {
|
|
226
|
-
|
|
227
|
-
return;
|
|
263
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conin");
|
|
228
264
|
}
|
|
229
|
-
std::string outNameStr
|
|
265
|
+
std::string outNameStr = path_util::wstring_to_string(outName);
|
|
230
266
|
if (outNameStr.empty()) {
|
|
231
|
-
|
|
232
|
-
return;
|
|
267
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conout");
|
|
233
268
|
}
|
|
234
269
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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);
|
|
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();
|
|
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;
|
|
269
274
|
}
|
|
270
275
|
|
|
271
|
-
static
|
|
272
|
-
|
|
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]
|
|
283
|
-
!info[1]
|
|
284
|
-
!info[2]
|
|
285
|
-
!info[3]
|
|
286
|
-
!info[4]
|
|
287
|
-
|
|
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]
|
|
292
|
-
const std::wstring cmdline(path_util::to_wstring(
|
|
293
|
-
const std::wstring cwd(path_util::to_wstring(
|
|
294
|
-
const
|
|
295
|
-
|
|
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
|
-
|
|
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
|
|
317
|
+
std::wstring envStr;
|
|
314
318
|
if (!envValues.IsEmpty()) {
|
|
315
|
-
std::
|
|
316
|
-
for(uint32_t i = 0; i < envValues
|
|
317
|
-
|
|
318
|
-
envBlock
|
|
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
|
|
321
|
-
|
|
324
|
+
envBlock += L'\0';
|
|
325
|
+
envStr = std::move(envBlock);
|
|
322
326
|
}
|
|
323
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
383
|
-
RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, OnProcessExitWinEvent, (PVOID)handle, INFINITE, WT_EXECUTEONLYONCE);
|
|
381
|
+
SetupExitCallback(env, exitCallback, handle);
|
|
384
382
|
|
|
385
383
|
// Return
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
|
392
|
-
|
|
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]
|
|
396
|
-
!info[1]
|
|
397
|
-
!info[2]
|
|
398
|
-
|
|
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]
|
|
403
|
-
SHORT cols = static_cast<SHORT>(info[1]
|
|
404
|
-
SHORT rows = static_cast<SHORT>(info[2]
|
|
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,48 +417,48 @@ static NAN_METHOD(PtyResize) {
|
|
|
419
417
|
}
|
|
420
418
|
}
|
|
421
419
|
|
|
422
|
-
return
|
|
420
|
+
return env.Undefined();
|
|
423
421
|
}
|
|
424
422
|
|
|
425
|
-
static
|
|
426
|
-
|
|
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]
|
|
430
|
-
|
|
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]
|
|
432
|
+
// int id = info[0].As<Napi::Number>().Int32Value();
|
|
435
433
|
|
|
436
|
-
const pty_baton* handle = get_pty_baton(id);
|
|
434
|
+
// const pty_baton* handle = get_pty_baton(id);
|
|
437
435
|
|
|
438
|
-
if (handle != nullptr) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
436
|
+
// if (handle != nullptr) {
|
|
437
|
+
// HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
|
|
438
|
+
// bool fLoadedDll = hLibrary != nullptr;
|
|
439
|
+
// if (fLoadedDll)
|
|
440
|
+
// {
|
|
441
|
+
// PFNCLEARPSEUDOCONSOLE const pfnClearPseudoConsole = (PFNCLEARPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClearPseudoConsole");
|
|
442
|
+
// if (pfnClearPseudoConsole)
|
|
443
|
+
// {
|
|
444
|
+
// pfnClearPseudoConsole(handle->hpc);
|
|
445
|
+
// }
|
|
446
|
+
// }
|
|
447
|
+
// }
|
|
450
448
|
|
|
451
|
-
return
|
|
449
|
+
return env.Undefined();
|
|
452
450
|
}
|
|
453
451
|
|
|
454
|
-
static
|
|
455
|
-
|
|
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]
|
|
459
|
-
|
|
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]
|
|
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
|
|
481
|
+
return env.Undefined();
|
|
487
482
|
}
|
|
488
483
|
|
|
489
484
|
/**
|
|
490
485
|
* Init
|
|
491
486
|
*/
|
|
492
487
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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);
|