@shitiandmw/node-pty 1.1.0-agent.0
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/LICENSE +69 -0
- package/README.md +170 -0
- package/binding.gyp +111 -0
- package/deps/.editorconfig +2 -0
- package/deps/winpty/.drone.yml +17 -0
- package/deps/winpty/.gitattributes +19 -0
- package/deps/winpty/LICENSE +21 -0
- package/deps/winpty/Makefile +166 -0
- package/deps/winpty/README.md +151 -0
- package/deps/winpty/RELEASES.md +280 -0
- package/deps/winpty/VERSION.txt +1 -0
- package/deps/winpty/configure +167 -0
- package/deps/winpty/misc/BufferResizeTests.cc +90 -0
- package/deps/winpty/misc/ChangeScreenBuffer.cc +53 -0
- package/deps/winpty/misc/ClearConsole.cc +72 -0
- package/deps/winpty/misc/ConinMode.cc +117 -0
- package/deps/winpty/misc/ConinMode.ps1 +116 -0
- package/deps/winpty/misc/ConoutMode.cc +113 -0
- package/deps/winpty/misc/DebugClient.py +42 -0
- package/deps/winpty/misc/DebugServer.py +63 -0
- package/deps/winpty/misc/DumpLines.py +5 -0
- package/deps/winpty/misc/EnableExtendedFlags.txt +46 -0
- package/deps/winpty/misc/Font-Report-June2016/CP437-Consolas.txt +528 -0
- package/deps/winpty/misc/Font-Report-June2016/CP437-Lucida.txt +633 -0
- package/deps/winpty/misc/Font-Report-June2016/CP932.txt +630 -0
- package/deps/winpty/misc/Font-Report-June2016/CP936.txt +630 -0
- package/deps/winpty/misc/Font-Report-June2016/CP949.txt +630 -0
- package/deps/winpty/misc/Font-Report-June2016/CP950.txt +630 -0
- package/deps/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt +16 -0
- package/deps/winpty/misc/Font-Report-June2016/Results.txt +4 -0
- package/deps/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt +144 -0
- package/deps/winpty/misc/FontSurvey.cc +100 -0
- package/deps/winpty/misc/FormatChar.h +21 -0
- package/deps/winpty/misc/FreezePerfTest.cc +62 -0
- package/deps/winpty/misc/GetCh.cc +20 -0
- package/deps/winpty/misc/GetConsolePos.cc +41 -0
- package/deps/winpty/misc/GetFont.cc +261 -0
- package/deps/winpty/misc/IdentifyConsoleWindow.ps1 +51 -0
- package/deps/winpty/misc/IsNewConsole.cc +87 -0
- package/deps/winpty/misc/MouseInputNotes.txt +90 -0
- package/deps/winpty/misc/MoveConsoleWindow.cc +34 -0
- package/deps/winpty/misc/Notes.txt +219 -0
- package/deps/winpty/misc/OSVersion.cc +27 -0
- package/deps/winpty/misc/ScreenBufferFreezeInactive.cc +101 -0
- package/deps/winpty/misc/ScreenBufferTest.cc +671 -0
- package/deps/winpty/misc/ScreenBufferTest2.cc +151 -0
- package/deps/winpty/misc/SelectAllTest.cc +45 -0
- package/deps/winpty/misc/SetBufferSize.cc +32 -0
- package/deps/winpty/misc/SetCursorPos.cc +10 -0
- package/deps/winpty/misc/SetFont.cc +145 -0
- package/deps/winpty/misc/SetWindowRect.cc +36 -0
- package/deps/winpty/misc/ShowArgv.cc +12 -0
- package/deps/winpty/misc/ShowConsoleInput.cc +40 -0
- package/deps/winpty/misc/Spew.py +5 -0
- package/deps/winpty/misc/TestUtil.cc +172 -0
- package/deps/winpty/misc/UnicodeDoubleWidthTest.cc +102 -0
- package/deps/winpty/misc/UnicodeWideTest1.cc +246 -0
- package/deps/winpty/misc/UnicodeWideTest2.cc +130 -0
- package/deps/winpty/misc/UnixEcho.cc +89 -0
- package/deps/winpty/misc/Utf16Echo.cc +46 -0
- package/deps/winpty/misc/VeryLargeRead.cc +122 -0
- package/deps/winpty/misc/VkEscapeTest.cc +56 -0
- package/deps/winpty/misc/Win10ResizeWhileFrozen.cc +52 -0
- package/deps/winpty/misc/Win10WrapTest1.cc +57 -0
- package/deps/winpty/misc/Win10WrapTest2.cc +30 -0
- package/deps/winpty/misc/Win32Echo1.cc +26 -0
- package/deps/winpty/misc/Win32Echo2.cc +19 -0
- package/deps/winpty/misc/Win32Test1.cc +46 -0
- package/deps/winpty/misc/Win32Test2.cc +70 -0
- package/deps/winpty/misc/Win32Test3.cc +78 -0
- package/deps/winpty/misc/Win32Write1.cc +44 -0
- package/deps/winpty/misc/WindowsBugCrashReader.cc +27 -0
- package/deps/winpty/misc/WriteConsole.cc +106 -0
- package/deps/winpty/misc/build32.sh +9 -0
- package/deps/winpty/misc/build64.sh +9 -0
- package/deps/winpty/misc/color-test.sh +212 -0
- package/deps/winpty/misc/font-notes.txt +300 -0
- package/deps/winpty/misc/winbug-15048.cc +201 -0
- package/deps/winpty/ship/build-pty4j-libpty.bat +36 -0
- package/deps/winpty/ship/common_ship.py +53 -0
- package/deps/winpty/ship/make_msvc_package.py +165 -0
- package/deps/winpty/ship/ship.py +108 -0
- package/deps/winpty/src/agent/Agent.cc +613 -0
- package/deps/winpty/src/agent/Agent.h +103 -0
- package/deps/winpty/src/agent/AgentCreateDesktop.cc +84 -0
- package/deps/winpty/src/agent/AgentCreateDesktop.h +28 -0
- package/deps/winpty/src/agent/ConsoleFont.cc +632 -0
- package/deps/winpty/src/agent/ConsoleFont.h +28 -0
- package/deps/winpty/src/agent/ConsoleInput.cc +852 -0
- package/deps/winpty/src/agent/ConsoleInput.h +109 -0
- package/deps/winpty/src/agent/ConsoleInputReencoding.cc +121 -0
- package/deps/winpty/src/agent/ConsoleInputReencoding.h +36 -0
- package/deps/winpty/src/agent/ConsoleLine.cc +152 -0
- package/deps/winpty/src/agent/ConsoleLine.h +41 -0
- package/deps/winpty/src/agent/Coord.h +87 -0
- package/deps/winpty/src/agent/DebugShowInput.cc +239 -0
- package/deps/winpty/src/agent/DebugShowInput.h +32 -0
- package/deps/winpty/src/agent/DefaultInputMap.cc +422 -0
- package/deps/winpty/src/agent/DefaultInputMap.h +28 -0
- package/deps/winpty/src/agent/DsrSender.h +30 -0
- package/deps/winpty/src/agent/EventLoop.cc +99 -0
- package/deps/winpty/src/agent/EventLoop.h +47 -0
- package/deps/winpty/src/agent/InputMap.cc +246 -0
- package/deps/winpty/src/agent/InputMap.h +114 -0
- package/deps/winpty/src/agent/LargeConsoleRead.cc +71 -0
- package/deps/winpty/src/agent/LargeConsoleRead.h +68 -0
- package/deps/winpty/src/agent/NamedPipe.cc +378 -0
- package/deps/winpty/src/agent/NamedPipe.h +125 -0
- package/deps/winpty/src/agent/Scraper.cc +699 -0
- package/deps/winpty/src/agent/Scraper.h +103 -0
- package/deps/winpty/src/agent/SimplePool.h +75 -0
- package/deps/winpty/src/agent/SmallRect.h +143 -0
- package/deps/winpty/src/agent/Terminal.cc +535 -0
- package/deps/winpty/src/agent/Terminal.h +69 -0
- package/deps/winpty/src/agent/UnicodeEncoding.h +157 -0
- package/deps/winpty/src/agent/UnicodeEncodingTest.cc +189 -0
- package/deps/winpty/src/agent/Win32Console.cc +107 -0
- package/deps/winpty/src/agent/Win32Console.h +67 -0
- package/deps/winpty/src/agent/Win32ConsoleBuffer.cc +193 -0
- package/deps/winpty/src/agent/Win32ConsoleBuffer.h +99 -0
- package/deps/winpty/src/agent/main.cc +114 -0
- package/deps/winpty/src/agent/subdir.mk +61 -0
- package/deps/winpty/src/configurations.gypi +60 -0
- package/deps/winpty/src/debugserver/DebugServer.cc +117 -0
- package/deps/winpty/src/debugserver/subdir.mk +41 -0
- package/deps/winpty/src/include/winpty.h +242 -0
- package/deps/winpty/src/include/winpty_constants.h +131 -0
- package/deps/winpty/src/libwinpty/AgentLocation.cc +75 -0
- package/deps/winpty/src/libwinpty/AgentLocation.h +28 -0
- package/deps/winpty/src/libwinpty/LibWinptyException.h +54 -0
- package/deps/winpty/src/libwinpty/WinptyInternal.h +72 -0
- package/deps/winpty/src/libwinpty/subdir.mk +46 -0
- package/deps/winpty/src/libwinpty/winpty.cc +970 -0
- package/deps/winpty/src/shared/AgentMsg.h +38 -0
- package/deps/winpty/src/shared/BackgroundDesktop.cc +122 -0
- package/deps/winpty/src/shared/BackgroundDesktop.h +73 -0
- package/deps/winpty/src/shared/Buffer.cc +103 -0
- package/deps/winpty/src/shared/Buffer.h +102 -0
- package/deps/winpty/src/shared/DebugClient.cc +187 -0
- package/deps/winpty/src/shared/DebugClient.h +38 -0
- package/deps/winpty/src/shared/GenRandom.cc +138 -0
- package/deps/winpty/src/shared/GenRandom.h +55 -0
- package/deps/winpty/src/shared/GetCommitHash.bat +13 -0
- package/deps/winpty/src/shared/Mutex.h +54 -0
- package/deps/winpty/src/shared/OsModule.h +63 -0
- package/deps/winpty/src/shared/OwnedHandle.cc +36 -0
- package/deps/winpty/src/shared/OwnedHandle.h +45 -0
- package/deps/winpty/src/shared/PrecompiledHeader.h +43 -0
- package/deps/winpty/src/shared/StringBuilder.h +227 -0
- package/deps/winpty/src/shared/StringBuilderTest.cc +114 -0
- package/deps/winpty/src/shared/StringUtil.cc +55 -0
- package/deps/winpty/src/shared/StringUtil.h +80 -0
- package/deps/winpty/src/shared/TimeMeasurement.h +63 -0
- package/deps/winpty/src/shared/UnixCtrlChars.h +45 -0
- package/deps/winpty/src/shared/UpdateGenVersion.bat +20 -0
- package/deps/winpty/src/shared/WindowsSecurity.cc +460 -0
- package/deps/winpty/src/shared/WindowsSecurity.h +104 -0
- package/deps/winpty/src/shared/WindowsVersion.cc +252 -0
- package/deps/winpty/src/shared/WindowsVersion.h +29 -0
- package/deps/winpty/src/shared/WinptyAssert.cc +55 -0
- package/deps/winpty/src/shared/WinptyAssert.h +64 -0
- package/deps/winpty/src/shared/WinptyException.cc +57 -0
- package/deps/winpty/src/shared/WinptyException.h +43 -0
- package/deps/winpty/src/shared/WinptyVersion.cc +42 -0
- package/deps/winpty/src/shared/WinptyVersion.h +27 -0
- package/deps/winpty/src/shared/winpty_snprintf.h +99 -0
- package/deps/winpty/src/subdir.mk +5 -0
- package/deps/winpty/src/tests/subdir.mk +28 -0
- package/deps/winpty/src/tests/trivial_test.cc +158 -0
- package/deps/winpty/src/unix-adapter/InputHandler.cc +114 -0
- package/deps/winpty/src/unix-adapter/InputHandler.h +56 -0
- package/deps/winpty/src/unix-adapter/OutputHandler.cc +80 -0
- package/deps/winpty/src/unix-adapter/OutputHandler.h +53 -0
- package/deps/winpty/src/unix-adapter/Util.cc +86 -0
- package/deps/winpty/src/unix-adapter/Util.h +31 -0
- package/deps/winpty/src/unix-adapter/WakeupFd.cc +70 -0
- package/deps/winpty/src/unix-adapter/WakeupFd.h +42 -0
- package/deps/winpty/src/unix-adapter/main.cc +729 -0
- package/deps/winpty/src/unix-adapter/subdir.mk +41 -0
- package/deps/winpty/src/winpty.gyp +234 -0
- package/deps/winpty/vcbuild.bat +83 -0
- package/lib/conpty_console_list_agent.js +16 -0
- package/lib/conpty_console_list_agent.js.map +1 -0
- package/lib/eventEmitter2.js +47 -0
- package/lib/eventEmitter2.js.map +1 -0
- package/lib/eventEmitter2.test.js +30 -0
- package/lib/eventEmitter2.test.js.map +1 -0
- package/lib/index.js +52 -0
- package/lib/index.js.map +1 -0
- package/lib/interfaces.js +7 -0
- package/lib/interfaces.js.map +1 -0
- package/lib/shared/conout.js +11 -0
- package/lib/shared/conout.js.map +1 -0
- package/lib/terminal.js +190 -0
- package/lib/terminal.js.map +1 -0
- package/lib/terminal.test.js +139 -0
- package/lib/terminal.test.js.map +1 -0
- package/lib/testUtils.test.js +28 -0
- package/lib/testUtils.test.js.map +1 -0
- package/lib/types.js +7 -0
- package/lib/types.js.map +1 -0
- package/lib/unixTerminal.js +346 -0
- package/lib/unixTerminal.js.map +1 -0
- package/lib/unixTerminal.test.js +351 -0
- package/lib/unixTerminal.test.js.map +1 -0
- package/lib/utils.js +39 -0
- package/lib/utils.js.map +1 -0
- package/lib/windowsConoutConnection.js +125 -0
- package/lib/windowsConoutConnection.js.map +1 -0
- package/lib/windowsPtyAgent.js +320 -0
- package/lib/windowsPtyAgent.js.map +1 -0
- package/lib/windowsPtyAgent.test.js +90 -0
- package/lib/windowsPtyAgent.test.js.map +1 -0
- package/lib/windowsTerminal.js +199 -0
- package/lib/windowsTerminal.js.map +1 -0
- package/lib/windowsTerminal.test.js +219 -0
- package/lib/windowsTerminal.test.js.map +1 -0
- package/lib/worker/conoutSocketWorker.js +22 -0
- package/lib/worker/conoutSocketWorker.js.map +1 -0
- package/package.json +68 -0
- package/prebuilds/darwin-arm64/pty.node +0 -0
- package/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/prebuilds/darwin-x64/pty.node +0 -0
- package/prebuilds/darwin-x64/spawn-helper +0 -0
- package/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
- package/prebuilds/win32-arm64/conpty.node +0 -0
- package/prebuilds/win32-arm64/conpty.pdb +0 -0
- package/prebuilds/win32-arm64/conpty_console_list.node +0 -0
- package/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
- package/prebuilds/win32-arm64/pty.node +0 -0
- package/prebuilds/win32-arm64/pty.pdb +0 -0
- package/prebuilds/win32-arm64/winpty-agent.exe +0 -0
- package/prebuilds/win32-arm64/winpty-agent.pdb +0 -0
- package/prebuilds/win32-arm64/winpty.dll +0 -0
- package/prebuilds/win32-arm64/winpty.pdb +0 -0
- package/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
- package/prebuilds/win32-x64/conpty/conpty.dll +0 -0
- package/prebuilds/win32-x64/conpty.node +0 -0
- package/prebuilds/win32-x64/conpty.pdb +0 -0
- package/prebuilds/win32-x64/conpty_console_list.node +0 -0
- package/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
- package/prebuilds/win32-x64/pty.node +0 -0
- package/prebuilds/win32-x64/pty.pdb +0 -0
- package/prebuilds/win32-x64/winpty-agent.exe +0 -0
- package/prebuilds/win32-x64/winpty-agent.pdb +0 -0
- package/prebuilds/win32-x64/winpty.dll +0 -0
- package/prebuilds/win32-x64/winpty.pdb +0 -0
- package/scripts/gen-compile-commands.js +8 -0
- package/scripts/increment-version.js +54 -0
- package/scripts/post-install.js +99 -0
- package/scripts/prebuild.js +39 -0
- package/scripts/sync-prebuild.js +31 -0
- package/scripts/verify-darwin-fd-leak.js +63 -0
- package/src/conpty_console_list_agent.ts +15 -0
- package/src/eventEmitter2.test.ts +30 -0
- package/src/eventEmitter2.ts +48 -0
- package/src/index.ts +52 -0
- package/src/interfaces.ts +130 -0
- package/src/native.d.ts +54 -0
- package/src/shared/conout.ts +15 -0
- package/src/terminal.test.ts +119 -0
- package/src/terminal.ts +211 -0
- package/src/testUtils.test.ts +23 -0
- package/src/tsconfig.json +22 -0
- package/src/types.ts +15 -0
- package/src/unix/pty.cc +808 -0
- package/src/unix/spawn-helper.cc +23 -0
- package/src/unixTerminal.test.ts +367 -0
- package/src/unixTerminal.ts +388 -0
- package/src/utils.ts +29 -0
- package/src/win/conpty.cc +583 -0
- package/src/win/conpty.h +41 -0
- package/src/win/conpty_console_list.cc +44 -0
- package/src/win/path_util.cc +95 -0
- package/src/win/path_util.h +26 -0
- package/src/win/winpty.cc +333 -0
- package/src/windowsConoutConnection.ts +82 -0
- package/src/windowsPtyAgent.test.ts +94 -0
- package/src/windowsPtyAgent.ts +321 -0
- package/src/windowsTerminal.test.ts +229 -0
- package/src/windowsTerminal.ts +203 -0
- package/src/worker/conoutSocketWorker.ts +22 -0
- package/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
- package/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
- package/typings/node-pty.d.ts +211 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
// Copyright (c) 2011-2016 Ryan Prichard
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to
|
|
5
|
+
// deal in the Software without restriction, including without limitation the
|
|
6
|
+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
7
|
+
// sell copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in
|
|
11
|
+
// all copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
18
|
+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
19
|
+
// IN THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
#include <windows.h>
|
|
22
|
+
|
|
23
|
+
#include <stdint.h>
|
|
24
|
+
#include <stdio.h>
|
|
25
|
+
#include <string.h>
|
|
26
|
+
|
|
27
|
+
#include <limits>
|
|
28
|
+
#include <string>
|
|
29
|
+
#include <vector>
|
|
30
|
+
|
|
31
|
+
#include "../include/winpty.h"
|
|
32
|
+
|
|
33
|
+
#include "../shared/AgentMsg.h"
|
|
34
|
+
#include "../shared/BackgroundDesktop.h"
|
|
35
|
+
#include "../shared/Buffer.h"
|
|
36
|
+
#include "../shared/DebugClient.h"
|
|
37
|
+
#include "../shared/GenRandom.h"
|
|
38
|
+
#include "../shared/OwnedHandle.h"
|
|
39
|
+
#include "../shared/StringBuilder.h"
|
|
40
|
+
#include "../shared/StringUtil.h"
|
|
41
|
+
#include "../shared/WindowsSecurity.h"
|
|
42
|
+
#include "../shared/WindowsVersion.h"
|
|
43
|
+
#include "../shared/WinptyAssert.h"
|
|
44
|
+
#include "../shared/WinptyException.h"
|
|
45
|
+
#include "../shared/WinptyVersion.h"
|
|
46
|
+
|
|
47
|
+
#include "AgentLocation.h"
|
|
48
|
+
#include "LibWinptyException.h"
|
|
49
|
+
#include "WinptyInternal.h"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/*****************************************************************************
|
|
54
|
+
* Error handling -- translate C++ exceptions to an optional error object
|
|
55
|
+
* output and log the result. */
|
|
56
|
+
|
|
57
|
+
static const winpty_error_s kOutOfMemory = {
|
|
58
|
+
WINPTY_ERROR_OUT_OF_MEMORY,
|
|
59
|
+
L"Out of memory",
|
|
60
|
+
nullptr
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
static const winpty_error_s kBadRpcPacket = {
|
|
64
|
+
WINPTY_ERROR_UNSPECIFIED,
|
|
65
|
+
L"Bad RPC packet",
|
|
66
|
+
nullptr
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
static const winpty_error_s kUncaughtException = {
|
|
70
|
+
WINPTY_ERROR_UNSPECIFIED,
|
|
71
|
+
L"Uncaught C++ exception",
|
|
72
|
+
nullptr
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/* Gets the error code from the error object. */
|
|
76
|
+
WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err) {
|
|
77
|
+
return err != nullptr ? err->code : WINPTY_ERROR_SUCCESS;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Returns a textual representation of the error. The string is freed when
|
|
81
|
+
* the error is freed. */
|
|
82
|
+
WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err) {
|
|
83
|
+
if (err != nullptr) {
|
|
84
|
+
if (err->msgStatic != nullptr) {
|
|
85
|
+
return err->msgStatic;
|
|
86
|
+
} else {
|
|
87
|
+
ASSERT(err->msgDynamic != nullptr);
|
|
88
|
+
std::wstring *msgPtr = err->msgDynamic->get();
|
|
89
|
+
ASSERT(msgPtr != nullptr);
|
|
90
|
+
return msgPtr->c_str();
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
return L"Success";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Free the error object. Every error returned from the winpty API must be
|
|
98
|
+
* freed. */
|
|
99
|
+
WINPTY_API void winpty_error_free(winpty_error_ptr_t err) {
|
|
100
|
+
if (err != nullptr && err->msgDynamic != nullptr) {
|
|
101
|
+
delete err->msgDynamic;
|
|
102
|
+
delete err;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static void translateException(winpty_error_ptr_t *&err) {
|
|
107
|
+
winpty_error_ptr_t ret = nullptr;
|
|
108
|
+
try {
|
|
109
|
+
try {
|
|
110
|
+
throw;
|
|
111
|
+
} catch (const ReadBuffer::DecodeError&) {
|
|
112
|
+
ret = const_cast<winpty_error_ptr_t>(&kBadRpcPacket);
|
|
113
|
+
} catch (const LibWinptyException &e) {
|
|
114
|
+
std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
|
|
115
|
+
obj->code = e.code();
|
|
116
|
+
obj->msgStatic = nullptr;
|
|
117
|
+
obj->msgDynamic =
|
|
118
|
+
new std::shared_ptr<std::wstring>(e.whatSharedStr());
|
|
119
|
+
ret = obj.release();
|
|
120
|
+
} catch (const WinptyException &e) {
|
|
121
|
+
std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
|
|
122
|
+
std::shared_ptr<std::wstring> msg(new std::wstring(e.what()));
|
|
123
|
+
obj->code = WINPTY_ERROR_UNSPECIFIED;
|
|
124
|
+
obj->msgStatic = nullptr;
|
|
125
|
+
obj->msgDynamic = new std::shared_ptr<std::wstring>(msg);
|
|
126
|
+
ret = obj.release();
|
|
127
|
+
}
|
|
128
|
+
} catch (const std::bad_alloc&) {
|
|
129
|
+
ret = const_cast<winpty_error_ptr_t>(&kOutOfMemory);
|
|
130
|
+
} catch (...) {
|
|
131
|
+
ret = const_cast<winpty_error_ptr_t>(&kUncaughtException);
|
|
132
|
+
}
|
|
133
|
+
trace("libwinpty error: code=%u msg='%s'",
|
|
134
|
+
static_cast<unsigned>(ret->code),
|
|
135
|
+
utf8FromWide(winpty_error_msg(ret)).c_str());
|
|
136
|
+
if (err != nullptr) {
|
|
137
|
+
*err = ret;
|
|
138
|
+
} else {
|
|
139
|
+
winpty_error_free(ret);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#define API_TRY \
|
|
144
|
+
if (err != nullptr) { *err = nullptr; } \
|
|
145
|
+
try
|
|
146
|
+
|
|
147
|
+
#define API_CATCH(ret) \
|
|
148
|
+
catch (...) { translateException(err); return (ret); }
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/*****************************************************************************
|
|
153
|
+
* Configuration of a new agent. */
|
|
154
|
+
|
|
155
|
+
WINPTY_API winpty_config_t *
|
|
156
|
+
winpty_config_new(UINT64 flags, winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
157
|
+
API_TRY {
|
|
158
|
+
ASSERT((flags & WINPTY_FLAG_MASK) == flags);
|
|
159
|
+
std::unique_ptr<winpty_config_t> ret(new winpty_config_t);
|
|
160
|
+
ret->flags = flags;
|
|
161
|
+
return ret.release();
|
|
162
|
+
} API_CATCH(nullptr)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
WINPTY_API void winpty_config_free(winpty_config_t *cfg) {
|
|
166
|
+
delete cfg;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
WINPTY_API void
|
|
170
|
+
winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows) {
|
|
171
|
+
ASSERT(cfg != nullptr && cols > 0 && rows > 0);
|
|
172
|
+
cfg->cols = cols;
|
|
173
|
+
cfg->rows = rows;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
WINPTY_API void
|
|
177
|
+
winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode) {
|
|
178
|
+
ASSERT(cfg != nullptr &&
|
|
179
|
+
mouseMode >= WINPTY_MOUSE_MODE_NONE &&
|
|
180
|
+
mouseMode <= WINPTY_MOUSE_MODE_FORCE);
|
|
181
|
+
cfg->mouseMode = mouseMode;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
WINPTY_API void
|
|
185
|
+
winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs) {
|
|
186
|
+
ASSERT(cfg != nullptr && timeoutMs > 0);
|
|
187
|
+
cfg->timeoutMs = timeoutMs;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
/*****************************************************************************
|
|
193
|
+
* Agent I/O. */
|
|
194
|
+
|
|
195
|
+
namespace {
|
|
196
|
+
|
|
197
|
+
// Once an I/O operation fails with ERROR_IO_PENDING, the caller *must* wait
|
|
198
|
+
// for it to complete, even after calling CancelIo on it! See
|
|
199
|
+
// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613. This
|
|
200
|
+
// class enforces that requirement.
|
|
201
|
+
class PendingIo {
|
|
202
|
+
HANDLE m_file;
|
|
203
|
+
OVERLAPPED &m_over;
|
|
204
|
+
bool m_finished;
|
|
205
|
+
public:
|
|
206
|
+
// The file handle and OVERLAPPED object must live as long as the PendingIo
|
|
207
|
+
// object.
|
|
208
|
+
PendingIo(HANDLE file, OVERLAPPED &over) :
|
|
209
|
+
m_file(file), m_over(over), m_finished(false) {}
|
|
210
|
+
~PendingIo() {
|
|
211
|
+
if (!m_finished) {
|
|
212
|
+
// We're not usually that interested in CancelIo's return value.
|
|
213
|
+
// In any case, we must not throw an exception in this dtor.
|
|
214
|
+
CancelIo(m_file);
|
|
215
|
+
waitForCompletion();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
std::tuple<BOOL, DWORD> waitForCompletion(DWORD &actual) WINPTY_NOEXCEPT {
|
|
219
|
+
m_finished = true;
|
|
220
|
+
const BOOL success =
|
|
221
|
+
GetOverlappedResult(m_file, &m_over, &actual, TRUE);
|
|
222
|
+
return std::make_tuple(success, GetLastError());
|
|
223
|
+
}
|
|
224
|
+
std::tuple<BOOL, DWORD> waitForCompletion() WINPTY_NOEXCEPT {
|
|
225
|
+
DWORD actual = 0;
|
|
226
|
+
return waitForCompletion(actual);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
} // anonymous namespace
|
|
231
|
+
|
|
232
|
+
static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
|
|
233
|
+
DWORD &lastError, DWORD &actual) {
|
|
234
|
+
if (!success && lastError == ERROR_IO_PENDING) {
|
|
235
|
+
PendingIo io(wp.controlPipe.get(), over);
|
|
236
|
+
const HANDLE waitHandles[2] = { wp.ioEvent.get(),
|
|
237
|
+
wp.agentProcess.get() };
|
|
238
|
+
DWORD waitRet = WaitForMultipleObjects(
|
|
239
|
+
2, waitHandles, FALSE, wp.agentTimeoutMs);
|
|
240
|
+
if (waitRet != WAIT_OBJECT_0) {
|
|
241
|
+
// The I/O is still pending. Cancel it, close the I/O event, and
|
|
242
|
+
// throw an exception.
|
|
243
|
+
if (waitRet == WAIT_OBJECT_0 + 1) {
|
|
244
|
+
throw LibWinptyException(WINPTY_ERROR_AGENT_DIED, L"agent died");
|
|
245
|
+
} else if (waitRet == WAIT_TIMEOUT) {
|
|
246
|
+
throw LibWinptyException(WINPTY_ERROR_AGENT_TIMEOUT,
|
|
247
|
+
L"agent timed out");
|
|
248
|
+
} else if (waitRet == WAIT_FAILED) {
|
|
249
|
+
throwWindowsError(L"WaitForMultipleObjects failed");
|
|
250
|
+
} else {
|
|
251
|
+
ASSERT(false &&
|
|
252
|
+
"unexpected WaitForMultipleObjects return value");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
std::tie(success, lastError) = io.waitForCompletion(actual);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
|
|
260
|
+
DWORD &lastError) {
|
|
261
|
+
DWORD actual = 0;
|
|
262
|
+
handlePendingIo(wp, over, success, lastError, actual);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static void handleReadWriteErrors(winpty_t &wp, BOOL success, DWORD lastError,
|
|
266
|
+
const wchar_t *genericErrMsg) {
|
|
267
|
+
if (!success) {
|
|
268
|
+
// If the pipe connection is broken after it's been connected, then
|
|
269
|
+
// later I/O operations fail with ERROR_BROKEN_PIPE (reads) or
|
|
270
|
+
// ERROR_NO_DATA (writes). With Wine, they may also fail with
|
|
271
|
+
// ERROR_PIPE_NOT_CONNECTED. See this gist[1].
|
|
272
|
+
//
|
|
273
|
+
// [1] https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba
|
|
274
|
+
if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_NO_DATA ||
|
|
275
|
+
lastError == ERROR_PIPE_NOT_CONNECTED) {
|
|
276
|
+
throw LibWinptyException(WINPTY_ERROR_LOST_CONNECTION,
|
|
277
|
+
L"lost connection to agent");
|
|
278
|
+
} else {
|
|
279
|
+
throwWindowsError(genericErrMsg, lastError);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Calls ConnectNamedPipe to wait until the agent connects to the control pipe.
|
|
285
|
+
static void
|
|
286
|
+
connectControlPipe(winpty_t &wp) {
|
|
287
|
+
OVERLAPPED over = {};
|
|
288
|
+
over.hEvent = wp.ioEvent.get();
|
|
289
|
+
BOOL success = ConnectNamedPipe(wp.controlPipe.get(), &over);
|
|
290
|
+
DWORD lastError = GetLastError();
|
|
291
|
+
handlePendingIo(wp, over, success, lastError);
|
|
292
|
+
if (!success && lastError == ERROR_PIPE_CONNECTED) {
|
|
293
|
+
success = TRUE;
|
|
294
|
+
}
|
|
295
|
+
if (!success) {
|
|
296
|
+
throwWindowsError(L"ConnectNamedPipe failed", lastError);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static void writeData(winpty_t &wp, const void *data, size_t amount) {
|
|
301
|
+
// Perform a single pipe write.
|
|
302
|
+
DWORD actual = 0;
|
|
303
|
+
OVERLAPPED over = {};
|
|
304
|
+
over.hEvent = wp.ioEvent.get();
|
|
305
|
+
BOOL success = WriteFile(wp.controlPipe.get(), data, amount,
|
|
306
|
+
&actual, &over);
|
|
307
|
+
DWORD lastError = GetLastError();
|
|
308
|
+
if (!success) {
|
|
309
|
+
handlePendingIo(wp, over, success, lastError, actual);
|
|
310
|
+
handleReadWriteErrors(wp, success, lastError, L"WriteFile failed");
|
|
311
|
+
ASSERT(success);
|
|
312
|
+
}
|
|
313
|
+
// TODO: Can a partial write actually happen somehow?
|
|
314
|
+
ASSERT(actual == amount && "WriteFile wrote fewer bytes than requested");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static inline WriteBuffer newPacket() {
|
|
318
|
+
WriteBuffer packet;
|
|
319
|
+
packet.putRawValue<uint64_t>(0); // Reserve space for size.
|
|
320
|
+
return packet;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
static void writePacket(winpty_t &wp, WriteBuffer &packet) {
|
|
324
|
+
const auto &buf = packet.buf();
|
|
325
|
+
packet.replaceRawValue<uint64_t>(0, buf.size());
|
|
326
|
+
writeData(wp, buf.data(), buf.size());
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
static size_t readData(winpty_t &wp, void *data, size_t amount) {
|
|
330
|
+
DWORD actual = 0;
|
|
331
|
+
OVERLAPPED over = {};
|
|
332
|
+
over.hEvent = wp.ioEvent.get();
|
|
333
|
+
BOOL success = ReadFile(wp.controlPipe.get(), data, amount,
|
|
334
|
+
&actual, &over);
|
|
335
|
+
DWORD lastError = GetLastError();
|
|
336
|
+
if (!success) {
|
|
337
|
+
handlePendingIo(wp, over, success, lastError, actual);
|
|
338
|
+
handleReadWriteErrors(wp, success, lastError, L"ReadFile failed");
|
|
339
|
+
}
|
|
340
|
+
return actual;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
static void readAll(winpty_t &wp, void *data, size_t amount) {
|
|
344
|
+
while (amount > 0) {
|
|
345
|
+
const size_t chunk = readData(wp, data, amount);
|
|
346
|
+
ASSERT(chunk <= amount && "readData result is larger than amount");
|
|
347
|
+
data = reinterpret_cast<char*>(data) + chunk;
|
|
348
|
+
amount -= chunk;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
static uint64_t readUInt64(winpty_t &wp) {
|
|
353
|
+
uint64_t ret = 0;
|
|
354
|
+
readAll(wp, &ret, sizeof(ret));
|
|
355
|
+
return ret;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Returns a reply packet's payload.
|
|
359
|
+
static ReadBuffer readPacket(winpty_t &wp) {
|
|
360
|
+
const uint64_t packetSize = readUInt64(wp);
|
|
361
|
+
if (packetSize < sizeof(packetSize) || packetSize > SIZE_MAX) {
|
|
362
|
+
throwWinptyException(L"Agent RPC error: invalid packet size");
|
|
363
|
+
}
|
|
364
|
+
const size_t payloadSize = packetSize - sizeof(packetSize);
|
|
365
|
+
std::vector<char> bytes(payloadSize);
|
|
366
|
+
readAll(wp, bytes.data(), bytes.size());
|
|
367
|
+
return ReadBuffer(std::move(bytes));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static OwnedHandle createControlPipe(const std::wstring &name) {
|
|
371
|
+
const auto sd = createPipeSecurityDescriptorOwnerFullControl();
|
|
372
|
+
if (!sd) {
|
|
373
|
+
throwWinptyException(
|
|
374
|
+
L"could not create the control pipe's SECURITY_DESCRIPTOR");
|
|
375
|
+
}
|
|
376
|
+
SECURITY_ATTRIBUTES sa = {};
|
|
377
|
+
sa.nLength = sizeof(sa);
|
|
378
|
+
sa.lpSecurityDescriptor = sd.get();
|
|
379
|
+
HANDLE ret = CreateNamedPipeW(name.c_str(),
|
|
380
|
+
/*dwOpenMode=*/
|
|
381
|
+
PIPE_ACCESS_DUPLEX |
|
|
382
|
+
FILE_FLAG_FIRST_PIPE_INSTANCE |
|
|
383
|
+
FILE_FLAG_OVERLAPPED,
|
|
384
|
+
/*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
|
|
385
|
+
/*nMaxInstances=*/1,
|
|
386
|
+
/*nOutBufferSize=*/8192,
|
|
387
|
+
/*nInBufferSize=*/256,
|
|
388
|
+
/*nDefaultTimeOut=*/30000,
|
|
389
|
+
&sa);
|
|
390
|
+
if (ret == INVALID_HANDLE_VALUE) {
|
|
391
|
+
throwWindowsError(L"CreateNamedPipeW failed");
|
|
392
|
+
}
|
|
393
|
+
return OwnedHandle(ret);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
/*****************************************************************************
|
|
399
|
+
* Start the agent. */
|
|
400
|
+
|
|
401
|
+
static OwnedHandle createEvent() {
|
|
402
|
+
// manual reset, initially unset
|
|
403
|
+
HANDLE h = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
|
404
|
+
if (h == nullptr) {
|
|
405
|
+
throwWindowsError(L"CreateEventW failed");
|
|
406
|
+
}
|
|
407
|
+
return OwnedHandle(h);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// For debugging purposes, provide a way to keep the console on the main window
|
|
411
|
+
// station, visible.
|
|
412
|
+
static bool shouldShowConsoleWindow() {
|
|
413
|
+
char buf[32];
|
|
414
|
+
return GetEnvironmentVariableA("WINPTY_SHOW_CONSOLE", buf, sizeof(buf)) > 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
static bool shouldCreateBackgroundDesktop(bool &createUsingAgent) {
|
|
418
|
+
// Prior to Windows 7, winpty's repeated selection-deselection loop
|
|
419
|
+
// prevented the user from interacting with their *visible* console
|
|
420
|
+
// windows, unless we placed the console onto a background desktop.
|
|
421
|
+
// The SetProcessWindowStation call interferes with the clipboard and
|
|
422
|
+
// isn't thread-safe, though[1]. The call should perhaps occur in a
|
|
423
|
+
// special agent subprocess. Spawning a process in a background desktop
|
|
424
|
+
// also breaks ConEmu, but marking the process SW_HIDE seems to correct
|
|
425
|
+
// that[2].
|
|
426
|
+
//
|
|
427
|
+
// Windows 7 moved a lot of console handling out of csrss.exe and into
|
|
428
|
+
// a per-console conhost.exe process, which may explain why it isn't
|
|
429
|
+
// affected.
|
|
430
|
+
//
|
|
431
|
+
// This is a somewhat risky change, so there are low-level flags to
|
|
432
|
+
// assist in debugging if there are issues.
|
|
433
|
+
//
|
|
434
|
+
// [1] https://github.com/rprichard/winpty/issues/58
|
|
435
|
+
// [2] https://github.com/rprichard/winpty/issues/70
|
|
436
|
+
bool ret = !shouldShowConsoleWindow() && !isAtLeastWindows7();
|
|
437
|
+
const bool force = hasDebugFlag("force_desktop");
|
|
438
|
+
const bool force_spawn = hasDebugFlag("force_desktop_spawn");
|
|
439
|
+
const bool force_curproc = hasDebugFlag("force_desktop_curproc");
|
|
440
|
+
const bool suppress = hasDebugFlag("no_desktop");
|
|
441
|
+
if (force + force_spawn + force_curproc + suppress > 1) {
|
|
442
|
+
trace("error: Only one of force_desktop, force_desktop_spawn, "
|
|
443
|
+
"force_desktop_curproc, and no_desktop may be set");
|
|
444
|
+
} else if (force) {
|
|
445
|
+
ret = true;
|
|
446
|
+
} else if (force_spawn) {
|
|
447
|
+
ret = true;
|
|
448
|
+
createUsingAgent = true;
|
|
449
|
+
} else if (force_curproc) {
|
|
450
|
+
ret = true;
|
|
451
|
+
createUsingAgent = false;
|
|
452
|
+
} else if (suppress) {
|
|
453
|
+
ret = false;
|
|
454
|
+
}
|
|
455
|
+
return ret;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
static bool shouldSpecifyHideFlag() {
|
|
459
|
+
const bool force = hasDebugFlag("force_sw_hide");
|
|
460
|
+
const bool suppress = hasDebugFlag("no_sw_hide");
|
|
461
|
+
bool ret = !shouldShowConsoleWindow();
|
|
462
|
+
if (force && suppress) {
|
|
463
|
+
trace("error: Both the force_sw_hide and no_sw_hide flags are set");
|
|
464
|
+
} else if (force) {
|
|
465
|
+
ret = true;
|
|
466
|
+
} else if (suppress) {
|
|
467
|
+
ret = false;
|
|
468
|
+
}
|
|
469
|
+
return ret;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
static OwnedHandle startAgentProcess(
|
|
473
|
+
const std::wstring &desktop,
|
|
474
|
+
const std::wstring &controlPipeName,
|
|
475
|
+
const std::wstring ¶ms,
|
|
476
|
+
DWORD creationFlags,
|
|
477
|
+
DWORD &agentPid) {
|
|
478
|
+
const std::wstring exePath = findAgentProgram();
|
|
479
|
+
const std::wstring cmdline =
|
|
480
|
+
(WStringBuilder(256)
|
|
481
|
+
<< L"\"" << exePath << L"\" "
|
|
482
|
+
<< controlPipeName << L' '
|
|
483
|
+
<< params).str_moved();
|
|
484
|
+
|
|
485
|
+
auto cmdlineV = vectorWithNulFromString(cmdline);
|
|
486
|
+
auto desktopV = vectorWithNulFromString(desktop);
|
|
487
|
+
|
|
488
|
+
// Start the agent.
|
|
489
|
+
STARTUPINFOW sui = {};
|
|
490
|
+
sui.cb = sizeof(sui);
|
|
491
|
+
sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
|
|
492
|
+
|
|
493
|
+
if (shouldSpecifyHideFlag()) {
|
|
494
|
+
sui.dwFlags |= STARTF_USESHOWWINDOW;
|
|
495
|
+
sui.wShowWindow = SW_HIDE;
|
|
496
|
+
}
|
|
497
|
+
PROCESS_INFORMATION pi = {};
|
|
498
|
+
const BOOL success =
|
|
499
|
+
CreateProcessW(exePath.c_str(),
|
|
500
|
+
cmdlineV.data(),
|
|
501
|
+
nullptr, nullptr,
|
|
502
|
+
/*bInheritHandles=*/FALSE,
|
|
503
|
+
/*dwCreationFlags=*/creationFlags,
|
|
504
|
+
nullptr, nullptr,
|
|
505
|
+
&sui, &pi);
|
|
506
|
+
if (!success) {
|
|
507
|
+
const DWORD lastError = GetLastError();
|
|
508
|
+
const auto errStr =
|
|
509
|
+
(WStringBuilder(256)
|
|
510
|
+
<< L"winpty-agent CreateProcess failed: cmdline='" << cmdline
|
|
511
|
+
<< L"' err=0x" << whexOfInt(lastError)).str_moved();
|
|
512
|
+
throw LibWinptyException(
|
|
513
|
+
WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str());
|
|
514
|
+
}
|
|
515
|
+
CloseHandle(pi.hThread);
|
|
516
|
+
TRACE("Created agent successfully, pid=%u, cmdline=%s",
|
|
517
|
+
static_cast<unsigned int>(pi.dwProcessId),
|
|
518
|
+
utf8FromWide(cmdline).c_str());
|
|
519
|
+
agentPid = pi.dwProcessId;
|
|
520
|
+
return OwnedHandle(pi.hProcess);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
static void verifyPipeClientPid(HANDLE serverPipe, DWORD agentPid) {
|
|
524
|
+
const auto client = getNamedPipeClientProcessId(serverPipe);
|
|
525
|
+
const auto success = std::get<0>(client);
|
|
526
|
+
const auto lastError = std::get<2>(client);
|
|
527
|
+
if (success == GetNamedPipeClientProcessId_Result::Success) {
|
|
528
|
+
const auto clientPid = std::get<1>(client);
|
|
529
|
+
if (clientPid != agentPid) {
|
|
530
|
+
WStringBuilder errMsg;
|
|
531
|
+
errMsg << L"Security check failed: pipe client pid (" << clientPid
|
|
532
|
+
<< L") does not match agent pid (" << agentPid << L")";
|
|
533
|
+
throwWinptyException(errMsg.c_str());
|
|
534
|
+
}
|
|
535
|
+
} else if (success == GetNamedPipeClientProcessId_Result::UnsupportedOs) {
|
|
536
|
+
trace("Pipe client PID security check skipped: "
|
|
537
|
+
"GetNamedPipeClientProcessId unsupported on this OS version");
|
|
538
|
+
} else {
|
|
539
|
+
throwWindowsError(L"GetNamedPipeClientProcessId failed", lastError);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
static std::unique_ptr<winpty_t>
|
|
544
|
+
createAgentSession(const winpty_config_t *cfg,
|
|
545
|
+
const std::wstring &desktop,
|
|
546
|
+
const std::wstring ¶ms,
|
|
547
|
+
DWORD creationFlags) {
|
|
548
|
+
std::unique_ptr<winpty_t> wp(new winpty_t);
|
|
549
|
+
wp->agentTimeoutMs = cfg->timeoutMs;
|
|
550
|
+
wp->ioEvent = createEvent();
|
|
551
|
+
|
|
552
|
+
// Create control server pipe.
|
|
553
|
+
const auto pipeName =
|
|
554
|
+
L"\\\\.\\pipe\\winpty-control-" + GenRandom().uniqueName();
|
|
555
|
+
wp->controlPipe = createControlPipe(pipeName);
|
|
556
|
+
|
|
557
|
+
DWORD agentPid = 0;
|
|
558
|
+
wp->agentProcess = startAgentProcess(
|
|
559
|
+
desktop, pipeName, params, creationFlags, agentPid);
|
|
560
|
+
connectControlPipe(*wp.get());
|
|
561
|
+
verifyPipeClientPid(wp->controlPipe.get(), agentPid);
|
|
562
|
+
|
|
563
|
+
return std::move(wp);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
namespace {
|
|
567
|
+
|
|
568
|
+
class AgentDesktop {
|
|
569
|
+
public:
|
|
570
|
+
virtual std::wstring name() = 0;
|
|
571
|
+
virtual ~AgentDesktop() {}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
class AgentDesktopDirect : public AgentDesktop {
|
|
575
|
+
public:
|
|
576
|
+
AgentDesktopDirect(BackgroundDesktop &&desktop) :
|
|
577
|
+
m_desktop(std::move(desktop))
|
|
578
|
+
{
|
|
579
|
+
}
|
|
580
|
+
std::wstring name() override { return m_desktop.desktopName(); }
|
|
581
|
+
private:
|
|
582
|
+
BackgroundDesktop m_desktop;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
class AgentDesktopIndirect : public AgentDesktop {
|
|
586
|
+
public:
|
|
587
|
+
AgentDesktopIndirect(std::unique_ptr<winpty_t> &&wp,
|
|
588
|
+
std::wstring &&desktopName) :
|
|
589
|
+
m_wp(std::move(wp)),
|
|
590
|
+
m_desktopName(std::move(desktopName))
|
|
591
|
+
{
|
|
592
|
+
}
|
|
593
|
+
std::wstring name() override { return m_desktopName; }
|
|
594
|
+
private:
|
|
595
|
+
std::unique_ptr<winpty_t> m_wp;
|
|
596
|
+
std::wstring m_desktopName;
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
} // anonymous namespace
|
|
600
|
+
|
|
601
|
+
std::unique_ptr<AgentDesktop>
|
|
602
|
+
setupBackgroundDesktop(const winpty_config_t *cfg) {
|
|
603
|
+
bool useDesktopAgent =
|
|
604
|
+
!(cfg->flags & WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION);
|
|
605
|
+
const bool useDesktop = shouldCreateBackgroundDesktop(useDesktopAgent);
|
|
606
|
+
|
|
607
|
+
if (!useDesktop) {
|
|
608
|
+
return std::unique_ptr<AgentDesktop>();
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (useDesktopAgent) {
|
|
612
|
+
auto wp = createAgentSession(
|
|
613
|
+
cfg, std::wstring(), L"--create-desktop", DETACHED_PROCESS);
|
|
614
|
+
|
|
615
|
+
// Read the desktop name.
|
|
616
|
+
auto packet = readPacket(*wp.get());
|
|
617
|
+
auto desktopName = packet.getWString();
|
|
618
|
+
packet.assertEof();
|
|
619
|
+
|
|
620
|
+
if (desktopName.empty()) {
|
|
621
|
+
return std::unique_ptr<AgentDesktop>();
|
|
622
|
+
} else {
|
|
623
|
+
return std::unique_ptr<AgentDesktop>(
|
|
624
|
+
new AgentDesktopIndirect(std::move(wp),
|
|
625
|
+
std::move(desktopName)));
|
|
626
|
+
}
|
|
627
|
+
} else {
|
|
628
|
+
try {
|
|
629
|
+
BackgroundDesktop desktop;
|
|
630
|
+
return std::unique_ptr<AgentDesktop>(new AgentDesktopDirect(
|
|
631
|
+
std::move(desktop)));
|
|
632
|
+
} catch (const WinptyException &e) {
|
|
633
|
+
trace("Error: failed to create background desktop, "
|
|
634
|
+
"using original desktop instead: %s",
|
|
635
|
+
utf8FromWide(e.what()).c_str());
|
|
636
|
+
return std::unique_ptr<AgentDesktop>();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
WINPTY_API winpty_t *
|
|
642
|
+
winpty_open(const winpty_config_t *cfg,
|
|
643
|
+
winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
644
|
+
API_TRY {
|
|
645
|
+
ASSERT(cfg != nullptr);
|
|
646
|
+
dumpWindowsVersion();
|
|
647
|
+
dumpVersionToTrace();
|
|
648
|
+
|
|
649
|
+
// Setup a background desktop for the agent.
|
|
650
|
+
auto desktop = setupBackgroundDesktop(cfg);
|
|
651
|
+
const auto desktopName = desktop ? desktop->name() : std::wstring();
|
|
652
|
+
|
|
653
|
+
// Start the primary agent session.
|
|
654
|
+
const auto params =
|
|
655
|
+
(WStringBuilder(128)
|
|
656
|
+
<< cfg->flags << L' '
|
|
657
|
+
<< cfg->mouseMode << L' '
|
|
658
|
+
<< cfg->cols << L' '
|
|
659
|
+
<< cfg->rows).str_moved();
|
|
660
|
+
auto wp = createAgentSession(cfg, desktopName, params,
|
|
661
|
+
CREATE_NEW_CONSOLE);
|
|
662
|
+
|
|
663
|
+
// Close handles to the background desktop and restore the original
|
|
664
|
+
// window station. This must wait until we know the agent is running
|
|
665
|
+
// -- if we close these handles too soon, then the desktop and
|
|
666
|
+
// windowstation will be destroyed before the agent can connect with
|
|
667
|
+
// them.
|
|
668
|
+
//
|
|
669
|
+
// If we used a separate agent process to create the desktop, we
|
|
670
|
+
// disconnect from that process here, allowing it to exit.
|
|
671
|
+
desktop.reset();
|
|
672
|
+
|
|
673
|
+
// If we ran the agent process on a background desktop, then when we
|
|
674
|
+
// spawn a child process from the agent, it will need to be explicitly
|
|
675
|
+
// placed back onto the original desktop.
|
|
676
|
+
if (!desktopName.empty()) {
|
|
677
|
+
wp->spawnDesktopName = getCurrentDesktopName();
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Get the CONIN/CONOUT pipe names.
|
|
681
|
+
auto packet = readPacket(*wp.get());
|
|
682
|
+
wp->coninPipeName = packet.getWString();
|
|
683
|
+
wp->conoutPipeName = packet.getWString();
|
|
684
|
+
if (cfg->flags & WINPTY_FLAG_CONERR) {
|
|
685
|
+
wp->conerrPipeName = packet.getWString();
|
|
686
|
+
}
|
|
687
|
+
packet.assertEof();
|
|
688
|
+
|
|
689
|
+
return wp.release();
|
|
690
|
+
} API_CATCH(nullptr)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
WINPTY_API HANDLE winpty_agent_process(winpty_t *wp) {
|
|
694
|
+
ASSERT(wp != nullptr);
|
|
695
|
+
return wp->agentProcess.get();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
/*****************************************************************************
|
|
701
|
+
* I/O pipes. */
|
|
702
|
+
|
|
703
|
+
static const wchar_t *cstrFromWStringOrNull(const std::wstring &str) {
|
|
704
|
+
try {
|
|
705
|
+
return str.c_str();
|
|
706
|
+
} catch (const std::bad_alloc&) {
|
|
707
|
+
return nullptr;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp) {
|
|
712
|
+
ASSERT(wp != nullptr);
|
|
713
|
+
return cstrFromWStringOrNull(wp->coninPipeName);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp) {
|
|
717
|
+
ASSERT(wp != nullptr);
|
|
718
|
+
return cstrFromWStringOrNull(wp->conoutPipeName);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp) {
|
|
722
|
+
ASSERT(wp != nullptr);
|
|
723
|
+
if (wp->conerrPipeName.empty()) {
|
|
724
|
+
return nullptr;
|
|
725
|
+
} else {
|
|
726
|
+
return cstrFromWStringOrNull(wp->conerrPipeName);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
/*****************************************************************************
|
|
733
|
+
* winpty agent RPC calls. */
|
|
734
|
+
|
|
735
|
+
namespace {
|
|
736
|
+
|
|
737
|
+
// Close the control pipe if something goes wrong with the pipe communication,
|
|
738
|
+
// which could leave the control pipe in an inconsistent state.
|
|
739
|
+
class RpcOperation {
|
|
740
|
+
public:
|
|
741
|
+
RpcOperation(winpty_t &wp) : m_wp(wp) {
|
|
742
|
+
if (m_wp.controlPipe.get() == nullptr) {
|
|
743
|
+
throwWinptyException(L"Agent shutdown due to RPC failure");
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
~RpcOperation() {
|
|
747
|
+
if (!m_success) {
|
|
748
|
+
trace("~RpcOperation: Closing control pipe");
|
|
749
|
+
m_wp.controlPipe.dispose(true);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
void success() { m_success = true; }
|
|
753
|
+
private:
|
|
754
|
+
winpty_t &m_wp;
|
|
755
|
+
bool m_success = false;
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
} // anonymous namespace
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
/*****************************************************************************
|
|
763
|
+
* winpty agent RPC call: process creation. */
|
|
764
|
+
|
|
765
|
+
// Return a std::wstring containing every character of the environment block.
|
|
766
|
+
// Typically, the block is non-empty, so the std::wstring returned ends with
|
|
767
|
+
// two NUL terminators. (These two terminators are counted in size(), so
|
|
768
|
+
// calling c_str() produces a triply-terminated string.)
|
|
769
|
+
static std::wstring wstringFromEnvBlock(const wchar_t *env) {
|
|
770
|
+
std::wstring envStr;
|
|
771
|
+
if (env != NULL) {
|
|
772
|
+
const wchar_t *p = env;
|
|
773
|
+
while (*p != L'\0') {
|
|
774
|
+
p += wcslen(p) + 1;
|
|
775
|
+
}
|
|
776
|
+
p++;
|
|
777
|
+
envStr.assign(env, p);
|
|
778
|
+
|
|
779
|
+
// Assuming the environment was non-empty, envStr now ends with two NUL
|
|
780
|
+
// terminators.
|
|
781
|
+
//
|
|
782
|
+
// If the environment were empty, though, then envStr would only be
|
|
783
|
+
// singly terminated, but the MSDN documentation thinks an env block is
|
|
784
|
+
// always doubly-terminated, so add an extra NUL just in case it
|
|
785
|
+
// matters.
|
|
786
|
+
const auto envStrSz = envStr.size();
|
|
787
|
+
if (envStrSz == 1) {
|
|
788
|
+
ASSERT(envStr[0] == L'\0');
|
|
789
|
+
envStr.push_back(L'\0');
|
|
790
|
+
} else {
|
|
791
|
+
ASSERT(envStrSz >= 3);
|
|
792
|
+
ASSERT(envStr[envStrSz - 3] != L'\0');
|
|
793
|
+
ASSERT(envStr[envStrSz - 2] == L'\0');
|
|
794
|
+
ASSERT(envStr[envStrSz - 1] == L'\0');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return envStr;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
WINPTY_API winpty_spawn_config_t *
|
|
801
|
+
winpty_spawn_config_new(UINT64 winptyFlags,
|
|
802
|
+
LPCWSTR appname /*OPTIONAL*/,
|
|
803
|
+
LPCWSTR cmdline /*OPTIONAL*/,
|
|
804
|
+
LPCWSTR cwd /*OPTIONAL*/,
|
|
805
|
+
LPCWSTR env /*OPTIONAL*/,
|
|
806
|
+
winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
807
|
+
API_TRY {
|
|
808
|
+
ASSERT((winptyFlags & WINPTY_SPAWN_FLAG_MASK) == winptyFlags);
|
|
809
|
+
std::unique_ptr<winpty_spawn_config_t> cfg(new winpty_spawn_config_t);
|
|
810
|
+
cfg->winptyFlags = winptyFlags;
|
|
811
|
+
if (appname != nullptr) { cfg->appname = appname; }
|
|
812
|
+
if (cmdline != nullptr) { cfg->cmdline = cmdline; }
|
|
813
|
+
if (cwd != nullptr) { cfg->cwd = cwd; }
|
|
814
|
+
if (env != nullptr) { cfg->env = wstringFromEnvBlock(env); }
|
|
815
|
+
return cfg.release();
|
|
816
|
+
} API_CATCH(nullptr)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg) {
|
|
820
|
+
delete cfg;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
|
|
824
|
+
// back to 64-bits. See the MSDN article, "Interprocess Communication Between
|
|
825
|
+
// 32-bit and 64-bit Applications".
|
|
826
|
+
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
|
|
827
|
+
static inline HANDLE handleFromInt64(int64_t i) {
|
|
828
|
+
return reinterpret_cast<HANDLE>(static_cast<intptr_t>(i));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Given a process and a handle in that process, duplicate the handle into the
|
|
832
|
+
// current process and close it in the originating process.
|
|
833
|
+
static inline OwnedHandle stealHandle(HANDLE process, HANDLE handle) {
|
|
834
|
+
HANDLE result = nullptr;
|
|
835
|
+
if (!DuplicateHandle(process, handle,
|
|
836
|
+
GetCurrentProcess(),
|
|
837
|
+
&result, 0, FALSE,
|
|
838
|
+
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
|
|
839
|
+
throwWindowsError(L"DuplicateHandle of process handle");
|
|
840
|
+
}
|
|
841
|
+
return OwnedHandle(result);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
WINPTY_API BOOL
|
|
845
|
+
winpty_spawn(winpty_t *wp,
|
|
846
|
+
const winpty_spawn_config_t *cfg,
|
|
847
|
+
HANDLE *process_handle /*OPTIONAL*/,
|
|
848
|
+
HANDLE *thread_handle /*OPTIONAL*/,
|
|
849
|
+
DWORD *create_process_error /*OPTIONAL*/,
|
|
850
|
+
winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
851
|
+
API_TRY {
|
|
852
|
+
ASSERT(wp != nullptr && cfg != nullptr);
|
|
853
|
+
|
|
854
|
+
if (process_handle != nullptr) { *process_handle = nullptr; }
|
|
855
|
+
if (thread_handle != nullptr) { *thread_handle = nullptr; }
|
|
856
|
+
if (create_process_error != nullptr) { *create_process_error = 0; }
|
|
857
|
+
|
|
858
|
+
LockGuard<Mutex> lock(wp->mutex);
|
|
859
|
+
RpcOperation rpc(*wp);
|
|
860
|
+
|
|
861
|
+
// Send spawn request.
|
|
862
|
+
auto packet = newPacket();
|
|
863
|
+
packet.putInt32(AgentMsg::StartProcess);
|
|
864
|
+
packet.putInt64(cfg->winptyFlags);
|
|
865
|
+
packet.putInt32(process_handle != nullptr);
|
|
866
|
+
packet.putInt32(thread_handle != nullptr);
|
|
867
|
+
packet.putWString(cfg->appname);
|
|
868
|
+
packet.putWString(cfg->cmdline);
|
|
869
|
+
packet.putWString(cfg->cwd);
|
|
870
|
+
packet.putWString(cfg->env);
|
|
871
|
+
packet.putWString(wp->spawnDesktopName);
|
|
872
|
+
writePacket(*wp, packet);
|
|
873
|
+
|
|
874
|
+
// Receive reply.
|
|
875
|
+
auto reply = readPacket(*wp);
|
|
876
|
+
const auto result = static_cast<StartProcessResult>(reply.getInt32());
|
|
877
|
+
if (result == StartProcessResult::CreateProcessFailed) {
|
|
878
|
+
const DWORD lastError = reply.getInt32();
|
|
879
|
+
reply.assertEof();
|
|
880
|
+
if (create_process_error != nullptr) {
|
|
881
|
+
*create_process_error = lastError;
|
|
882
|
+
}
|
|
883
|
+
rpc.success();
|
|
884
|
+
throw LibWinptyException(WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED,
|
|
885
|
+
L"CreateProcess failed");
|
|
886
|
+
} else if (result == StartProcessResult::ProcessCreated) {
|
|
887
|
+
const HANDLE remoteProcess = handleFromInt64(reply.getInt64());
|
|
888
|
+
const HANDLE remoteThread = handleFromInt64(reply.getInt64());
|
|
889
|
+
reply.assertEof();
|
|
890
|
+
OwnedHandle localProcess;
|
|
891
|
+
OwnedHandle localThread;
|
|
892
|
+
if (remoteProcess != nullptr) {
|
|
893
|
+
localProcess =
|
|
894
|
+
stealHandle(wp->agentProcess.get(), remoteProcess);
|
|
895
|
+
}
|
|
896
|
+
if (remoteThread != nullptr) {
|
|
897
|
+
localThread =
|
|
898
|
+
stealHandle(wp->agentProcess.get(), remoteThread);
|
|
899
|
+
}
|
|
900
|
+
if (process_handle != nullptr) {
|
|
901
|
+
*process_handle = localProcess.release();
|
|
902
|
+
}
|
|
903
|
+
if (thread_handle != nullptr) {
|
|
904
|
+
*thread_handle = localThread.release();
|
|
905
|
+
}
|
|
906
|
+
rpc.success();
|
|
907
|
+
} else {
|
|
908
|
+
throwWinptyException(
|
|
909
|
+
L"Agent RPC error: invalid StartProcessResult");
|
|
910
|
+
}
|
|
911
|
+
return TRUE;
|
|
912
|
+
} API_CATCH(FALSE)
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
/*****************************************************************************
|
|
918
|
+
* winpty agent RPC calls: everything else */
|
|
919
|
+
|
|
920
|
+
WINPTY_API BOOL
|
|
921
|
+
winpty_set_size(winpty_t *wp, int cols, int rows,
|
|
922
|
+
winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
923
|
+
API_TRY {
|
|
924
|
+
ASSERT(wp != nullptr && cols > 0 && rows > 0);
|
|
925
|
+
LockGuard<Mutex> lock(wp->mutex);
|
|
926
|
+
RpcOperation rpc(*wp);
|
|
927
|
+
auto packet = newPacket();
|
|
928
|
+
packet.putInt32(AgentMsg::SetSize);
|
|
929
|
+
packet.putInt32(cols);
|
|
930
|
+
packet.putInt32(rows);
|
|
931
|
+
writePacket(*wp, packet);
|
|
932
|
+
readPacket(*wp).assertEof();
|
|
933
|
+
rpc.success();
|
|
934
|
+
return TRUE;
|
|
935
|
+
} API_CATCH(FALSE)
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
WINPTY_API int
|
|
939
|
+
winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
|
|
940
|
+
winpty_error_ptr_t *err /*OPTIONAL*/) {
|
|
941
|
+
API_TRY {
|
|
942
|
+
ASSERT(wp != nullptr);
|
|
943
|
+
ASSERT(processList != nullptr);
|
|
944
|
+
LockGuard<Mutex> lock(wp->mutex);
|
|
945
|
+
RpcOperation rpc(*wp);
|
|
946
|
+
auto packet = newPacket();
|
|
947
|
+
packet.putInt32(AgentMsg::GetConsoleProcessList);
|
|
948
|
+
writePacket(*wp, packet);
|
|
949
|
+
auto reply = readPacket(*wp);
|
|
950
|
+
|
|
951
|
+
auto actualProcessCount = reply.getInt32();
|
|
952
|
+
|
|
953
|
+
if (actualProcessCount <= processCount) {
|
|
954
|
+
for (auto i = 0; i < actualProcessCount; i++) {
|
|
955
|
+
processList[i] = reply.getInt32();
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
reply.assertEof();
|
|
960
|
+
rpc.success();
|
|
961
|
+
return actualProcessCount;
|
|
962
|
+
} API_CATCH(0)
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
WINPTY_API void winpty_free(winpty_t *wp) {
|
|
966
|
+
// At least in principle, CloseHandle can fail, so this deletion can
|
|
967
|
+
// fail. It won't throw an exception, but maybe there's an error that
|
|
968
|
+
// should be propagated?
|
|
969
|
+
delete wp;
|
|
970
|
+
}
|