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.
- package/README.md +5 -5
- package/binding.gyp +6 -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/lib/windowsPtyAgent.js +6 -7
- package/lib/windowsPtyAgent.js.map +1 -1
- package/lib/windowsTerminal.test.js +98 -96
- package/lib/windowsTerminal.test.js.map +1 -1
- package/package.json +3 -2
- package/src/native.d.ts +4 -6
- 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 +172 -173
- package/src/win/conpty_console_list.cc +18 -17
- package/src/win/path_util.cc +30 -7
- package/src/win/path_util.h +6 -2
- package/src/win/winpty.cc +162 -141
- package/src/windowsPtyAgent.ts +6 -7
- package/src/windowsTerminal.test.ts +99 -97
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 = info[1]
|
|
183
|
-
const SHORT rows = 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)
|
|
@@ -194,13 +232,11 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
194
232
|
shellpath = filename;
|
|
195
233
|
}
|
|
196
234
|
|
|
197
|
-
std::string shellpath_(shellpath.begin(), shellpath.end());
|
|
198
|
-
|
|
199
235
|
if (shellpath.empty() || !path_util::file_exists(shellpath)) {
|
|
200
|
-
std::
|
|
201
|
-
why
|
|
202
|
-
|
|
203
|
-
|
|
236
|
+
std::string why;
|
|
237
|
+
why += "File not found: ";
|
|
238
|
+
why += path_util::wstring_to_string(shellpath);
|
|
239
|
+
throw Napi::Error::New(env, why);
|
|
204
240
|
}
|
|
205
241
|
|
|
206
242
|
HANDLE hIn, hOut;
|
|
@@ -211,61 +247,35 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
211
247
|
SetConsoleCtrlHandler(NULL, FALSE);
|
|
212
248
|
|
|
213
249
|
// Set return values
|
|
214
|
-
marshal =
|
|
250
|
+
marshal = Napi::Object::New(env);
|
|
215
251
|
|
|
216
252
|
if (SUCCEEDED(hr)) {
|
|
217
253
|
// We were able to instantiate a conpty
|
|
218
254
|
const int ptyId = InterlockedIncrement(&ptyCounter);
|
|
219
|
-
|
|
255
|
+
marshal.Set("pty", Napi::Number::New(env, ptyId));
|
|
220
256
|
ptyHandles.insert(ptyHandles.end(), new pty_baton(ptyId, hIn, hOut, hpc));
|
|
221
257
|
} else {
|
|
222
|
-
|
|
223
|
-
return;
|
|
258
|
+
throw Napi::Error::New(env, "Cannot launch conpty");
|
|
224
259
|
}
|
|
225
260
|
|
|
226
|
-
|
|
227
|
-
{
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
261
|
+
std::string inNameStr = path_util::wstring_to_string(inName);
|
|
262
|
+
if (inNameStr.empty()) {
|
|
263
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conin");
|
|
264
|
+
}
|
|
265
|
+
std::string outNameStr = path_util::wstring_to_string(outName);
|
|
266
|
+
if (outNameStr.empty()) {
|
|
267
|
+
throw Napi::Error::New(env, "Failed to initialize conpty conout");
|
|
233
268
|
}
|
|
234
|
-
info.GetReturnValue().Set(marshal);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
VOID CALLBACK OnProcessExitWinEvent(
|
|
238
|
-
_In_ PVOID context,
|
|
239
|
-
_In_ BOOLEAN TimerOrWaitFired) {
|
|
240
|
-
pty_baton *baton = static_cast<pty_baton*>(context);
|
|
241
|
-
|
|
242
|
-
// Fire OnProcessExit
|
|
243
|
-
uv_async_send(&baton->async);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
static void OnProcessExit(uv_async_t *async) {
|
|
247
|
-
Nan::HandleScope scope;
|
|
248
|
-
pty_baton *baton = static_cast<pty_baton*>(async->data);
|
|
249
|
-
|
|
250
|
-
UnregisterWait(baton->hWait);
|
|
251
|
-
|
|
252
|
-
// Get exit code
|
|
253
|
-
DWORD exitCode = 0;
|
|
254
|
-
GetExitCodeProcess(baton->hShell, &exitCode);
|
|
255
|
-
|
|
256
|
-
// Call function
|
|
257
|
-
v8::Local<v8::Value> args[1] = {
|
|
258
|
-
Nan::New<v8::Number>(exitCode)
|
|
259
|
-
};
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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;
|
|
265
274
|
}
|
|
266
275
|
|
|
267
|
-
static
|
|
268
|
-
|
|
276
|
+
static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
|
|
277
|
+
Napi::Env env(info.Env());
|
|
278
|
+
Napi::HandleScope scope(env);
|
|
269
279
|
|
|
270
280
|
// If we're working with conpty's we need to call ConnectNamedPipe here AFTER
|
|
271
281
|
// the Socket has attempted to connect to the other end, then actually
|
|
@@ -275,26 +285,24 @@ static NAN_METHOD(PtyConnect) {
|
|
|
275
285
|
BOOL fSuccess = FALSE;
|
|
276
286
|
|
|
277
287
|
if (info.Length() != 5 ||
|
|
278
|
-
!info[0]
|
|
279
|
-
!info[1]
|
|
280
|
-
!info[2]
|
|
281
|
-
!info[3]
|
|
282
|
-
!info[4]
|
|
283
|
-
|
|
284
|
-
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)");
|
|
285
294
|
}
|
|
286
295
|
|
|
287
|
-
const int id = info[0]
|
|
288
|
-
const std::wstring cmdline(path_util::to_wstring(
|
|
289
|
-
const std::wstring cwd(path_util::to_wstring(
|
|
290
|
-
const
|
|
291
|
-
|
|
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>();
|
|
292
301
|
|
|
293
302
|
// Fetch pty handle from ID and start process
|
|
294
303
|
pty_baton* handle = get_pty_baton(id);
|
|
295
304
|
if (!handle) {
|
|
296
|
-
|
|
297
|
-
return;
|
|
305
|
+
throw Napi::Error::New(env, "Invalid pty handle");
|
|
298
306
|
}
|
|
299
307
|
|
|
300
308
|
// Prepare command line
|
|
@@ -306,17 +314,17 @@ static NAN_METHOD(PtyConnect) {
|
|
|
306
314
|
hr = StringCchCopyW(mutableCwd.get(), cwd.length() + 1, cwd.c_str());
|
|
307
315
|
|
|
308
316
|
// Prepare environment
|
|
309
|
-
std::wstring
|
|
317
|
+
std::wstring envStr;
|
|
310
318
|
if (!envValues.IsEmpty()) {
|
|
311
|
-
std::
|
|
312
|
-
for(uint32_t i = 0; i < envValues
|
|
313
|
-
|
|
314
|
-
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';
|
|
315
323
|
}
|
|
316
|
-
envBlock
|
|
317
|
-
|
|
324
|
+
envBlock += L'\0';
|
|
325
|
+
envStr = std::move(envBlock);
|
|
318
326
|
}
|
|
319
|
-
|
|
327
|
+
std::vector<wchar_t> envV(envStr.cbegin(), envStr.cend());
|
|
320
328
|
LPWSTR envArg = envV.empty() ? nullptr : envV.data();
|
|
321
329
|
|
|
322
330
|
ConnectNamedPipe(handle->hIn, nullptr);
|
|
@@ -337,7 +345,7 @@ static NAN_METHOD(PtyConnect) {
|
|
|
337
345
|
|
|
338
346
|
fSuccess = InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size);
|
|
339
347
|
if (!fSuccess) {
|
|
340
|
-
|
|
348
|
+
throw errorWithCode(info, "InitializeProcThreadAttributeList failed");
|
|
341
349
|
}
|
|
342
350
|
fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList,
|
|
343
351
|
0,
|
|
@@ -347,7 +355,7 @@ static NAN_METHOD(PtyConnect) {
|
|
|
347
355
|
NULL,
|
|
348
356
|
NULL);
|
|
349
357
|
if (!fSuccess) {
|
|
350
|
-
|
|
358
|
+
throw errorWithCode(info, "UpdateProcThreadAttribute failed");
|
|
351
359
|
}
|
|
352
360
|
|
|
353
361
|
PROCESS_INFORMATION piClient{};
|
|
@@ -364,40 +372,34 @@ static NAN_METHOD(PtyConnect) {
|
|
|
364
372
|
&piClient // lpProcessInformation
|
|
365
373
|
);
|
|
366
374
|
if (!fSuccess) {
|
|
367
|
-
|
|
375
|
+
throw errorWithCode(info, "Cannot create process");
|
|
368
376
|
}
|
|
369
377
|
|
|
370
378
|
// Update handle
|
|
371
379
|
handle->hShell = piClient.hProcess;
|
|
372
|
-
handle->cb.Reset(exitCallback);
|
|
373
|
-
handle->async.data = handle;
|
|
374
|
-
|
|
375
|
-
// Setup OnProcessExit callback
|
|
376
|
-
uv_async_init(uv_default_loop(), &handle->async, OnProcessExit);
|
|
377
380
|
|
|
378
|
-
|
|
379
|
-
RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, OnProcessExitWinEvent, (PVOID)handle, INFINITE, WT_EXECUTEONLYONCE);
|
|
381
|
+
SetupExitCallback(env, exitCallback, handle);
|
|
380
382
|
|
|
381
383
|
// Return
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
384
|
+
auto marshal = Napi::Object::New(env);
|
|
385
|
+
marshal.Set("pid", Napi::Number::New(env, piClient.dwProcessId));
|
|
386
|
+
return marshal;
|
|
385
387
|
}
|
|
386
388
|
|
|
387
|
-
static
|
|
388
|
-
|
|
389
|
+
static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
390
|
+
Napi::Env env(info.Env());
|
|
391
|
+
Napi::HandleScope scope(env);
|
|
389
392
|
|
|
390
393
|
if (info.Length() != 3 ||
|
|
391
|
-
!info[0]
|
|
392
|
-
!info[1]
|
|
393
|
-
!info[2]
|
|
394
|
-
|
|
395
|
-
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)");
|
|
396
398
|
}
|
|
397
399
|
|
|
398
|
-
int id = info[0]
|
|
399
|
-
SHORT cols = info[1]
|
|
400
|
-
SHORT rows = 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());
|
|
401
403
|
|
|
402
404
|
const pty_baton* handle = get_pty_baton(id);
|
|
403
405
|
|
|
@@ -415,48 +417,48 @@ static NAN_METHOD(PtyResize) {
|
|
|
415
417
|
}
|
|
416
418
|
}
|
|
417
419
|
|
|
418
|
-
return
|
|
420
|
+
return env.Undefined();
|
|
419
421
|
}
|
|
420
422
|
|
|
421
|
-
static
|
|
422
|
-
|
|
423
|
+
static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
|
|
424
|
+
Napi::Env env(info.Env());
|
|
425
|
+
Napi::HandleScope scope(env);
|
|
423
426
|
|
|
424
427
|
if (info.Length() != 1 ||
|
|
425
|
-
!info[0]
|
|
426
|
-
|
|
427
|
-
return;
|
|
428
|
+
!info[0].IsNumber()) {
|
|
429
|
+
throw Napi::Error::New(env, "Usage: pty.clear(id)");
|
|
428
430
|
}
|
|
429
431
|
|
|
430
|
-
int id = info[0]
|
|
432
|
+
// int id = info[0].As<Napi::Number>().Int32Value();
|
|
431
433
|
|
|
432
|
-
const pty_baton* handle = get_pty_baton(id);
|
|
434
|
+
// const pty_baton* handle = get_pty_baton(id);
|
|
433
435
|
|
|
434
|
-
if (handle != nullptr) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
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
|
+
// }
|
|
446
448
|
|
|
447
|
-
return
|
|
449
|
+
return env.Undefined();
|
|
448
450
|
}
|
|
449
451
|
|
|
450
|
-
static
|
|
451
|
-
|
|
452
|
+
static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
|
|
453
|
+
Napi::Env env(info.Env());
|
|
454
|
+
Napi::HandleScope scope(env);
|
|
452
455
|
|
|
453
456
|
if (info.Length() != 1 ||
|
|
454
|
-
!info[0]
|
|
455
|
-
|
|
456
|
-
return;
|
|
457
|
+
!info[0].IsNumber()) {
|
|
458
|
+
throw Napi::Error::New(env, "Usage: pty.kill(id)");
|
|
457
459
|
}
|
|
458
460
|
|
|
459
|
-
int id = info[0]
|
|
461
|
+
int id = info[0].As<Napi::Number>().Int32Value();
|
|
460
462
|
|
|
461
463
|
const pty_baton* handle = get_pty_baton(id);
|
|
462
464
|
|
|
@@ -472,27 +474,24 @@ static NAN_METHOD(PtyKill) {
|
|
|
472
474
|
}
|
|
473
475
|
}
|
|
474
476
|
|
|
475
|
-
DisconnectNamedPipe(handle->hIn);
|
|
476
|
-
DisconnectNamedPipe(handle->hOut);
|
|
477
|
-
CloseHandle(handle->hIn);
|
|
478
|
-
CloseHandle(handle->hOut);
|
|
479
477
|
CloseHandle(handle->hShell);
|
|
478
|
+
assert(remove_pty_baton(id));
|
|
480
479
|
}
|
|
481
480
|
|
|
482
|
-
return
|
|
481
|
+
return env.Undefined();
|
|
483
482
|
}
|
|
484
483
|
|
|
485
484
|
/**
|
|
486
485
|
* Init
|
|
487
486
|
*/
|
|
488
487
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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;
|
|
496
495
|
};
|
|
497
496
|
|
|
498
|
-
|
|
497
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
|
|
@@ -2,42 +2,43 @@
|
|
|
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
|
|
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
|
-
auto processCount = GetConsoleProcessList(&processList[0], processList.size());
|
|
25
|
+
auto processCount = GetConsoleProcessList(&processList[0], static_cast<DWORD>(processList.size()));
|
|
25
26
|
if (processList.size() < processCount) {
|
|
26
27
|
processList.resize(processCount);
|
|
27
|
-
processCount = GetConsoleProcessList(&processList[0], processList.size());
|
|
28
|
+
processCount = GetConsoleProcessList(&processList[0], static_cast<DWORD>(processList.size()));
|
|
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);
|