Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De Windows Pseudoconsole, ook wel pseudoconsole, ConPTY of Windows PTY genoemd, is een mechanisme dat is ontworpen voor het maken van een externe host voor subsysteemactiviteiten in de tekenmodus die het interactiviteitgedeelte van de gebruiker van het standaardconsolehostvenster vervangen.
Het hosten van een pseudoconsolesessie is iets anders dan een traditionele consolesessie. Traditionele consolesessies worden automatisch gestart wanneer het besturingssysteem herkent dat een toepassing in de tekenmodus op het punt staat te worden uitgevoerd. Daarentegen moeten een pseudoconsolesessie en de communicatiekanalen worden gemaakt door de hostingtoepassing voordat het proces wordt gemaakt met de onderliggende tekenmodustoepassing die moet worden gehost. Het onderliggende proces wordt nog steeds gemaakt met de functie CreateProcess , maar met aanvullende informatie waarmee het besturingssysteem de juiste omgeving tot stand brengt.
U vindt aanvullende achtergrondinformatie over dit systeem in de eerste aankondigingsblogpost.
Volledige voorbeelden van het gebruik van de Pseudoconsole zijn beschikbaar in onze GitHub-opslagplaats microsoft/terminal in de map met voorbeelden.
De communicatiekanalen voorbereiden
De eerste stap is het maken van een paar synchrone communicatiekanalen die worden verstrekt tijdens het maken van de pseudoconsolesessie voor bidirectionele communicatie met de gehoste toepassing. Deze kanalen worden verwerkt door het pseudoconsolesysteem met behulp van ReadFile en WriteFile met synchrone I/O. Bestands- of I/O-apparaatgrepen zoals een bestandsstroom of pijp zijn acceptabel zolang een OVERLAPPED-structuur niet vereist is voor asynchrone communicatie.
Waarschuwing
Om raceomstandigheden en impasses te voorkomen, raden we u ten zeerste aan dat elk van de communicatiekanalen wordt onderhouden op een afzonderlijke thread die een eigen clientbufferstatus en berichtenwachtrij in uw toepassing onderhoudt. Het uitvoeren van alle pseudoconsole-activiteiten op dezelfde thread kan leiden tot een impasse waarbij een van de communicatiebuffers wordt gevuld en wacht op uw actie terwijl u een blokkeringsaanvraag op een ander kanaal probeert te verzenden.
De Pseudoconsole maken
Met de communicatiekanalen die tot stand zijn gebracht, identificeert u het einde van het invoerkanaal en het 'write'-einde van het uitvoerkanaal. Dit paar ingangen wordt geleverd bij het aanroepen van CreatePseudoConsole om het object te maken.
Bij het maken is een grootte voor de X- en Y-dimensies (in aantal tekens) vereist. Dit zijn de afmetingen die van toepassing zijn op het weergaveoppervlak voor het uiteindelijke (terminal) presentatievenster. De waarden worden gebruikt om een buffer in het geheugen te maken in het pseudoconsolesysteem.
De buffergrootte biedt antwoorden op client-tekenmodustoepassingen die testen op informatie met behulp van de consolefuncties aan de clientzijde zoals GetConsoleScreenBufferInfoEx en bepaalt de indeling en plaatsing van tekst wanneer clients functies zoals WriteConsoleOutput gebruiken.
Ten slotte wordt een vlagveld geboden bij het maken van een pseudoconsole om speciale functionaliteit uit te voeren. Stel deze standaard in op 0 om geen speciale functionaliteit te hebben.
Op dit moment is slechts één speciale vlag beschikbaar om de overname van de cursorpositie aan te vragen van een consolesessie die al is gekoppeld aan de aanroeper van de pseudoconsole-API. Dit is bedoeld voor gebruik in geavanceerdere scenario's waarbij een hostingtoepassing die een pseudoconsolesessie voorbereidt, ook een client-tekenmodustoepassing van een andere consoleomgeving is.
Hieronder vindt u een voorbeeldfragment waarin CreatePipe wordt gebruikt om een paar communicatiekanalen tot stand te brengen en de pseudoconsole te maken.
HRESULT SetUpPseudoConsole(COORD size)
{
HRESULT hr = S_OK;
// Create communication channels
// - Close these after CreateProcess of child application with pseudoconsole object.
HANDLE inputReadSide, outputWriteSide;
// - Hold onto these and use them for communication with the child through the pseudoconsole.
HANDLE outputReadSide, inputWriteSide;
if (!CreatePipe(&inputReadSide, &inputWriteSide, NULL, 0))
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (!CreatePipe(&outputReadSide, &outputWriteSide, NULL, 0))
{
return HRESULT_FROM_WIN32(GetLastError());
}
HPCON hPC;
hr = CreatePseudoConsole(size, inputReadSide, outputWriteSide, 0, &hPC);
if (FAILED(hr))
{
return hr;
}
// ...
}
Opmerking
Dit fragment is onvolledig en wordt alleen gebruikt voor demonstratie van deze specifieke aanroep. U moet de levensduur van de HANDLEop de juiste manier beheren. Als u de levensduur van HANDLEs niet correct beheert, kan dit leiden tot impassescenario's, met name bij synchrone I/O-aanroepen.
Na voltooiing van de createProcess-aanroep om de client-tekenmodustoepassing te maken die is gekoppeld aan de pseudoconsole, moeten de ingangen die tijdens het maken zijn opgegeven, worden losgekoppeld van dit proces. Dit vermindert het aantal verwijzingen op het onderliggende apparaatobject en staat I/O-bewerkingen toe om een verbroken kanaal correct te detecteren wanneer de pseudoconsolesessie de kopie van de ingangen sluit.
Voorbereiden voor het maken van het onderliggende proces
De volgende fase is het voorbereiden van de STARTUPINFOEX-structuur die de pseudoconsole-informatie overbrengt tijdens het starten van het onderliggende proces.
Deze structuur bevat de mogelijkheid om complexe opstartinformatie te bieden, waaronder kenmerken voor het maken van processen en threads.
Gebruik InitializeProcThreadAttributeList op een dubbele aanroep om eerst het aantal bytes te berekenen dat nodig is voor het opslaan van de lijst, het aangevraagde geheugen toe te wijzen en vervolgens opnieuw aan te roepen, zodat de ondoorzichtige geheugenaanwijzer moet worden ingesteld als de kenmerklijst.
Roep vervolgens UpdateProcThreadAttribute aan die de geïnitialiseerde kenmerklijst doorgeeft met de vlag PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, de pseudoconsolegreep en de grootte van de pseudoconsolegreep.
HRESULT PrepareStartupInformation(HPCON hpc, STARTUPINFOEX* psi)
{
// Prepare Startup Information structure
STARTUPINFOEX si;
ZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEX);
// Discover the size required for the list
size_t bytesRequired;
InitializeProcThreadAttributeList(NULL, 1, 0, &bytesRequired);
// Allocate memory to represent the list
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, bytesRequired);
if (!si.lpAttributeList)
{
return E_OUTOFMEMORY;
}
// Initialize the list memory location
if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &bytesRequired))
{
HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
return HRESULT_FROM_WIN32(GetLastError());
}
// Set the pseudoconsole information into the list
if (!UpdateProcThreadAttribute(si.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hpc,
sizeof(hpc),
NULL,
NULL))
{
HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
return HRESULT_FROM_WIN32(GetLastError());
}
*psi = si;
return S_OK;
}
Het gehoste proces maken
Roep vervolgens CreateProcess aan die de STRUCTUUR STARTUPINFOEX doorgeeft, samen met het pad naar het uitvoerbare bestand en eventuele aanvullende configuratiegegevens, indien van toepassing. Het is belangrijk om de EXTENDED_STARTUPINFO_PRESENT vlag in te stellen bij het aanroepen om het systeem te waarschuwen dat de pseudoconsole-verwijzing is opgenomen in de uitgebreide informatie.
HRESULT SetUpPseudoConsole(COORD size)
{
// ...
PCWSTR childApplication = L"C:\\windows\\system32\\cmd.exe";
// Create mutable text string for CreateProcessW command line string.
const size_t charsRequired = wcslen(childApplication) + 1; // +1 null terminator
PWSTR cmdLineMutable = (PWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t) * charsRequired);
if (!cmdLineMutable)
{
return E_OUTOFMEMORY;
}
wcscpy_s(cmdLineMutable, charsRequired, childApplication);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
// Call CreateProcess
if (!CreateProcessW(NULL,
cmdLineMutable,
NULL,
NULL,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
&siEx.StartupInfo,
&pi))
{
HeapFree(GetProcessHeap(), 0, cmdLineMutable);
return HRESULT_FROM_WIN32(GetLastError());
}
// ...
}
Opmerking
Als u de pseudoconsolesessie sluit terwijl het gehoste proces nog steeds wordt gestart en er verbinding wordt gemaakt, kan er een foutdialoogvenster worden weergegeven door de clienttoepassing. Hetzelfde foutdialoogvenster wordt weergegeven als het gehoste proces een ongeldige pseudoconsole-ingang krijgt voor het opstarten. Voor de gehoste initialisatiecode voor processen zijn de twee omstandigheden identiek. Het pop-updialoogvenster van de gehoste clienttoepassing over een fout wordt gelezen 0xc0000142 met een gelokaliseerd bericht met details van de initialisatiefout.
Communiceren met de Pseudoconsole-sessie
Zodra het proces is gemaakt, kan de hostingtoepassing het schrijfeinde van de invoerpijp gebruiken om gebruikersinteractiegegevens naar de pseudoconsole en het leeseinde van de uitvoerpijp te verzenden om grafische presentatiegegevens van de pseudoconsole te ontvangen.
Het is volledig aan de hostingtoepassing om te bepalen hoe verdere activiteiten moeten worden verwerkt. De hostingtoepassing kan een venster in een andere thread starten om invoer van gebruikersinteractie te verzamelen en deze te serialiseren in het schrijfeinde van de invoerpijp voor de pseudoconsole en de gehoste tekenmodustoepassing. Er kan een andere thread worden gestart om het leeseinde van de uitvoerpijp voor de pseudoconsole leeg te maken, de tekst- en virtuele terminalreeksinformatie te decoderen en dit aan het scherm te presenteren.
Threads kunnen ook worden gebruikt om de informatie van de pseudoconsolekanalen door te sturen naar een ander kanaal of apparaat, inclusief een netwerk naar externe informatie naar een ander proces of een andere computer en om lokale transcodering van de informatie te voorkomen.
Het formaat van de Pseudoconsole wijzigen
Gedurende de loop van de runtime kan er een omstandigheid zijn waardoor de grootte van de buffer moet worden gewijzigd vanwege een gebruikersinteractie of een aanvraag die buiten de band is ontvangen van een ander apparaat voor weergave/interactie.
Dit kan worden gedaan met de functie ResizePseudoConsole die zowel de hoogte als de breedte van de buffer aangeeft in een aantal tekens.
// Theoretical event handler function with theoretical
// event that has associated display properties
// on Source property.
void OnWindowResize(Event e)
{
// Retrieve width and height dimensions of display in
// characters using theoretical height/width functions
// that can retrieve the properties from the display
// attached to the event.
COORD size;
size.X = GetViewWidth(e.Source);
size.Y = GetViewHeight(e.Source);
// Call pseudoconsole API to inform buffer dimension update
ResizePseudoConsole(m_hpc, size);
}
De pseudoconsolesessie beëindigen
Als u de sessie wilt beëindigen, roept u de functie ClosePseudoConsole aan met de ingang van de oorspronkelijke pseudoconsole-creatie. Alle gekoppelde client-tekenmodustoepassingen, zoals de client van de aanroep CreateProcess , worden beëindigd wanneer de sessie wordt gesloten. Als het oorspronkelijke onderliggende element een shell-type toepassing is waarmee andere processen worden gemaakt, worden alle gerelateerde gekoppelde processen in de structuur ook beëindigd.
Waarschuwing
Het sluiten van de sessie heeft verschillende bijwerkingen die kunnen leiden tot een impasse als de pseudoconsole op een synchrone wijze met één thread wordt gebruikt. De handeling van het sluiten van de pseudoconsolesessie kan een definitieve frameupdate hOutput verzenden waarnaar de communicatiekanaalbuffer moet worden verwijderd.
PSEUDOCONSOLE_INHERIT_CURSOR Als deze optie is geselecteerd tijdens het maken van de pseudoconsole, kan een poging om de pseudoconsole te sluiten zonder te reageren op het bericht over de overnamequery van de cursor (ontvangen hOutput en beantwoord viahInput) tot een andere impasse leiden. Het wordt aanbevolen dat communicatiekanalen voor de pseudoconsole worden onderhouden op afzonderlijke threads en blijven leeg en verwerkt totdat de clienttoepassing wordt afgesloten of door het voltooien van de teardown-activiteiten bij het aanroepen van de functie ClosePseudoConsole .