Skip to content

Instantly share code, notes, and snippets.

@aont
Last active October 30, 2021 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aont/0d40aab4ddd332d4da4d8e84cf625c6e to your computer and use it in GitHub Desktop.
Save aont/0d40aab4ddd332d4da4d8e84cf625c6e to your computer and use it in GitHub Desktop.
// EchoCon.cpp : Entry point for the EchoCon Pseudo-Console sample application.
// Copyright © 2018, Microsoft
// Modified by aont 2021/10/15
// #include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <cstdio>
// Forward declarations
HRESULT CreatePseudoConsoleAndPipes(HPCON*, HANDLE*, HANDLE*);
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX*, HPCON);
void __cdecl PipeListener(LPVOID);
int main()
{
wchar_t szCommand[]{ L"C:\\msys64\\usr\\bin\\bash.exe --login -i" };
HRESULT hr{ E_UNEXPECTED };
HANDLE hConsole = { GetStdHandle(STD_OUTPUT_HANDLE) };
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
// Enable Console VT Processing
DWORD consoleMode{};
GetConsoleMode(hin, &consoleMode);
hr = SetConsoleMode(hin, consoleMode ^ ENABLE_PROCESSED_INPUT ^ ENABLE_LINE_INPUT)
? S_OK
: GetLastError();
GetConsoleMode(hConsole, &consoleMode);
hr = SetConsoleMode(hConsole, consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
? S_OK
: GetLastError();
if (S_OK == hr)
{
HPCON hPC{ INVALID_HANDLE_VALUE };
// Create the Pseudo Console and pipes to it
HANDLE hPipeIn{ INVALID_HANDLE_VALUE };
HANDLE hPipeOut{ INVALID_HANDLE_VALUE };
hr = CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut);
if (S_OK == hr)
{
// Create & start thread to listen to the incoming pipe
// Note: Using CRT-safe _beginthread() rather than CreateThread()
HANDLE hPipeListenerThread{ reinterpret_cast<HANDLE>(_beginthread(PipeListener, 0, hPipeIn)) };
// Initialize the necessary startup info struct
STARTUPINFOEX startupInfo{};
if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hPC))
{
// Launch ping to emit some text back via the pipe
PROCESS_INFORMATION piClient{};
hr = CreateProcess(
NULL, // No module name - use Command Line
szCommand, // Command Line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Inherit handles
EXTENDED_STARTUPINFO_PRESENT, // Creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo.StartupInfo, // Pointer to STARTUPINFO
&piClient) // Pointer to PROCESS_INFORMATION
? S_OK
: GetLastError();
char input_key;
DWORD num_events;
while (true) {
bool ret;
ret = ReadFile(hin, &input_key, 1, &num_events, NULL);
if (ret) {
fprintf(stderr, "input: 0x%x\n", input_key);
WriteFile(hPipeOut, &input_key, 1, &num_events, NULL);
}
};
if (S_OK == hr)
{
// Wait up to 10s for ping process to complete
WaitForSingleObject(piClient.hThread, 10 * 1000);
// Allow listening thread to catch-up with final output!
Sleep(500);
}
// --- CLOSEDOWN ---
// Now safe to clean-up client app's process-info & thread
CloseHandle(piClient.hThread);
CloseHandle(piClient.hProcess);
// Cleanup attribute list
DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
free(startupInfo.lpAttributeList);
}
// Close ConPTY - this will terminate client process if running
ClosePseudoConsole(hPC);
// Clean-up the pipes
if (INVALID_HANDLE_VALUE != hPipeOut) CloseHandle(hPipeOut);
if (INVALID_HANDLE_VALUE != hPipeIn) CloseHandle(hPipeIn);
}
}
return S_OK == hr ? EXIT_SUCCESS : EXIT_FAILURE;
}
HRESULT CreatePseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut)
{
HRESULT hr{ E_UNEXPECTED };
HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE };
// Create the pipes to which the ConPTY will connect
if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
{
// Determine required size of Pseudo Console
COORD consoleSize{};
CONSOLE_SCREEN_BUFFER_INFO csbi{};
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };
if (GetConsoleScreenBufferInfo(hConsole, &csbi))
{
consoleSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
consoleSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
// Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);
// Note: We can close the handles to the PTY-end of the pipes here
// because the handles are dup'ed into the ConHost and will be released
// when the ConPTY is destroyed.
if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
}
return hr;
}
// Initializes the specified startup info struct with the required properties and
// updates its thread attribute list with the specified ConPTY handle
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
{
HRESULT hr{ E_UNEXPECTED };
if (pStartupInfo)
{
SIZE_T attrListSize{};
pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
// Get the size of the thread attribute list.
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
// Allocate a thread attribute list of the correct size
pStartupInfo->lpAttributeList =
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));
// Initialize thread attribute list
if (pStartupInfo->lpAttributeList
&& InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
{
// Set Pseudo Console attribute
hr = UpdateProcThreadAttribute(
pStartupInfo->lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hPC,
sizeof(HPCON),
NULL,
NULL)
? S_OK
: HRESULT_FROM_WIN32(GetLastError());
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
void __cdecl PipeListener(LPVOID pipe)
{
HANDLE hPipe{ pipe };
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };
const DWORD BUFF_SIZE{ 512 };
char szBuffer[BUFF_SIZE]{};
DWORD dwBytesWritten{};
DWORD dwBytesRead{};
BOOL fRead{ FALSE };
do
{
// Read from the pipe
fRead = ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);
// Write received text to the Console
// Note: Write to the Console using WriteFile(hConsole...), not printf()/puts() to
// prevent partially-read VT sequences from corrupting output
WriteFile(hConsole, szBuffer, dwBytesRead, &dwBytesWritten, NULL);
} while (fRead && dwBytesRead >= 0);
}
// original: https://blog.goo.ne.jp/lm324/e/16629a8aadaa0de77fc05611390cf15b
// modified by aont 2021/10/15
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int getkey(void)
{
struct termios oldt, newt;
int ch0,ch1,ch2,ch3,ch4;
int ret;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_iflag = ~( BRKINT | ISTRIP | IXON );
newt.c_lflag = ~( ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL );
newt.c_cc[VTIME] = 0;
newt.c_cc[VMIN] = 1;
newt.c_cc[VINTR] = 1;
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt)==-1) {
fprintf(stderr,"error tcsetattr\n");
exit(EXIT_FAILURE);
}
ch0 = getchar();
if(ch0==0x1B) {
ch1 = getchar();
ch2 = getchar();
if(ch2==0x32) {
ch3 = getchar();
if(ch3==0x7e) {
ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
} else {
ch4 = getchar();
ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
}
} else if(ch2==0x31) {
ch3 = getchar();
ch4 = getchar();
ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
} else if((ch2==0x33)||(ch2==0x35)||(ch2==0x36)) {
ch3 = getchar();
ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
} else {
ret = (ch0<<16) | (ch1<<8) | ch2;
}
} else if(ch0 != EOF) {
ret = ch0;
} else {
ret = 0;
}
if(tcsetattr(STDIN_FILENO, TCSANOW, &oldt)==-1) {
fprintf(stderr,"error tcsetattr\n");
exit(EXIT_FAILURE);
}
return ret;
}
int main(int argc,char *argv[])
{
int key;
while (1) {
key = getkey();
if (key!=0) {
printf("key code 0x%x\n",key);
}else{
;
usleep(1000);
}
if(key==0x71 /* q */ ) {
break;
}
}
return 0;
}
#include <setjmp.h>
#include <stdio.h>
#include <signal.h>
#include <readline/readline.h>
sigjmp_buf ctrlc_buf;
void handle_signals(int signo) {
if (signo == SIGINT) {
siglongjmp(ctrlc_buf, 1);
}
}
int main(int argc, char **argv)
{
char * input;
if (signal(SIGINT, handle_signals) == SIG_ERR) {
fprintf(stderr, "installing signal handler failed\n");
}
char buf[128];
while (1)
{
while ( sigsetjmp( ctrlc_buf, 1 ) != 0 ) {
fprintf(stderr, "Ctrl+C\n");
}
input = readline("> ");
if (!input)
break;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment