mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-07 14:50:55 +00:00
Compare commits
277 Commits
experiment
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00db8704b6 | ||
|
|
71e19cd825 | ||
|
|
dadd74c3c6 | ||
|
|
1c16b2c06b | ||
|
|
c3e32eb1ca | ||
|
|
e52170e2cf | ||
|
|
3d7160d731 | ||
|
|
d24d647c0d | ||
|
|
e2b5fecd48 | ||
|
|
097f7d32a6 | ||
|
|
2f88c46350 | ||
|
|
8baba4b46c | ||
|
|
cfc72cee5d | ||
|
|
9ad2544033 | ||
|
|
5f938a0465 | ||
|
|
4c47631bf4 | ||
|
|
cc30475955 | ||
|
|
80f107965d | ||
|
|
42e87ed3e3 | ||
|
|
40b557a4b6 | ||
|
|
0f62ec81d8 | ||
|
|
ce0eaab9ac | ||
|
|
1c50968333 | ||
|
|
efd69990c6 | ||
|
|
2fdcb679ab | ||
|
|
1191a59681 | ||
|
|
8dab297bd1 | ||
|
|
3f95d58805 | ||
|
|
2d4eca7f4f | ||
|
|
547cba968c | ||
|
|
798912c2f4 | ||
|
|
06a5583c86 | ||
|
|
82e75ce3e2 | ||
|
|
5ec7c0325e | ||
|
|
bbbd3e0323 | ||
|
|
1d9cdb3d31 | ||
|
|
8c3af2d066 | ||
|
|
e9a3d16286 | ||
|
|
83b139596f | ||
|
|
6a79025027 | ||
|
|
20359d40e4 | ||
|
|
6c7dfd2ce4 | ||
|
|
080843f826 | ||
|
|
db637021fd | ||
|
|
8da6737d64 | ||
|
|
fd2fb07bcf | ||
|
|
68d0c23246 | ||
|
|
29e380824f | ||
|
|
acabbe0459 | ||
|
|
461c8b53fa | ||
|
|
dd9bc6ee45 | ||
|
|
0060614173 | ||
|
|
37ea2dce48 | ||
|
|
9f4ad6d1ce | ||
|
|
a0ebd2ed1b | ||
|
|
67f68ebf62 | ||
|
|
41a6d8ed3a | ||
|
|
7533b31cbd | ||
|
|
73ad742c12 | ||
|
|
fd98145af2 | ||
|
|
2b41fad198 | ||
|
|
251505b96b | ||
|
|
723ff47789 | ||
|
|
7291121112 | ||
|
|
22103ff9c6 | ||
|
|
e0f131121b | ||
|
|
bc925d8909 | ||
|
|
e3764b2081 | ||
|
|
a12521ffd3 | ||
|
|
f867a2d4a4 | ||
|
|
3a27b29afc | ||
|
|
781d779b37 | ||
|
|
bc69d1a99a | ||
|
|
de24334898 | ||
|
|
507d787fe8 | ||
|
|
ad27906db7 | ||
|
|
fb72dca939 | ||
|
|
ea5270e563 | ||
|
|
b5eeddfb0f | ||
|
|
7c6278de44 | ||
|
|
303e227f44 | ||
|
|
a8cf3d6f4a | ||
|
|
71229239d4 | ||
|
|
639d5f3f93 | ||
|
|
df789a4e75 | ||
|
|
ef8e20af51 | ||
|
|
8c177fab4f | ||
|
|
2c1ab620bf | ||
|
|
af3a421938 | ||
|
|
04c7b944bd | ||
|
|
c2ee6277f8 | ||
|
|
5b1183a4b3 | ||
|
|
6c80ab8017 | ||
|
|
fc49caca8a | ||
|
|
aeef340bdc | ||
|
|
9ba3a53b4b | ||
|
|
1e478ae99d | ||
|
|
dc7fff7ab0 | ||
|
|
6088134832 | ||
|
|
bf460ab7fe | ||
|
|
5c707032a7 | ||
|
|
660d31ac52 | ||
|
|
644cd3ec6c | ||
|
|
99555ef9e9 | ||
|
|
cafe59d73c | ||
|
|
f74a9d3e0b | ||
|
|
6c98fc19f5 | ||
|
|
37fd00c822 | ||
|
|
5dc7d0e843 | ||
|
|
32f4f7133c | ||
|
|
af7316c130 | ||
|
|
5a8e746d82 | ||
|
|
b3b98373c6 | ||
|
|
ec44bf0068 | ||
|
|
cb17115c72 | ||
|
|
2aed13ac37 | ||
|
|
e37ba7a923 | ||
|
|
a7404a2df9 | ||
|
|
d5b8e7c32f | ||
|
|
ec38580042 | ||
|
|
599a8dff0f | ||
|
|
e6767acf46 | ||
|
|
5948b95cd8 | ||
|
|
79c74aadff | ||
|
|
58ec47236d | ||
|
|
501a4a5e59 | ||
|
|
dda4ef23c8 | ||
|
|
1c345515b8 | ||
|
|
590eb1fc91 | ||
|
|
b35c801093 | ||
|
|
688483c3af | ||
|
|
fc83699c1d | ||
|
|
f9f2525c72 | ||
|
|
47cebce11c | ||
|
|
f1309ee211 | ||
|
|
35229a775d | ||
|
|
82b9efc1c6 | ||
|
|
b726a3d05d | ||
|
|
23f85d01f0 | ||
|
|
4ab4051f63 | ||
|
|
87e85603b9 | ||
|
|
00bb050826 | ||
|
|
864f45fa11 | ||
|
|
723efc70e2 | ||
|
|
2d1055d153 | ||
|
|
987805ebaf | ||
|
|
f8f4f263a5 | ||
|
|
2e0fd58bc5 | ||
|
|
cfe3eb9624 | ||
|
|
370cea5cab | ||
|
|
5bd3f887b1 | ||
|
|
2dc178b852 | ||
|
|
12fff3126b | ||
|
|
b61cb830c3 | ||
|
|
05f518db5b | ||
|
|
7daea0a25c | ||
|
|
7eea98d4ea | ||
|
|
cade139e0c | ||
|
|
619a80ea14 | ||
|
|
2661fbe0b9 | ||
|
|
a247624e90 | ||
|
|
3484e07089 | ||
|
|
9bf9a6f62c | ||
|
|
c1e1f5124c | ||
|
|
3990a68770 | ||
|
|
ecea0c9f40 | ||
|
|
b38f6ffbd1 | ||
|
|
e6500864bc | ||
|
|
ee8589110a | ||
|
|
99f71a0cc5 | ||
|
|
16b1b059a4 | ||
|
|
cafe71c50b | ||
|
|
1145336538 | ||
|
|
a30f56645a | ||
|
|
14f9cfc389 | ||
|
|
52ef47533b | ||
|
|
f3e53f1dac | ||
|
|
ae5be18556 | ||
|
|
4a30b1868b | ||
|
|
7e5c034b7f | ||
|
|
058b3f5d19 | ||
|
|
df1843c87d | ||
|
|
10b05fbe23 | ||
|
|
66bc1f547e | ||
|
|
34ff272cfa | ||
|
|
9971abf4e4 | ||
|
|
5456666d35 | ||
|
|
bb795f5258 | ||
|
|
2791753780 | ||
|
|
e5aa14ea7b | ||
|
|
e25ca32022 | ||
|
|
bb5088ae6c | ||
|
|
4272e9c8e9 | ||
|
|
15c2e57b96 | ||
|
|
0c3872d577 | ||
|
|
7371ed764d | ||
|
|
f1627dd571 | ||
|
|
046475f7ab | ||
|
|
2c33edcba9 | ||
|
|
08b436f1a5 | ||
|
|
3a1ee61476 | ||
|
|
bf32b8d48f | ||
|
|
637c57473e | ||
|
|
e09359138e | ||
|
|
0884a1bb1d | ||
|
|
d8ab20d970 | ||
|
|
ac843745fa | ||
|
|
1e6232b751 | ||
|
|
8dacee626a | ||
|
|
da53ff957f | ||
|
|
11a65ef0c7 | ||
|
|
aa20e89a84 | ||
|
|
f334ba68c9 | ||
|
|
8afb12e747 | ||
|
|
b3b9f719fa | ||
|
|
ae5b5fb5b1 | ||
|
|
3d0f15d433 | ||
|
|
d3678caea7 | ||
|
|
f735286a7d | ||
|
|
aed5f9eae9 | ||
|
|
b2ed728bf1 | ||
|
|
8d75ff1bec | ||
|
|
a9973139e4 | ||
|
|
78e50fcf26 | ||
|
|
5885732a2d | ||
|
|
ae4e0086a9 | ||
|
|
3f76c471d0 | ||
|
|
d6e77edc27 | ||
|
|
1465871e98 | ||
|
|
198c75cf26 | ||
|
|
550e197684 | ||
|
|
4c3065fec7 | ||
|
|
cad9d55e41 | ||
|
|
21b618648b | ||
|
|
d66df17bcb | ||
|
|
ddb74ef5db | ||
|
|
49b3df3d71 | ||
|
|
22dd8a8e01 | ||
|
|
e0248749eb | ||
|
|
eba68a04b6 | ||
|
|
85e347fc4a | ||
|
|
9a5393d49d | ||
|
|
48021e1d75 | ||
|
|
5b47a58a3a | ||
|
|
889e19a05a | ||
|
|
dc9cab01cd | ||
|
|
f052ca5dc4 | ||
|
|
e9d52f7f0f | ||
|
|
a8c1100bfa | ||
|
|
fe7a2d00fa | ||
|
|
7ae6ee6e00 | ||
|
|
9bd6053664 | ||
|
|
cf4780a4a2 | ||
|
|
c12f5a9157 | ||
|
|
ce43f9af2a | ||
|
|
fed6302eeb | ||
|
|
f757ecaa29 | ||
|
|
4964aad780 | ||
|
|
067cf3a29b | ||
|
|
7899586b81 | ||
|
|
27fa917ada | ||
|
|
dd4f1fd296 | ||
|
|
b1fb72f237 | ||
|
|
bcb46c1e8d | ||
|
|
864c54f918 | ||
|
|
1a6f3f89c8 | ||
|
|
32e5d40777 | ||
|
|
1b875aafb3 | ||
|
|
21dea14db1 | ||
|
|
7ed7650a6b | ||
|
|
10d1f05c8d | ||
|
|
a88194e891 | ||
|
|
0a423074a3 | ||
|
|
c0f5c3e149 | ||
|
|
b0d43f8067 | ||
|
|
a313826830 | ||
|
|
1be2939f25 |
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{*.cpp,*.c,*.hpp,*.h,*.cs}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
39
.github/ISSUE_TEMPLATE/Bug_Report.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/Bug_Report.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug report 🐛
|
||||
about: Report errors or unexpected behavior
|
||||
title: "Bug Report"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
This bug tracker is monitored by Windows Terminal development team and other technical folks.
|
||||
|
||||
**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
|
||||
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.
|
||||
|
||||
Please use this form and describe your issue, concisely but precisely, with as much detail as possible.
|
||||
|
||||
-->
|
||||
|
||||
# Environment
|
||||
|
||||
```none
|
||||
Windows build number: [run "ver" at a command prompt]
|
||||
Windows Terminal version (if applicable):
|
||||
|
||||
Any other software?
|
||||
```
|
||||
|
||||
# Steps to reproduce
|
||||
|
||||
<!-- A description of how to trigger this bug. -->
|
||||
|
||||
# Expected behavior
|
||||
|
||||
<!-- A description of what you're expecting, possibly containing screenshots or reference material. -->
|
||||
|
||||
# Actual behavior
|
||||
|
||||
<!-- What's actually happening? -->
|
||||
10
.github/ISSUE_TEMPLATE/Documentation_Issue.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/Documentation_Issue.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Documentation Issue 📚
|
||||
about: Report issues in our documentation
|
||||
title: "Documentation Issue"
|
||||
labels: Issue-Docs
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Briefly describe which document needs to be corrected and why. -->
|
||||
21
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Feature Request/Idea 🚀
|
||||
about: Suggest a new feature or improvement (this does not mean you have to implement it)
|
||||
title: "Feature Request"
|
||||
labels: Issue-Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Summary of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
Describe why and how a user would use this new functionality (if applicable).
|
||||
-->
|
||||
|
||||
# Proposed technical implementation details (optional)
|
||||
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
10
.github/ISSUE_TEMPLATE/Guidance_Issue.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/Guidance_Issue.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Community Guidance Request ✨
|
||||
about: Suggest somewhere the Windows Terminal Team needs to provide community guidance through new documentation or process.
|
||||
title: "Guidance"
|
||||
labels: Issue-Docs
|
||||
assignees: 'bitcrazed'
|
||||
|
||||
---
|
||||
|
||||
<!-- What needs to change? Who is responsible for it? Why is it an open question? -->
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
|
||||
## Summary of the Pull Request
|
||||
|
||||
<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
|
||||
## References
|
||||
|
||||
<!-- Please review the items on the PR checklist before submitting-->
|
||||
## PR Checklist
|
||||
* [ ] Closes #xxx
|
||||
* [ ] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
|
||||
* [ ] Tests added/passed
|
||||
* [ ] Requires documentation to be updated
|
||||
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
|
||||
|
||||
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
|
||||
## Detailed Description of the Pull Request / Additional comments
|
||||
62
.gitignore
vendored
62
.gitignore
vendored
@@ -17,10 +17,11 @@
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
ARM64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
objfre/
|
||||
objchk/
|
||||
|
||||
@@ -79,14 +80,18 @@ _Chutzpah*
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
@@ -111,6 +116,7 @@ _TeamCity*
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
@@ -138,13 +144,16 @@ publish/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
## TODO: Comment the next line if you want to checkin your
|
||||
## web deploy settings but do note that will include unencrypted
|
||||
## passwords
|
||||
#*.pubxml
|
||||
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
@@ -153,13 +162,23 @@ publish/
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Windows Azure Build Output
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
@@ -211,12 +230,25 @@ FakesAssemblies/
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
*.opendb
|
||||
*.db
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
*.exe
|
||||
|
||||
# Windows Build System files
|
||||
build*.dbb
|
||||
@@ -238,7 +270,7 @@ MSG*.bin
|
||||
# python
|
||||
*.pyc
|
||||
|
||||
**Generated Files/
|
||||
**/Generated Files/
|
||||
**/Merged/*
|
||||
**/Unmerged/*
|
||||
profiles.json
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,6 +1,6 @@
|
||||
[submodule "dep/gsl"]
|
||||
path = dep/gsl
|
||||
url = https://github.com/Microsoft/gsl
|
||||
url = https://github.com/microsoft/gsl
|
||||
[submodule "dep/wil"]
|
||||
path = dep/wil
|
||||
url = https://github.com/Microsoft/wil
|
||||
url = https://github.com/microsoft/wil
|
||||
|
||||
4
.nuget/packages.config
Normal file
4
.nuget/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="vswhere" version="2.6.7" />
|
||||
</packages>
|
||||
34
.vsconfig
Normal file
34
.vsconfig
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.VisualStudio.Component.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.Universal",
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.Workload.ManagedDesktop",
|
||||
"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
|
||||
"Microsoft.VisualStudio.Component.NuGet",
|
||||
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
|
||||
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
|
||||
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
|
||||
"Microsoft.Component.MSBuild",
|
||||
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
|
||||
"Microsoft.Net.Component.4.TargetingPack",
|
||||
"Microsoft.Net.Component.4.5.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.DiagnosticTools",
|
||||
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.18362",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
"Microsoft.VisualStudio.Component.Graphics",
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.ARM64",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
|
||||
]
|
||||
}
|
||||
8
CODE_OF_CONDUCT.md
Normal file
8
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code].
|
||||
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[conduct-email]: mailto:opencode@microsoft.com
|
||||
25
NuGet.Config
25
NuGet.Config
@@ -1,14 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<!-- Add repositories here to the list of available repositories -->
|
||||
|
||||
<!-- Dependencies that we must carry because they're not on public nuget feeds right now. -->
|
||||
<add key="Static Package Dependencies" value="dep\packages" />
|
||||
|
||||
<!-- Internal NuGet feeds that may not be accessible outside Microsoft corporate network -->
|
||||
<!--<add key="TAEF - internal" value="https://microsoft.pkgs.visualstudio.com/DefaultCollection/_packaging/Taef/nuget/v3/index.json" />
|
||||
<add key="OpenConsole - Internal" value="https://microsoft.pkgs.visualstudio.com/_packaging/OpenConsole/nuget/v3/index.json" />-->
|
||||
</packageSources>
|
||||
<packageSources>
|
||||
<add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<!-- Add repositories here to the list of available repositories -->
|
||||
|
||||
<!-- Dependencies that we must carry because they're not on public nuget feeds right now. -->
|
||||
<add key="Static Package Dependencies" value="dep\packages" />
|
||||
|
||||
<!-- Internal NuGet feeds that may not be accessible outside Microsoft corporate network -->
|
||||
<!--<add key="TAEF - internal" value="https://microsoft.pkgs.visualstudio.com/DefaultCollection/_packaging/Taef/nuget/v3/index.json" />
|
||||
<add key="OpenConsole - Internal" value="https://microsoft.pkgs.visualstudio.com/_packaging/OpenConsole/nuget/v3/index.json" />-->
|
||||
</packageSources>
|
||||
<config>
|
||||
<add key="repositorypath" value=".\packages" />
|
||||
</config>
|
||||
</configuration>
|
||||
|
||||
@@ -71,7 +71,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Unit", "src\host
|
||||
{06EC74CB-9A12-429C-B551-8562EC954747} = {06EC74CB-9A12-429C-B551-8562EC954747}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextBuffer.UnitTests", "src\buffer\out\ut_textbuffer\TextBuffer.UnitTests.vcxproj", "{531C23E7-4B76-4C08-8BBD-04164CB628C9}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextBuffer.Unit.Tests", "src\buffer\out\ut_textbuffer\TextBuffer.Unit.Tests.vcxproj", "{531C23E7-4B76-4C08-8BBD-04164CB628C9}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
|
||||
EndProjectSection
|
||||
@@ -228,6 +228,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Buffer", "Buffer", "{1E4A06
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{89CDCC5C-9F53-4054-97A4-639D99F169CD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Types.Unit.Tests", "src\types\ut_types\Types.Unit.Tests.vcxproj", "{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|ARM64 = AuditMode|ARM64
|
||||
@@ -1099,6 +1101,24 @@ Global
|
||||
{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x64.Build.0 = Release|x64
|
||||
{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.ActiveCfg = Release|Win32
|
||||
{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.Build.0 = Release|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x64.Build.0 = AuditMode|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x64.Build.0 = Debug|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x86.Build.0 = Debug|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x64.ActiveCfg = Release|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x64.Build.0 = Release|x64
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.ActiveCfg = Release|Win32
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1157,6 +1177,7 @@ Global
|
||||
{F1995847-4AE5-479A-BBAF-382E51A63532} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{05500DEF-2294-41E3-AF9A-24E580B82836} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{1E4A062E-293B-4817-B20D-BF16B979E350} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
223
README.md
223
README.md
@@ -1,45 +1,178 @@
|
||||
# Welcome to the Console Project!
|
||||
|
||||
This project is currently controlled by the Windows Developer Platform Tools & Runtimes' Open Source Software team (*WDG > DEP > DART > OSS*).
|
||||
|
||||
Our team can be reached at `dartcon@microsoft.com`.
|
||||
|
||||
The code is stored at <https://microsoft.visualstudio.com/Dart/_git/OpenConsole>.
|
||||
|
||||
The area path within the Microsoft.VisualStudio.com database for our Work Items is `OS\CORE-OS Core\DEP-Developer Ecosystem Platform\DART-Developer Tools and Runtimes\Open Source Software\Console`.
|
||||
|
||||
## Jumping In
|
||||
|
||||
To get started, feel free to read up on some of our documentation on the way we get things done and hop in.
|
||||
|
||||
Make a branch off of `dev/main` for yourself of the pattern `dev/myalias/foo` and feel free to push it to the server to get automatic builds and unit test runs.
|
||||
|
||||
Choose a bit of code to clean up, try to add a new feature, or improve something that you try to use every day.
|
||||
|
||||
When you are ready, use the [web portal](https://microsoft.visualstudio.com/Dart/_git/OpenConsole/pullrequests) to send a pull request into our `dev/main` branch and we'll be happy to help you get your code in line with the rest of the console.
|
||||
|
||||
## Building
|
||||
|
||||
OpenConsole uses submodules for some of its dependencies. To make sure submodules are restored or updated:
|
||||
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command line using msbuild. To build from the command line:
|
||||
|
||||
```
|
||||
nuget.exe restore OpenConsole.sln
|
||||
msbuild.exe OpenConsole.sln
|
||||
```
|
||||
|
||||
We provide a set of convienence scripts in the /tools directory to help automate the process of building and running tests.
|
||||
|
||||
## Assorted Notes
|
||||
|
||||
Here's some assorted notes on the way we do things. If you learn something about how we do things, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones!) This is a work in progress as we try to learn what we'll need to train people on in order to be effective contributors to our project. We're pretty blind to these things after staring at this code for so long... so mind the gaps and ask us plenty of questions!
|
||||
|
||||
* [Coding Style](./doc/STYLE.md)
|
||||
* [Code Organization](./doc/ORGANIZATION.md)
|
||||
* [Exceptions in our legacy codebase](./doc/EXCEPTIONS.md)
|
||||
* [Helpful smart pointers and macros for interfacing with Windows in WIL](./doc/WIL.md)
|
||||
# Welcome\!
|
||||
#### This repository contains the source code for:
|
||||
|
||||
* Windows Terminal
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
#### Other related repositories include:
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs/issues)
|
||||
|
||||
### Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
# Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the code:
|
||||
|
||||
## Windows Terminal
|
||||
|
||||
Windows Terminal is a new, modern, feature-rich, productive terminal application for command-line users. It includes many of the features most frequently requested by the Windows command-line community including support for tabs, rich text, globalization, configurability, theming & styling, and more.
|
||||
|
||||
The Terminal will also need to meet our goals and measures to ensure it remains fast, and efficient, and doesn't consume vast amounts of memory or power.
|
||||
|
||||
## The Windows console host
|
||||
|
||||
The Windows console host, `conhost.exe`, is Windows' original command-line user experience. It implements Windows' command-line infrastructure, and is responsible for hosting the Windows Console API, input engine, rendering engine, and user preferences. The console host code in this repository is the actual source from which the `conhost.exe` in Windows itself is built.
|
||||
|
||||
Console's primary goal is to remain backwards-compatible with existing console subsystem applications.
|
||||
|
||||
Since assuming ownership of the Windows command-line in 2014, the team has added several new features to the Console, including window transparency, line-based selection, support for [ANSI / Virtual Terminal sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/), a [Pseudoconsole ("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/), and more.
|
||||
|
||||
However, because the Console's primary goal is to maintain backward compatibility, we've been unable to add many of the features the community has been asking for, and which we've been wanting to add for the last several years--like tabs!
|
||||
|
||||
These limitations led us to create the new Windows Terminal.
|
||||
|
||||
## Shared Components
|
||||
|
||||
While overhauling the Console, we've modernized its codebase considerably. We've cleanly separated logical entities into modules and classes, introduced some key extensibility points, replaced several old, home-grown collections and containers with safer, more efficient [STL containers](https://docs.microsoft.com/en-us/cpp/standard-library/stl-containers?view=vs-2019), and made the code simpler and safer by using Microsoft's [WIL](https://github.com/Microsoft/wil) header library.
|
||||
|
||||
This overhaul work resulted in the creation of several key components that would be useful for any terminal implementation on Windows, including a new DirectWrite-based text layout and rendering engine, a text buffer capable of storing both UTF-16 and UTF-8, and a VT parser/emitter.
|
||||
|
||||
## Building a new terminal
|
||||
|
||||
When we started building the new terminal application, we explored and evaluated several approaches and technology stacks. We ultimately decided that our goals would be best met by sticking with C++ and sharing the aforementioned modernized components, placing them atop the modern Windows application platform and UI framework.
|
||||
|
||||
Further, we realized that this would allow us to build the terminal's renderer and input stack as a reusable Windows UI control that others can incorporate into their applications.
|
||||
|
||||
# FAQ
|
||||
|
||||
## Where can I download Windows Terminal?
|
||||
|
||||
### There are no binaries to download quite yet.
|
||||
|
||||
The Windows Terminal is in the _very early_ alpha stage, and not ready for the general public quite yet. If you want to jump in early, you can try building it yourself from source.
|
||||
|
||||
Otherwise, you'll need to wait until Mid-June for an official preview build to drop.
|
||||
|
||||
## I built and ran the new Terminal, but I just get a blank window app!
|
||||
|
||||
Make sure you are building for your computer's architecture. If your box has a 64-bit Windows change your Solution Platform to x64.
|
||||
To check your OS architecture go to Settings -> System -> About (or Win+X -> System) and under `Device specifications` check for the `System type`
|
||||
|
||||
## I built and ran the new Terminal, but it looks just like the old console! What gives?
|
||||
|
||||
Firstly, make sure you're building & deploying `CascadiaPackage` in Visual Studio, _NOT_ `Host.EXE`. `OpenConsole.exe` is just `conhost.exe`, the same old console you know and love. `opencon.cmd` will launch `openconsole.exe`, and unfortunately, `openterm.cmd` is currently broken.
|
||||
|
||||
Secondly, try pressing <kbd>Ctrl</kbd> + <kbd>T</kbd>. The tabs are hidden when you only have one tab by default. In the future, the UI will be dramatically different, but for now, the defaults are _supposed_ to look like the console defaults.
|
||||
|
||||
## I tried running WindowsTerminal.exe and it crashes!
|
||||
|
||||
* Don't try to run it unpackaged. Make sure to build & deploy `CascadiaPackage` from Visual Studio, and run the Windows Terminal (Dev Build) app.
|
||||
* Make sure you're on the right version of Windows. You'll need to be on Insider's builds, or wait for the 1903 release, as the Windows Terminal **REQUIRES** features from the latest Windows release.
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or above in order to run Windows Terminal
|
||||
* You must have the [1903 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) (build 10.0.18362.0) installed
|
||||
* You must have at least [VS 2017](https://visualstudio.microsoft.com/downloads/) installed.
|
||||
* You must install the following Workloads via the VS Installer. If you're running VS 2019, opening the solution will [prompt you to install missing components automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/).
|
||||
- Desktop Development with C++
|
||||
- Universal Windows Platform Development
|
||||
- Also install the following Individual Component:
|
||||
- C++ (v141) Universal Windows Platform Tools
|
||||
|
||||
* You must also [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) to locally install and run the Terminal app.
|
||||
|
||||
## Debugging
|
||||
|
||||
* To debug in VS, right click on CascadiaPackage (from VS Solution Explorer) and go to properties, in the Debug menu, change "Application process" and "Background task process" to "Native Only"
|
||||
|
||||
## Contributing
|
||||
|
||||
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
|
||||
|
||||
We ask that **before you start work on a feature that you would like to contribute, <span class="underline">please file an issue</span> describing your proposed change**: We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
|
||||
|
||||
> 👉 **Remember\!** Your contributions may be incorporated into future versions of Windows\! Because of this, all pull requests will be subject to the same level of scrutiny for quality, coding standards, performance, globalization, accessibility, and compatibility as those of our internal contributors.
|
||||
|
||||
> ⚠ **Note**: The Command-Line Team is actively working out of this repository and will be periodically re-structuring the code to make it easier to comprehend, navigate, build, test, and contribute to, so **DO expect significant changes to code layout on a regular basis**.
|
||||
|
||||
## Documentation
|
||||
|
||||
All documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
|
||||
## Communicating with the Team
|
||||
|
||||
The easiest way to communicate with the team is via GitHub issues. Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before you do**.
|
||||
|
||||
Please help us keep this repository clean, inclusive, and fun\! We will not tolerate any abusive, rude, disrespectful or inappropriate behavior. Read our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/) for more details.
|
||||
|
||||
If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter:
|
||||
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
|
||||
* Kayla Cinnamon, Program Manager (especially for UX issues): [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
|
||||
# Developer Guidance
|
||||
|
||||
## Building the Code
|
||||
|
||||
This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its dependencies. To make sure submodules are restored or updated, be sure to run the following prior to building:
|
||||
|
||||
```shell
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using MSBuild. To build from the command line, find your shell below.
|
||||
|
||||
### PowerShell
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
Set-MsBuildDevEnvironment
|
||||
Invoke-OpenConsoleBuild
|
||||
```
|
||||
|
||||
### CMD
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
bcz
|
||||
```
|
||||
|
||||
We've provided a set of convenience scripts as well as [README](./tools/README.md) in the **/tools** directory to help automate the process of building and running tests.
|
||||
|
||||
## Coding Guidance
|
||||
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
|
||||
|
||||
This is a work in progress as we learn what we'll need to provide people in order to be effective contributors to our project.
|
||||
|
||||
- [Coding Style](https://github.com/Microsoft/Terminal/blob/master/doc/STYLE.md)
|
||||
- [Code Organization](https://github.com/Microsoft/Terminal/blob/master/doc/ORGANIZATION.md)
|
||||
- [Exceptions in our legacy codebase](https://github.com/Microsoft/Terminal/blob/master/doc/EXCEPTIONS.md)
|
||||
- [Helpful smart pointers and macros for interfacing with Windows in WIL](https://github.com/Microsoft/Terminal/blob/master/doc/WIL.md)
|
||||
|
||||
# Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code].
|
||||
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[conduct-email]: mailto:opencode@microsoft.com
|
||||
|
||||
4
build/.nuget/packages.config
Normal file
4
build/.nuget/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Taef.TestAdapter" version="10.30.180808002" />
|
||||
</packages>
|
||||
9
build/config/NuGet.config
Normal file
9
build/config/NuGet.config
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="TAEF Internal" value="https://microsoft.pkgs.visualstudio.com/_packaging/Taef/nuget/v3/index.json" />
|
||||
</packageSources>
|
||||
<config>
|
||||
<add key="repositorypath" value="..\..\packages" />
|
||||
</config>
|
||||
</configuration>
|
||||
36
build/pipelines/ci.yml
Normal file
36
build/pipelines/ci.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
paths:
|
||||
exclude:
|
||||
- doc/*
|
||||
- samples/*
|
||||
- tools/*
|
||||
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
|
||||
# 0.0.yyMM.dd##
|
||||
# 0.0.1904.0900
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: ARM64
|
||||
38
build/pipelines/release.yml
Normal file
38
build/pipelines/release.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
variables:
|
||||
baseYearForVersioning: 2019 # Used by build-console-int
|
||||
versionMajor: 0
|
||||
versionMinor: 1
|
||||
|
||||
# When we move off PackageES for Versioning, we'll need to switch
|
||||
# name to this format. For now, though, we need to use DayOfYear.Rev
|
||||
# to unique our builds, as mandated by PackageES's Setup task.
|
||||
# name: '$(versionMajor).$(versionMinor).$(DayOfYear)$(Rev:r).0'
|
||||
#
|
||||
# Build name/version number above must end with .0 to make the
|
||||
# store publication machinery happy.
|
||||
name: 'Terminal_$(date:yyMM).$(date:dd)$(rev:rrr)'
|
||||
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/release-sign-and-bundle.yml
|
||||
53
build/pipelines/templates/build-console-audit-job.yml
Normal file
53
build/pipelines/templates/build-console-audit-job.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
parameters:
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}AuditMode
|
||||
displayName: Static Analysis Build ${{ parameters.platform }}
|
||||
variables:
|
||||
BuildConfiguration: AuditMode
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: { vmImage: vs2017-win2016 }
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
clean: true
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: Ensure NuGet 4.8.1
|
||||
inputs:
|
||||
versionSpec: 4.8.1
|
||||
|
||||
# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
|
||||
# This should be `task: NuGetCommand@2`
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: Restore NuGet packages
|
||||
inputs:
|
||||
command: restore
|
||||
feedsToUse: config
|
||||
configPath: NuGet.config
|
||||
restoreSolution: OpenConsole.sln
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet restore packages for CI'
|
||||
inputs:
|
||||
command: restore
|
||||
restoreSolution: build/.nuget/packages.config
|
||||
feedsToUse: config
|
||||
externalFeedCredentials: 'TAEF NuGet Feed'
|
||||
nugetConfigPath: build/config/NuGet.config
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 15.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
17
build/pipelines/templates/build-console-ci.yml
Normal file
17
build/pipelines/templates/build-console-ci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: { vmImage: vs2017-win2016 }
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
|
||||
30
build/pipelines/templates/build-console-int.yml
Normal file
30
build/pipelines/templates/build-console-int.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
demands:
|
||||
- msbuild
|
||||
- visualstudio
|
||||
- vstest
|
||||
|
||||
steps:
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: "/p:XesUseOneStoreVersioning=true;XesBaseYearForStoreVersion=$(baseYearForVersioning) ${{ parameters.additionalBuildArguments }}"
|
||||
95
build/pipelines/templates/build-console-steps.yml
Normal file
95
build/pipelines/templates/build-console-steps.yml
Normal file
@@ -0,0 +1,95 @@
|
||||
parameters:
|
||||
additionalBuildArguments: ''
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
clean: true
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: Ensure NuGet 4.8.1
|
||||
inputs:
|
||||
versionSpec: 4.8.1
|
||||
|
||||
- task: VisualStudioTestPlatformInstaller@1
|
||||
displayName: Ensure VSTest Platform
|
||||
|
||||
# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
|
||||
# This should be `task: NuGetCommand@2`
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: Restore NuGet packages
|
||||
inputs:
|
||||
command: restore
|
||||
feedsToUse: config
|
||||
configPath: NuGet.config
|
||||
restoreSolution: OpenConsole.sln
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet restore packages for CI'
|
||||
inputs:
|
||||
command: restore
|
||||
restoreSolution: build/.nuget/packages.config
|
||||
feedsToUse: config
|
||||
externalFeedCredentials: 'TAEF NuGet Feed'
|
||||
nugetConfigPath: build/config/NuGet.config
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 15.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Unit Tests'
|
||||
inputs:
|
||||
testAssemblyVer2: |
|
||||
$(BUILD.SOURCESDIRECTORY)\**\*unit.test*.dll
|
||||
!**\obj\**
|
||||
runSettingsFile: '$(BUILD.SOURCESDIRECTORY)\src\unit.tests.$(BuildPlatform).runsettings'
|
||||
codeCoverageEnabled: true
|
||||
runInParallel: False
|
||||
testRunTitle: 'Console Unit Tests'
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Feature Tests (x64 only)'
|
||||
inputs:
|
||||
testAssemblyVer2: |
|
||||
$(BUILD.SOURCESDIRECTORY)\**\*feature.test*.dll
|
||||
!**\obj\**
|
||||
runSettingsFile: '$(BUILD.SOURCESDIRECTORY)\src\unit.tests.$(BuildPlatform).runsettings'
|
||||
codeCoverageEnabled: true
|
||||
runInParallel: False
|
||||
testRunTitle: 'Console Feature Tests'
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy *.appx/*.msix to Artifacts (Non-PR builds only)'
|
||||
inputs:
|
||||
Contents: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
**/*.appxsym
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact (appx) (Non-PR builds only)'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)/appx'
|
||||
ArtifactName: 'appx-$(BuildConfiguration)'
|
||||
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
|
||||
72
build/pipelines/templates/release-sign-and-bundle.yml
Normal file
72
build/pipelines/templates/release-sign-and-bundle.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
|
||||
jobs:
|
||||
- job: SignDeploy${{ parameters.configuration }}
|
||||
displayName: Sign and Deploy for ${{ parameters.configuration }}
|
||||
|
||||
dependsOn:
|
||||
- Buildx64AuditMode
|
||||
- Buildx64Release
|
||||
- Buildx86Release
|
||||
- Buildarm64Release
|
||||
condition: |
|
||||
and
|
||||
(
|
||||
in(dependencies.Buildx64AuditMode.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx86Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildarm64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
|
||||
)
|
||||
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
AppxProjectName: CascadiaPackage
|
||||
AppxBundleName: Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download AppX artifacts
|
||||
inputs:
|
||||
artifactName: 'appx-$(BuildConfiguration)'
|
||||
itemPattern: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
downloadPath: '$(Build.ArtifactStagingDirectory)\appx'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Create $(AppxBundleName)'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Create-AppxBundle.ps1'
|
||||
arguments: |
|
||||
-InputPath "$(Build.ArtifactStagingDirectory)\appx" -ProjectName $(AppxProjectName) -BundleVersion 0.0.0.0 -OutputPath "$(Build.ArtifactStagingDirectory)\$(AppxBundleName)"
|
||||
|
||||
- task: PkgESCodeSign@10
|
||||
displayName: 'Package ES - SignConfig.WindowsTerminal.xml'
|
||||
inputs:
|
||||
signConfigXml: 'build\config\SignConfig.WindowsTerminal.xml'
|
||||
inPathRoot: '$(Build.ArtifactStagingDirectory)'
|
||||
outPathRoot: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Signed AppX'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
ArtifactName: 'appxbundle-signed-$(BuildConfiguration)'
|
||||
@@ -11,7 +11,12 @@
|
||||
"/packages/",
|
||||
"/ipch/",
|
||||
"/dep/",
|
||||
"/.vs/"
|
||||
"/.vs/",
|
||||
"/build/",
|
||||
"/src/cascadia/",
|
||||
"/.nuget/",
|
||||
"/.github/",
|
||||
"/samples/"
|
||||
],
|
||||
"SuffixFilters": [
|
||||
".dbb",
|
||||
|
||||
@@ -586,6 +586,8 @@ typedef struct _CONSOLE_STATE_INFO {
|
||||
COLORREF DefaultForeground;
|
||||
COLORREF DefaultBackground;
|
||||
BOOL TerminalScrolling;
|
||||
|
||||
LPWSTR VersionString;
|
||||
/* END V2 CONSOLE_STATE_INFO */
|
||||
|
||||
} CONSOLE_STATE_INFO, *PCONSOLE_STATE_INFO;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
2. Add matching fields to Settings.hpp
|
||||
- add getters, setters, the whole drill.
|
||||
|
||||
3. Add to the propsheet.
|
||||
3. Add to the propsheet
|
||||
- We need to add it to *reading and writing* the registry from the propsheet, and *reading* the link from the propsheet. Yes, that's weird, but the propsheet is smart enough to re-use ShortcutSerialization::s_SetLinkValues, but not smart enough to do the same with RegistrySerialization.
|
||||
- `src/propsheet/registry.cpp`
|
||||
- `propsheet/registry.cpp@InitRegistryValues` should initialize the default value for the property.
|
||||
@@ -28,7 +28,7 @@
|
||||
6. Add the setting to `Menu::s_GetConsoleState`, and `Menu::s_PropertiesUpdate`
|
||||
Now, your new setting should be stored just like all the other properties.
|
||||
|
||||
7. Update the feature test properties to get add the setting as well.
|
||||
7. Update the feature test properties to get add the setting as well
|
||||
- `ft_uia/Common/NativeMethods.cs@WinConP`:
|
||||
- `Wtypes.PROPERTYKEY PKEY_Console_`
|
||||
- `NT_CONSOLE_PROPS`
|
||||
@@ -36,5 +36,5 @@ Now, your new setting should be stored just like all the other properties.
|
||||
8. Add the default value for the setting to `win32k-settings.man`
|
||||
- If the setting shouldn't default to 0 or `nullptr`, then you'll need to set the default value of the setting in `win32k-settings.man`.
|
||||
|
||||
9. Update `Settings::InitFromStateInfo` and `Settings::CreateConsoleStateInfo` to get/set the value in a CONSOLE_STATE_INFO appropriately.
|
||||
9. Update `Settings::InitFromStateInfo` and `Settings::CreateConsoleStateInfo` to get/set the value in a CONSOLE_STATE_INFO appropriately
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Generation
|
||||
|
||||
conhost requests that user32 inject a thread into the attached console application.
|
||||
conhost requests that user32 injects a thread into the attached console application.
|
||||
See ntuser's exitwin.c for `CreateCtrlThread`.
|
||||
|
||||
## Timeouts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Understanding Console Host Settings
|
||||
|
||||
Settings in the Windows Console Host can be a bit tricky to understand. This is mostly because the settings system evolved over the course of decades. Before we dig into the details of how settings are persisted, it's probably worth a quick look at what these settings are.
|
||||
Settings in the Windows Console Host can be a bit tricky to understand. This is mostly because the settings system evolved over the course of decades. Before we dig into the details of how settings are persisted, it's probably worth taking a quick look at what these settings are.
|
||||
|
||||
## Settings Description
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ This file contains notes about debugging various items in the repository.
|
||||
|
||||
If you want to debug code in the Cascadia package via Visual Studio, your breakpoints will not be hit by default. A tweak is required to the *CascadiaPackage* project in order to enable this.
|
||||
|
||||
1. Right-click on *CascadiaPackage* in Solution Explorer and select Properties
|
||||
1. Right-click on *CascadiaPackage* in Solution Explorer and select Properties.
|
||||
2. Change the *Application process* type from *Mixed (Managed and Native)* to *Native Only*.
|
||||
@@ -9,8 +9,8 @@ sometimes it's significantly simpler to use them. Given that, we have a set of r
|
||||
exception use.
|
||||
|
||||
## Rules
|
||||
1. **DO NOT** allow exceptions to leak out of new code into new code
|
||||
1. **DO** use NTSTATUS or HRESULT as return values as appropriate
|
||||
1. **DO NOT** allow exceptions to leak out of new code into old code
|
||||
1. **DO** use NTSTATUS or HRESULT as return values as appropriate (HRESULT is preferred)
|
||||
1. **DO** Encapsulate all exception behaviors within implementing classes
|
||||
1. **DO NOT** introduce modern exception throwing code into old code. Instead, refactor as needed to allow encapsulation or
|
||||
use non-exception based code
|
||||
@@ -33,4 +33,4 @@ exception use.
|
||||
|
||||
### Using WIL for non-throwing modern facilities
|
||||
|
||||
###
|
||||
TODO
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- **Follow the pattern of what you already see in the code**
|
||||
- Try to package new ideas/components into libraries that have nicely defined interfaces
|
||||
- Package new ideas into classes or refactor existing ideas into a class as you extend.
|
||||
- Package new ideas into classes or refactor existing ideas into a class as you extend
|
||||
- Each project should have a Unit test in a ut_ folder in its subdirectory (like `ut_host`)
|
||||
- Functional tests should be in ft_ subdirectories (like `ft_api`)
|
||||
- Build scripts are generally in subdirectories with their type of output (like `/dll` or `/exe`)
|
||||
@@ -36,16 +36,16 @@
|
||||
* `/src/host/ft_resize` – Special test for resizing/reflowing the buffer window
|
||||
* `/src/host/ft_uia` – Currently disabled (for not being very reliable) UI Automation tests that we are looking to re-enable and expand to do UI Automation coverage of various human interactions
|
||||
* `/src/host/...` - The files I’ll list out below
|
||||
* `/src/inc` – Include files that are shared between the host and some of the other libraries. This is only some of them. The include story is kind of a mess right now, but we’d like to clean it up at some point
|
||||
* `/src/propslib` – Library shared between console host and the OS shell “right click a shortcut file and modify console properties” page to read/write user settings to and from the registry and embedded within shortcut LNK data
|
||||
* `/src/renderer` – Refactored extraction of all activities related to rendering the text in the buffers onto the screen
|
||||
* `/src/renderer/base` – Base interface layer providing non-engine-specific rendering things like choosing the data from the console buffer, deciding how to lay out or transform that data, then dispatching commands to a specific final display engine
|
||||
* `/src/renderer/gdi` – The GDI implementation of rendering to the screen. Takes commands to “draw a line” or “fill the background” or “select a region” from the base and turns them into GDI calls to the screen. Extracted from original console host code.
|
||||
* `/src/renderer/inc – Interface definitions for all renderer communication
|
||||
* `/src/terminal` – Virtual terminal support for the console. This is the sequences that are found in-band with other text on STDIN/STDOUT that command the display to do things. This is the *nix way of controlling a console.
|
||||
* `/src/terminal/parser` – This contains a state machine and sorting engine for feeding in individual characters from STDOUT or STDIN and decoding them into the appropriate verbs that should be performed
|
||||
* `/src/terminal/adapter` – This converts the verbs from the interface into calls on the console API. It doesn’t actually call through the API (for performance reasons since it lives inside the same binary), but it tries to remain as close to an API call as possible. There are some private extensions to the API for behaviors that didn’t exist before this was written that we’ve not made public. We don’t know if we will yet or force people to use VT to get at them.
|
||||
* `/src/tsf` – Text Services Foundation. This provides IME input services to the console. This was historically used for only Chinese, Japanese, and Korean IMEs specifically on OS installations with those as the primary language. It was in the summer of 2016 unrestricted to be able to be used on any OS installation with any IME (whether or not it will display correctly is a different story). It also was unrestricted to allow things like Pen and Touch input (which are routed via IME messages) to display properly inside the console from the TabTip window (the little popup that helps you insert pen/touch writing/keyboard candidates into an application)
|
||||
* `/src/inc` – Include files that are shared between the host and some of the other libraries. This is only some of them. The include story is kind of a mess right now, but we’d like to clean it up at some point
|
||||
* `/src/propslib` – Library shared between console host and the OS shell “right click a shortcut file and modify console properties” page to read/write user settings to and from the registry and embedded within shortcut LNK data
|
||||
* `/src/renderer` – Refactored extraction of all activities related to rendering the text in the buffers onto the screen
|
||||
* `/src/renderer/base` – Base interface layer providing non-engine-specific rendering things like choosing the data from the console buffer, deciding how to lay out or transform that data, then dispatching commands to a specific final display engine
|
||||
* `/src/renderer/gdi` – The GDI implementation of rendering to the screen. Takes commands to “draw a line” or “fill the background” or “select a region” from the base and turns them into GDI calls to the screen. Extracted from original console host code.
|
||||
* `/src/renderer/inc` – Interface definitions for all renderer communication
|
||||
* `/src/terminal` – Virtual terminal support for the console. This is the sequences that are found in-band with other text on STDIN/STDOUT that command the display to do things. This is the *nix way of controlling a console.
|
||||
* `/src/terminal/parser` – This contains a state machine and sorting engine for feeding in individual characters from STDOUT or STDIN and decoding them into the appropriate verbs that should be performed
|
||||
* `/src/terminal/adapter` – This converts the verbs from the interface into calls on the console API. It doesn’t actually call through the API (for performance reasons since it lives inside the same binary), but it tries to remain as close to an API call as possible. There are some private extensions to the API for behaviors that didn’t exist before this was written that we’ve not made public. We don’t know if we will yet or force people to use VT to get at them.
|
||||
* `/src/tsf` – Text Services Foundation. This provides IME input services to the console. This was historically used for only Chinese, Japanese, and Korean IMEs specifically on OS installations with those as the primary language. It was in the summer of 2016 unrestricted to be able to be used on any OS installation with any IME (whether or not it will display correctly is a different story). It also was unrestricted to allow things like Pen and Touch input (which are routed via IME messages) to display properly inside the console from the TabTip window (the little popup that helps you insert pen/touch writing/keyboard candidates into an application)
|
||||
|
||||
## Host File Overview
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
## Philosophy
|
||||
1. If it's inserting something into the existing classes/functions, try to follow the existing style as closely as possible.
|
||||
1. If it's brand new code or refactoring a complete class or area of the code, please follow as Modern C++ of a style as you can and reference the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) as much as you possibly can.
|
||||
1. When working with any Win32 or NT API, please try to use the [Windows Internal Library](./WIL.md) smart pointers and result handlers.
|
||||
1. When working with any Win32 or NT API, please try to use the [Windows Implementation Library](./WIL.md) smart pointers and result handlers.
|
||||
1. The use of NTSTATUS as a result code is discouraged, HRESULT or exceptions are preferred. Functions should not return a status code if they would always return a successful status code. Any function that returns a status code should be marked `noexcept` and have the `nodiscard` attribute.
|
||||
|
||||
@@ -7,13 +7,13 @@ Universal Testing is the Microsoft framework for creating and deploying test pac
|
||||
It involves several parts:
|
||||
- TESTMD
|
||||
- These define a package unit for deployment to the test device. This usually includes the test binaries and any dependent data that it will need to execute.
|
||||
- There can also be a hierarchy where one package can depend on another such that packages can be re-used
|
||||
- There can also be a hierarchy where one package can depend on another such that packages can be re-used.
|
||||
|
||||
- TESTLIST
|
||||
- This defines a batch of TESTMD packages that should be executed together.
|
||||
|
||||
- TESTPASSES
|
||||
- This defines a list of tests via a TESTLIST and a lab environment configuration on which the tests should be run
|
||||
- This defines a list of tests via a TESTLIST and a lab environment configuration on which the tests should be run.
|
||||
|
||||
These files can either include their child element as they're supposed to (TESTMDs included in TESTLISTs) or they can often include themselves to provide chain structuring (one TESTLIST can reference another TESTLIST).
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Windows Internal Library
|
||||
# Windows Implementation Library
|
||||
|
||||
## Overview
|
||||
Windows Internal Library, or WIL, is a header-only library created to help make working with the Windows API more predictable and (hopefully) bug free.
|
||||
[Windows Implementation Library](https://github.com/Microsoft/wil), or WIL, is a header-only library created to help make working with the Windows API more predictable and (hopefully) bug free.
|
||||
|
||||
A majority of functions are in either the `wil::` or `wistd::` namespace. `wistd::` is used for things that have an equivalent in STL's `std::` namespace but have some special functionality like being exception-free. Everything else is in `wil::` namespace.
|
||||
|
||||
@@ -9,13 +9,13 @@ The primary usages of WIL in our code so far are...
|
||||
|
||||
### Smart Pointers ###
|
||||
|
||||
Inside [wil\resource.h](..\dep\wil\resource.h) are smart pointer like classes for many Windows OS resources like file handles, socket handles, process handles, and so on. They're of the form `wil::unique_handle` and call the appropriate/matching OS function (like `CloseHandle()` in this case) when they go out of scope.
|
||||
Inside [wil\resource.h](https://github.com/microsoft/wil/blob/master/include/wil/resource.h) are smart pointer like classes for many Windows OS resources like file handles, socket handles, process handles, and so on. They're of the form `wil::unique_handle` and call the appropriate/matching OS function (like `CloseHandle()` in this case) when they go out of scope.
|
||||
|
||||
Another useful item is `wil::make_unique_nothrow()` which is analogous to `std::make_unique` (except without the exception which might help you integrate with existing exception-free code in the console.) This will return a `wistd::unique_ptr` (vs. a `std::unique_ptr`) which can be used in a similar manner.
|
||||
|
||||
### Result Handling ###
|
||||
|
||||
To manage the various types of result codes that come back from Windows APIs, the file [wil\result.h](..\dep\wil\result.h) provides a wealth of macros that can help.
|
||||
To manage the various types of result codes that come back from Windows APIs, the file [wil\result.h](https://github.com/microsoft/wil/blob/master/include/wil/result.h) provides a wealth of macros that can help.
|
||||
|
||||
As an example, the method `DuplicateHandle()` returns a `BOOL` value that is `FALSE` under failure and would like you to `GetLastError()` from the operating system to find out what the actual result code is. In this circumstance, you could use the macro `RETURN_IF_WIN32_BOOL_FALSE` to wrap the call to `DuplicateHandle()` which would automatically handle this pattern for you and return the `HRESULT` equivalent on failure.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ The next step would be investigating one of these failures...
|
||||
|
||||
A quick overview of investigation... normally you can just attempt to build and reproduce the failure locally with the `OpenConsole` project and it will happen the same way as it did on the nightly build in the lab. However, sometimes the failure will be exclusive to the lab or won't happen in the same way as it does on your local dev machine. At that point, you need to move into setting up the environment as it was during the testpass and figuring out what went wrong.
|
||||
|
||||
You can try to do this all manually by pulling down a VM image from the release share for the nightly build, making a VM, deploying the test binaries and TAEF test runnner executables to the machine, installing the VS Remote Debugging or WinDBG tools on the VM, and then running the test and figuring out what's going wrong with the debuggers.
|
||||
You can try to do this all manually by pulling down a VM image from the release share for the nightly build, making a VM, deploying the test binaries and TAEF test runner executables to the machine, installing the VS Remote Debugging or WinDBG tools on the VM, and then running the test and figuring out what's going wrong with the debuggers.
|
||||
|
||||
Or you can use some of the Engineering Systems tools to make this easier. I'll detail how to do that below.
|
||||
|
||||
@@ -24,12 +24,12 @@ Prerequisites:
|
||||
- Visual Studio 2017
|
||||
- Install the TDP (Test Development Platform) plug-in (see: [https://osgwiki.com/wiki/Test_Development_Platform_(TDP)]).
|
||||
|
||||
1. Open Visual Studio 2017 and use the TDP drop-down menu to open the `Device Manager`
|
||||
1. Open Visual Studio 2017 and use the TDP drop-down menu to open the `Device Manager`.
|
||||
1. In the pane that opens to the left, choose `Add` and then `Nebula VM Device`. Nebula is a cloud provider for VMs (like Azure but a more private instance for corporate work usage).
|
||||
1. Name the machine and choose the build/branch/flavor/SKU from the drop downs at the bottom. It will find the VHD for you from the build shares. Hit `Add Device` to deploy to Nebula
|
||||
1. Name the machine and choose the build/branch/flavor/SKU from the drop downs at the bottom. It will find the VHD for you from the build shares. Hit `Add Device` to deploy to Nebula.
|
||||
1. Wait a few minutes. It took 5-10 for it to be deployed.
|
||||
1. Right click the machine name in the `Device Manager` list and choose `Launch T-Shell`. You can also use `Connect via Console` to get a "remote desktop"-like session to the KVM port on the VM.
|
||||
1. In T-shell, use `testd Microsoft.Console.TestLab.Desktop.testlist` or a command of that format with a different TESTLIST or TESTMD name from our project (see the [UniversalTest.md] documentation). The `testd` utility will automatically resolve the build/branch/flavor information, dig through the build shares for the matching TESTLIST/TESTMD metadata, and attempt to deploy all relevent packages and dependencies on the device. When it's successful, it will move onto running all the tests and giving you the results. On conclusion, the test results should pop up in the web browser or the `Hubble - Log Viewer` tool provided by the Engineering Systems team.
|
||||
1. In T-shell, use `testd Microsoft.Console.TestLab.Desktop.testlist` or a command of that format with a different TESTLIST or TESTMD name from our project (see the [UniversalTest.md] documentation). The `testd` utility will automatically resolve the build/branch/flavor information, dig through the build shares for the matching TESTLIST/TESTMD metadata, and attempt to deploy all relevant packages and dependencies on the device. When it's successful, it will move onto running all the tests and giving you the results. On conclusion, the test results should pop up in the web browser or the `Hubble - Log Viewer` tool provided by the Engineering Systems team.
|
||||
|
||||
If some of the above things do not work, go to [https://osgwiki.com] and type them into the Search bar. For instance, if T-Shell isn't found or working, you can find out where to get it or download it on `OSGWiki`. The same goes for the other commands besides `testd` to use in T-shell and more information on what `Hubble` or `Nebula` are.
|
||||
|
||||
|
||||
89
doc/bot.md
Normal file
89
doc/bot.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Issue/PR Management Bot Information
|
||||
|
||||
## Overview
|
||||
|
||||
The goal here is to help us automate, manage, and narrow down what we actually need to focus on in this repository.
|
||||
We'll be using tags, primarily, to help us understand what needs attention, what is sitting around and turning stale, etc.
|
||||
|
||||
### Quick-Guidance to Core Contributors
|
||||
1. Look at `Needs-Attention` as top priority
|
||||
1. Look at `Needs-Triage` during triage meetings to get a handle on what's new and sort it out
|
||||
1. Look at `Needs-Tag-Fix` when you have a few minutes to fix up things tagged impoperly
|
||||
1. Manually add `Needs-Author-Feedback` when there's something we need the author to follow up on and want attention if they return it or an auto-close for inactivity if it goes stale.
|
||||
|
||||
### Tagging/Process Details
|
||||
1. When new issues arrive, or when issues are not properly tagged... we'll mark them as `Needs-Triage` automatically.
|
||||
- The core contributor team will then come through and mark them up as appropriate. The goal is to have a tag that fits the `Product`, `Area`, and `Issue` category.
|
||||
- The `Needs-Triage` tag will be removed manually by the core contributor team during a triage meeting. (Exception, triage may also be done offline by senior team members during high-volume times.)
|
||||
- An issue may or may not be assigned to a contributor during triage. It is not necessary to assign someone to complete it.
|
||||
- We're not focusing on Projects yet.
|
||||
1. When core contributors need to ask something of the author, they will manually assign the `Needs-Author-Feedback` tag.
|
||||
- This tag will automatically drop off when the author comes back around and applies activity to the thread.
|
||||
- When this tag drops off, the bot will apply the `Needs-Attention` tag to get the core contribution team's attention again. If an author cares enough to be active, we will attempt to prioritize engaging with that author.
|
||||
- If the author doesn't come back around in a while, this will become a `No-Recent-Activity` tag.
|
||||
- If there's activity on an issue, the `No-Recent-Activity` tag will automatically drop.
|
||||
- If the `No-Recent-Activity` stays, the issue will be closed as stale.
|
||||
1. PRs will automatically get a `Needs-Author-Feedback` tag when reviewers wait on the author
|
||||
- This follows a similar decay strategy to issues.
|
||||
- If the author responds, the `Needs-Author-Feedback` tag will drop.
|
||||
- If there is no activity in a while, the `No-Recent-Activity` tag will appear.
|
||||
- If the `No-Recent-Activity` tag exists for a while, the PR will be closed as stale.
|
||||
1. Issues manually marked as `Resolution-Duplicate` will be closed shortly after activity stops
|
||||
1. Pull requests manually marked as `AutoMerge` will permit the bot to complete the PR and do cleanup when certain conditions are met. See details below.
|
||||
|
||||
## Rules
|
||||
|
||||
### Issue Management
|
||||
|
||||
#### Mark as Triage Needed
|
||||
- When an issue doesn't meet triage criteria, applies `Needs-Triage` tag. Right now, this is just when it's opened.
|
||||
|
||||
#### Author Has Responded
|
||||
- When an issue with `Needs-Author-Feedback` gets an author response, drops that tag in favor of `Needs-Attention` to flag core contributors to drop by.
|
||||
|
||||
#### Remove Activity Tag
|
||||
- When an issue with `No-Recent-Activity` has activity, drops this tag
|
||||
|
||||
#### Close Stale
|
||||
- Every hour, checks if there's an issue with `Needs-Author-Feedback` and `No-Recent-Activity` for 3 days. Closes as stale.
|
||||
|
||||
#### Tag as No Activity
|
||||
- Every hour, checks if there's been no activity in 4 days on an issue that `Needs-Author-Feedback`. If it's been 4 days, mark `No-Recent-Activity` as well.
|
||||
|
||||
#### Close Duplicate Issues
|
||||
- Every hour, checks if there's been a day since the last activity on an issue with tag `Resolution-Duplicate` and closes it if inactive.
|
||||
|
||||
#### Enforce tag system
|
||||
- When an issue is opened or labels are changed in any way, we will check if the tagging matches the system. If not, it will get `Needs-Tag-Fix`. The system is to have an `Area-`, `Issue-`, and `Product-` tag for all open things, and also a `Resolution-` for closed ones.
|
||||
- When the tags from appropriate categories are applied, it will auto-remove the `Needs-Tag-Fix` tag.
|
||||
|
||||
### PR Management
|
||||
|
||||
#### Codeflow Link *(Disabled)*
|
||||
- Bumps a PR with a link to the Microsoft CodeFlow tool for reviewing PRs
|
||||
|
||||
#### Marks PR as Awaiting Author Feedback
|
||||
- When a reviewer marks the PR as changes requested, the `Needs-Author-Feedback` tag will be applied
|
||||
|
||||
#### Removes Awaiting Author Feedback
|
||||
- When the PR author updates the pull request, comments on it, or responds to a review, the `Needs-Author-Feedback` tag is removed.
|
||||
|
||||
#### Removes No Recent Activity
|
||||
- When anyone touches the pull request, the `No-Recent-Activity` tag is removed.
|
||||
|
||||
#### Markup stale pull requests
|
||||
- Every hour, if a pull request `Needs-Author-Feedback` and hasn't been touched in 7 days, it will get the `No-Recent-Activity` tag.
|
||||
|
||||
#### Close stale pull requests
|
||||
- Every hour, if a pull request has `No-Recent-Activity` and hasn't been touched in a further 7 days, it will be closed.
|
||||
|
||||
#### Auto-Merge pull requests
|
||||
- When a pull request has the `AutoMerge` label...
|
||||
- If it has been at least 480 minutes and all the statuses pass, merge it in.
|
||||
- Will use Squash merge stratgy
|
||||
- Will attempt to delete branch after merge, if possible
|
||||
- Will automatically remove the `AutoMerge` label if changes are pushed by someone *without* Write Access.
|
||||
- More information on bot-logic that can be controlled with comments is [here](https://github.com/OfficeDev/office-ui-fabric-react/wiki/Advanced-auto-merge)
|
||||
|
||||
## Admin Panel
|
||||
[Here](https://fabric-cp.azurewebsites.net/bot/)
|
||||
@@ -8,18 +8,18 @@ Openconsole can be built with Visual Studio or from the command line. There are
|
||||
The cmd scripts are set up to emulate a portion of the OS razzle build environment. razzle.cmd is the first script that should be run. bcz.cmd will build clean and bz.cmd should build incrementally.
|
||||
|
||||
There are also scripts for running the tests:
|
||||
- runut.cmd - run the unit tests
|
||||
- runft.cmd - run the feature tests
|
||||
- runuia.cmd - run the UIA tests
|
||||
- `runut.cmd` - run the unit tests
|
||||
- `runft.cmd` - run the feature tests
|
||||
- `runuia.cmd` - run the UIA tests
|
||||
|
||||
## Build with Powershell
|
||||
|
||||
Openconsole.psm1 should be loaded with `Import-Module`. From there `Set-MsbuildDevEnvironment` will set up environment variables required to build. There are a few exported functions (look at their documentation for further details):
|
||||
|
||||
- Invoke-OpenConsolebuild - builds the solution. Can be passed msbuild arguments.
|
||||
- Invoke-OpenConsoleTests - runs the various tests. Will run the unit tests by default.
|
||||
- Start-OpenConsole - starts Openconsole.exe from the output directory. x64 is run by default.
|
||||
- Debug-OpenConsole - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
|
||||
- `Invoke-OpenConsolebuild` - builds the solution. Can be passed msbuild arguments.
|
||||
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
|
||||
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
|
||||
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
|
||||
|
||||
## Configuration Types
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
## Abstract
|
||||
It should be possible to configure the terminal so that it doesn't send certain keystrokes as input to the terminal, and instead triggers certain actions. Examples of these actions could be copy/pasting text, opening a new tab, or changing the font size.
|
||||
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in it's idomatic way, and UWP could implement them in their own way.
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in its idomatic way, and UWP could implement them in their own way.
|
||||
|
||||
## Terminology
|
||||
* **Key Chord**: This is any possible keystroke that a user can input
|
||||
@@ -28,7 +28,7 @@ This spec describes a mechanism by which we could provide a common implementatio
|
||||
|
||||
When the UX frontend is created, it should instantiate a `IKeyBindings` object with the keybindings mapped as it would like.
|
||||
|
||||
When it's creating it's platform-dependent terminal component, it can pass the `IKeyBindings` object to that component. The component will then be able to pass that object to the terminal instance.
|
||||
When it's creating its platform-dependent terminal component, it can pass the `IKeyBindings` object to that component. The component will then be able to pass that object to the terminal instance.
|
||||
|
||||
When the terminal component calls `ITerminalInput.SendKeyEvent(uint vkey, KeyModifiers modifiers)`, the terminal will use `IKeyBindings.TryKeyChord` to see if there are any bound actions to that input. If there are, the `IKeyBindings` implementation will either handle the event by interacting with the `ITerminalInput`, or it'll invoke an event that's been registered by the frontend
|
||||
|
||||
@@ -66,7 +66,7 @@ partial class Terminal
|
||||
```
|
||||
### Project Cascadia Sample
|
||||
|
||||
Below is an example of how the Project Cascadia application might implement it's
|
||||
Below is an example of how the Project Cascadia application might implement its
|
||||
keybindings.
|
||||
|
||||
```csharp
|
||||
@@ -105,4 +105,4 @@ class KeyBindings : IKeyBindings
|
||||
How does Copy/paste play into this?
|
||||
|
||||
When Input is written to the terminal, and it tries the copy keybinding, what happens?
|
||||
The Keybindings are global to the frontend, not local to the terminal. Copy/Paste events should also be delegates that get raised, and the frontend can then determine what to do with them. It'll probably query it's active/focused Terminal Component, then Get the `ITerminalInput` from that component, and use that to CopyText / PasteText from the Terminal as needed.
|
||||
The Keybindings are global to the frontend, not local to the terminal. Copy/Paste events should also be delegates that get raised, and the frontend can then determine what to do with them. It'll probably query its active/focused Terminal Component, then Get the `ITerminalInput` from that component, and use that to CopyText / PasteText from the Terminal as needed.
|
||||
|
||||
@@ -34,7 +34,7 @@ This spec will outline how various terminal frontends will be able to interact w
|
||||
the need for a globals/profiles structure.
|
||||
6. The Terminal should be able to read information from a settings structure
|
||||
that's independant of how it's persisted / implemented by the Application
|
||||
7. The Component should be able to have it's own settings independent of the
|
||||
7. The Component should be able to have its own settings independent of the
|
||||
application that's embedding it, such as font size and face, scrollbar
|
||||
visibility, etc. These should be settings that are specific to the component,
|
||||
and the Terminal should logically be unaffected by these settings.
|
||||
@@ -57,7 +57,7 @@ VS needs to be able to persist settings just as a simple set of global settings.
|
||||
|
||||
When the application needs to retrieve these settings, they need to use them as a tripartite structure: frontend-component-terminal settings.
|
||||
|
||||
Each frontend will have it's own set of settings.
|
||||
Each frontend will have its own set of settings.
|
||||
Each component implementation will also ned to have some settings that control it.
|
||||
The terminal also will have some settings specific to the terminal.
|
||||
|
||||
@@ -106,13 +106,13 @@ public interface IApplicationSettings
|
||||
}
|
||||
```
|
||||
|
||||
The Application can store whatever settings it wants in it's implementation of `IApplicationSettings`. When it instantiates a Terminal Component, it will pass it's `IComponentSettings` to it.
|
||||
The Application can store whatever settings it wants in its implementation of `IApplicationSettings`. When it instantiates a Terminal Component, it will pass its `IComponentSettings` to it.
|
||||
|
||||
The component will retrieve whatever settings it wants from that object, and then pass the `TerminalSettings` to the Terminal it creates.
|
||||
|
||||
|
||||
The frontend will be able to get/set it's settings from the `IApplicationSettings` implementation.
|
||||
The frontend will be able to create components using the `IComponentSettings` in it's `IApplicationSettings`.
|
||||
The frontend will be able to get/set its settings from the `IApplicationSettings` implementation.
|
||||
The frontend will be able to create components using the `IComponentSettings` in its `IApplicationSettings`.
|
||||
The Component will then create the Terminal using the `TerminalSettings`.
|
||||
|
||||
#### Project Cascadia Settings Details
|
||||
|
||||
36
samples/ConPTY/EchoCon/EchoCon.sln
Normal file
36
samples/ConPTY/EchoCon/EchoCon.sln
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2026
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EchoCon", "EchoCon\EchoCon.vcxproj", "{96274800-9574-423E-892A-909FBE2AC8BE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{556CAA54-33E0-4F99-95C8-0DFD6E8F6C6B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
readme.md = readme.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x64.Build.0 = Debug|x64
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x86.Build.0 = Debug|Win32
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x64.ActiveCfg = Release|x64
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x64.Build.0 = Release|x64
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x86.ActiveCfg = Release|Win32
|
||||
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B27C5007-61E2-4080-965D-8C934367BA4F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
189
samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp
Normal file
189
samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// EchoCon.cpp : Entry point for the EchoCon Pseudo-Console sample application.
|
||||
// Copyright © 2018, Microsoft
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <Windows.h>
|
||||
#include <process.h>
|
||||
|
||||
// Forward declarations
|
||||
HRESULT CreatePseudoConsoleAndPipes(HPCON*, HANDLE*, HANDLE*);
|
||||
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX*, HPCON);
|
||||
void __cdecl PipeListener(LPVOID);
|
||||
|
||||
int main()
|
||||
{
|
||||
wchar_t szCommand[]{ L"ping localhost" };
|
||||
HRESULT hr{ E_UNEXPECTED };
|
||||
HANDLE hConsole = { GetStdHandle(STD_OUTPUT_HANDLE) };
|
||||
|
||||
// Enable Console VT Processing
|
||||
DWORD consoleMode{};
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
165
samples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj
Normal file
165
samples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj
Normal file
@@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>EchoCon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EchoCon.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
33
samples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters
Normal file
33
samples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EchoCon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
8
samples/ConPTY/EchoCon/EchoCon/stdafx.cpp
Normal file
8
samples/ConPTY/EchoCon/EchoCon/stdafx.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// EchoCon.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
13
samples/ConPTY/EchoCon/EchoCon/stdafx.h
Normal file
13
samples/ConPTY/EchoCon/EchoCon/stdafx.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
8
samples/ConPTY/EchoCon/EchoCon/targetver.h
Normal file
8
samples/ConPTY/EchoCon/EchoCon/targetver.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
34
samples/ConPTY/EchoCon/readme.md
Normal file
34
samples/ConPTY/EchoCon/readme.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# "EchoCon" ConPTY Sample App
|
||||
This is a very simple sample application that illustrates how to use the new Win32 Pseudo Console
|
||||
(ConPTY) by:
|
||||
|
||||
1. Creating an input and an output pipe
|
||||
1. Calling `CreatePseudoConsole()` to create a ConPTY instance attached to the other end of the pipes
|
||||
1. Spawning an instance of `ping.exe` connected to the ConPTY
|
||||
1. Running a thread that listens for output from `ping.exe`, writing received text to the Console
|
||||
|
||||
# Pre-Requirements
|
||||
To build and run this sample, you must install:
|
||||
* Windows 10 Insider build 17733 or later
|
||||
* [Latest Windows 10 Insider SDK](https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewSDK)
|
||||
|
||||
# Running the sample
|
||||
Once successfully built, running EchoCon should clear the screen and display the results of the
|
||||
echo command:
|
||||
|
||||
```
|
||||
Pinging Rincewind [::1] with 32 bytes of data:
|
||||
Reply from ::1: time<1ms
|
||||
Reply from ::1: time<1ms
|
||||
Reply from ::1: time<1ms
|
||||
Reply from ::1: time<1ms
|
||||
|
||||
Ping statistics for ::1:
|
||||
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
|
||||
Approximate round trip times in milli-seconds:
|
||||
Minimum = 0ms, Maximum = 0ms, Average = 0ms
|
||||
```
|
||||
|
||||
# Resources
|
||||
For more information on the new Pseudo Console infrastructure and API, please review
|
||||
[this blog post](https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/)
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GUIConsole.ConPTY.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for Win32's Console API.
|
||||
/// </summary>
|
||||
static class ConsoleApi
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
|
||||
internal delegate bool ConsoleEventDelegate(CtrlTypes ctrlType);
|
||||
|
||||
internal enum CtrlTypes : uint
|
||||
{
|
||||
CTRL_C_EVENT = 0,
|
||||
CTRL_BREAK_EVENT,
|
||||
CTRL_CLOSE_EVENT,
|
||||
CTRL_LOGOFF_EVENT = 5,
|
||||
CTRL_SHUTDOWN_EVENT
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GUIConsole.ConPTY.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for Win32's Process API.
|
||||
/// </summary>
|
||||
static class ProcessApi
|
||||
{
|
||||
internal const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct STARTUPINFOEX
|
||||
{
|
||||
public STARTUPINFO StartupInfo;
|
||||
public IntPtr lpAttributeList;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct STARTUPINFO
|
||||
{
|
||||
public int cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public int dwX;
|
||||
public int dwY;
|
||||
public int dwXSize;
|
||||
public int dwYSize;
|
||||
public int dwXCountChars;
|
||||
public int dwYCountChars;
|
||||
public int dwFillAttribute;
|
||||
public int dwFlags;
|
||||
public short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool InitializeProcThreadAttributeList(
|
||||
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UpdateProcThreadAttribute(
|
||||
IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue,
|
||||
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CreateProcess(
|
||||
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
|
||||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
||||
out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GUIConsole.ConPTY.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for Win32's PseudoConsole API.
|
||||
/// </summary>
|
||||
static class PseudoConsoleApi
|
||||
{
|
||||
internal const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct COORD
|
||||
{
|
||||
public short X;
|
||||
public short Y;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int CreatePseudoConsole(COORD size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int ClosePseudoConsole(IntPtr hPC);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, IntPtr lpPipeAttributes, int nSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static GUIConsole.ConPTY.Native.ProcessApi;
|
||||
|
||||
namespace GUIConsole.ConPTY.Processes
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an instance of a process.
|
||||
/// </summary>
|
||||
internal sealed class Process : IDisposable
|
||||
{
|
||||
public Process(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo)
|
||||
{
|
||||
StartupInfo = startupInfo;
|
||||
ProcessInfo = processInfo;
|
||||
}
|
||||
|
||||
public STARTUPINFOEX StartupInfo { get; }
|
||||
public PROCESS_INFORMATION ProcessInfo { get; }
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// dispose managed state (managed objects).
|
||||
}
|
||||
|
||||
// dispose unmanaged state
|
||||
|
||||
// Free the attribute list
|
||||
if (StartupInfo.lpAttributeList != IntPtr.Zero)
|
||||
{
|
||||
DeleteProcThreadAttributeList(StartupInfo.lpAttributeList);
|
||||
Marshal.FreeHGlobal(StartupInfo.lpAttributeList);
|
||||
}
|
||||
|
||||
// Close process and thread handles
|
||||
if (ProcessInfo.hProcess != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(ProcessInfo.hProcess);
|
||||
}
|
||||
if (ProcessInfo.hThread != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(ProcessInfo.hThread);
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~Process()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using static GUIConsole.ConPTY.Native.ProcessApi;
|
||||
|
||||
namespace GUIConsole.ConPTY.Processes
|
||||
{
|
||||
/// <summary>
|
||||
/// Support for starting and configuring processes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Possible to replace with managed code? The key is being able to provide the PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE attribute
|
||||
/// </remarks>
|
||||
static class ProcessFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Start and configure a process. The return value represents the process and should be disposed.
|
||||
/// </summary>
|
||||
internal static Process Start(string command, IntPtr attributes, IntPtr hPC)
|
||||
{
|
||||
var startupInfo = ConfigureProcessThread(hPC, attributes);
|
||||
var processInfo = RunProcess(ref startupInfo, command);
|
||||
return new Process(startupInfo, processInfo);
|
||||
}
|
||||
|
||||
private static STARTUPINFOEX ConfigureProcessThread(IntPtr hPC, IntPtr attributes)
|
||||
{
|
||||
// this method implements the behavior described in https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#preparing-for-creation-of-the-child-process
|
||||
|
||||
var lpSize = IntPtr.Zero;
|
||||
var success = InitializeProcThreadAttributeList(
|
||||
lpAttributeList: IntPtr.Zero,
|
||||
dwAttributeCount: 1,
|
||||
dwFlags: 0,
|
||||
lpSize: ref lpSize
|
||||
);
|
||||
if (success || lpSize == IntPtr.Zero) // we're not expecting `success` here, we just want to get the calculated lpSize
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not calculate the number of bytes for the attribute list.");
|
||||
}
|
||||
|
||||
var startupInfo = new STARTUPINFOEX();
|
||||
startupInfo.StartupInfo.cb = Marshal.SizeOf<STARTUPINFOEX>();
|
||||
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
|
||||
|
||||
success = InitializeProcThreadAttributeList(
|
||||
lpAttributeList: startupInfo.lpAttributeList,
|
||||
dwAttributeCount: 1,
|
||||
dwFlags: 0,
|
||||
lpSize: ref lpSize
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not set up attribute list.");
|
||||
}
|
||||
|
||||
success = UpdateProcThreadAttribute(
|
||||
lpAttributeList: startupInfo.lpAttributeList,
|
||||
dwFlags: 0,
|
||||
attribute: attributes,
|
||||
lpValue: hPC,
|
||||
cbSize: (IntPtr)IntPtr.Size,
|
||||
lpPreviousValue: IntPtr.Zero,
|
||||
lpReturnSize: IntPtr.Zero
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not set pseudoconsole thread attribute.");
|
||||
}
|
||||
|
||||
return startupInfo;
|
||||
}
|
||||
|
||||
private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine)
|
||||
{
|
||||
int securityAttributeSize = Marshal.SizeOf<SECURITY_ATTRIBUTES>();
|
||||
var pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
|
||||
var tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
|
||||
var success = CreateProcess(
|
||||
lpApplicationName: null,
|
||||
lpCommandLine: commandLine,
|
||||
lpProcessAttributes: ref pSec,
|
||||
lpThreadAttributes: ref tSec,
|
||||
bInheritHandles: false,
|
||||
dwCreationFlags: EXTENDED_STARTUPINFO_PRESENT,
|
||||
lpEnvironment: IntPtr.Zero,
|
||||
lpCurrentDirectory: null,
|
||||
lpStartupInfo: ref sInfoEx,
|
||||
lpProcessInformation: out PROCESS_INFORMATION pInfo
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not create process.");
|
||||
}
|
||||
|
||||
return pInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/PseudoConsole.cs
Normal file
40
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/PseudoConsole.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using static GUIConsole.ConPTY.Native.PseudoConsoleApi;
|
||||
|
||||
namespace GUIConsole.ConPTY
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions around the new Pseudo Console APIs.
|
||||
/// </summary>
|
||||
internal sealed class PseudoConsole : IDisposable
|
||||
{
|
||||
public static readonly IntPtr PseudoConsoleThreadAttribute = (IntPtr)PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE;
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
|
||||
private PseudoConsole(IntPtr handle)
|
||||
{
|
||||
this.Handle = handle;
|
||||
}
|
||||
|
||||
internal static PseudoConsole Create(SafeFileHandle inputReadSide, SafeFileHandle outputWriteSide, int width, int height)
|
||||
{
|
||||
var createResult = CreatePseudoConsole(
|
||||
new COORD { X = (short)width, Y = (short)height },
|
||||
inputReadSide, outputWriteSide,
|
||||
0, out IntPtr hPC);
|
||||
if(createResult != 0)
|
||||
{
|
||||
throw new Win32Exception(createResult, "Could not create pseudo console.");
|
||||
}
|
||||
return new PseudoConsole(hPC);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ClosePseudoConsole(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using static GUIConsole.ConPTY.Native.PseudoConsoleApi;
|
||||
|
||||
namespace GUIConsole.ConPTY
|
||||
{
|
||||
/// <summary>
|
||||
/// A pipe used to talk to the pseudoconsole, as described in:
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We'll have two instances of this class, one for input and one for output.
|
||||
/// </remarks>
|
||||
internal sealed class PseudoConsolePipe : IDisposable
|
||||
{
|
||||
public readonly SafeFileHandle ReadSide;
|
||||
public readonly SafeFileHandle WriteSide;
|
||||
|
||||
public PseudoConsolePipe()
|
||||
{
|
||||
if (!CreatePipe(out ReadSide, out WriteSide, IntPtr.Zero, 0))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "failed to create pipe");
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
ReadSide?.Dispose();
|
||||
WriteSide?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
113
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs
Normal file
113
samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using GUIConsole.ConPTY.Processes;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using static GUIConsole.ConPTY.Native.ConsoleApi;
|
||||
|
||||
namespace GUIConsole.ConPTY
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for managing communication with the underlying console, and communicating with its pseudoconsole.
|
||||
/// </summary>
|
||||
public sealed class Terminal
|
||||
{
|
||||
private const string ExitCommand = "exit\r";
|
||||
private const string CtrlC_Command = "\x3";
|
||||
private SafeFileHandle _consoleInputPipeWriteHandle;
|
||||
private StreamWriter _consoleInputWriter;
|
||||
|
||||
/// <summary>
|
||||
/// A stream of VT-100-enabled output from the console.
|
||||
/// </summary>
|
||||
public FileStream ConsoleOutStream { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired once the console has been hooked up and is ready to receive input.
|
||||
/// </summary>
|
||||
public event EventHandler OutputReady;
|
||||
|
||||
public Terminal()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the psuedoconsole and run the process as shown in
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole
|
||||
/// </summary>
|
||||
/// <param name="command">the command to run, e.g. cmd.exe</param>
|
||||
/// <param name="consoleHeight">The height (in characters) to start the pseudoconsole with. Defaults to 80.</param>
|
||||
/// <param name="consoleWidth">The width (in characters) to start the pseudoconsole with. Defaults to 30.</param>
|
||||
public void Start(string command, int consoleWidth = 80, int consoleHeight = 30)
|
||||
{
|
||||
using (var inputPipe = new PseudoConsolePipe())
|
||||
using (var outputPipe = new PseudoConsolePipe())
|
||||
using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, consoleWidth, consoleHeight))
|
||||
using (var process = ProcessFactory.Start(command, PseudoConsole.PseudoConsoleThreadAttribute, pseudoConsole.Handle))
|
||||
{
|
||||
// copy all pseudoconsole output to a FileStream and expose it to the rest of the app
|
||||
ConsoleOutStream = new FileStream(outputPipe.ReadSide, FileAccess.Read);
|
||||
OutputReady.Invoke(this, EventArgs.Empty);
|
||||
|
||||
// Store input pipe handle, and a writer for later reuse
|
||||
_consoleInputPipeWriteHandle = inputPipe.WriteSide;
|
||||
_consoleInputWriter = new StreamWriter(new FileStream(_consoleInputPipeWriteHandle, FileAccess.Write))
|
||||
{
|
||||
AutoFlush = true
|
||||
};
|
||||
|
||||
// free resources in case the console is ungracefully closed (e.g. by the 'x' in the window titlebar)
|
||||
OnClose(() => DisposeResources(process, pseudoConsole, outputPipe, inputPipe, _consoleInputWriter));
|
||||
|
||||
WaitForExit(process).WaitOne(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the given string to the anonymous pipe that writes to the active pseudoconsole.
|
||||
/// </summary>
|
||||
/// <param name="input">A string of characters to write to the console. Supports VT-100 codes.</param>
|
||||
public void WriteToPseudoConsole(string input)
|
||||
{
|
||||
if (_consoleInputWriter == null)
|
||||
{
|
||||
throw new InvalidOperationException("There is no writer attached to a pseudoconsole. Have you called Start on this instance yet?");
|
||||
}
|
||||
_consoleInputWriter.Write(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an AutoResetEvent that signals when the process exits
|
||||
/// </summary>
|
||||
private static AutoResetEvent WaitForExit(Process process) =>
|
||||
new AutoResetEvent(false)
|
||||
{
|
||||
SafeWaitHandle = new SafeWaitHandle(process.ProcessInfo.hProcess, ownsHandle: false)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Set a callback for when the terminal is closed (e.g. via the "X" window decoration button).
|
||||
/// Intended for resource cleanup logic.
|
||||
/// </summary>
|
||||
private static void OnClose(Action handler)
|
||||
{
|
||||
SetConsoleCtrlHandler(eventType =>
|
||||
{
|
||||
if (eventType == CtrlTypes.CTRL_CLOSE_EVENT)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
return false;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void DisposeResources(params IDisposable[] disposables)
|
||||
{
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.config
Normal file
6
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
8
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.xaml
Normal file
8
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.xaml
Normal file
@@ -0,0 +1,8 @@
|
||||
<Application x:Class="GUIConsole.Wpf.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
15
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.xaml.cs
Normal file
15
samples/ConPTY/GUIConsole/GUIConsole.WPF/App.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace GUIConsole.Wpf
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public App()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
105
samples/ConPTY/GUIConsole/GUIConsole.WPF/GUIConsole.WPF.csproj
Normal file
105
samples/ConPTY/GUIConsole/GUIConsole.WPF/GUIConsole.WPF.csproj
Normal file
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FD2109FE-F78A-4E31-8317-11D1B66B69AF}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>GUIConsole.Wpf</RootNamespace>
|
||||
<AssemblyName>GUIConsole.Wpf</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GUIConsole.ConPTY\GUIConsole.ConPTY.csproj">
|
||||
<Project>{96634c74-0c52-4381-9477-97e1d58fe5b5}</Project>
|
||||
<Name>GUIConsole.ConPTY</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
84
samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml
Normal file
84
samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml
Normal file
@@ -0,0 +1,84 @@
|
||||
<Window x:Class="GUIConsole.Wpf.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
|
||||
Title="MainWindow" Height="450" Width="800"
|
||||
Background="#C7000000"
|
||||
AllowsTransparency="True"
|
||||
WindowStyle="None"
|
||||
MouseDown="Window_MouseDown"
|
||||
BorderThickness="1"
|
||||
BorderBrush="LightSlateGray"
|
||||
|
||||
KeyDown="Window_KeyDown"
|
||||
Loaded="Window_Loaded">
|
||||
<Window.Resources>
|
||||
<Style x:Key="TitleBarButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="Width" Value="46"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource TitleBarButtonStyle}">
|
||||
<Setter Property="Content" Value=""/>
|
||||
<!--Remove the default Button template's Triggers, otherwise they'll override our trigger below.-->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="{TemplateBinding Background}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Button.Background" Value="Red"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="TitleBarTitle" Grid.Column="0" VerticalAlignment="Center" Padding="10 0" Foreground="White">
|
||||
GUIConsole
|
||||
</TextBlock>
|
||||
<StackPanel x:Name="TitleBarButtons" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top">
|
||||
<Button x:Name="MinimizeButton"
|
||||
Click="MinimizeButton_Click"
|
||||
Content=""
|
||||
Style="{StaticResource TitleBarButtonStyle}"/>
|
||||
<Button x:Name="MaximizeRestoreButton"
|
||||
Click="MaximizeRestoreButton_Click"
|
||||
Content=""
|
||||
FontSize="12"
|
||||
Style="{StaticResource TitleBarButtonStyle}"/>
|
||||
<Button x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
FontSize="12"
|
||||
Style="{StaticResource CloseButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ScrollViewer x:Name="TerminalHistoryViewer" Grid.Row="1" ScrollChanged="ScrollViewer_ScrollChanged">
|
||||
<TextBlock x:Name="TerminalHistoryBlock" FontFamily="Consolas" TextWrapping="Wrap" Foreground="White"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
121
samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml.cs
Normal file
121
samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using GUIConsole.ConPTY;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GUIConsole.Wpf
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private Terminal _terminal;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Start up the console, and point it to cmd.exe.
|
||||
_terminal = new Terminal();
|
||||
Task.Run(() => _terminal.Start("powershell.exe"));
|
||||
_terminal.OutputReady += Terminal_OutputReady;
|
||||
}
|
||||
|
||||
private void Terminal_OutputReady(object sender, EventArgs e)
|
||||
{
|
||||
// Start a long-lived thread for the "read console" task, so that we don't use a standard thread pool thread.
|
||||
Task.Factory.StartNew(() => CopyConsoleToWindow(), TaskCreationOptions.LongRunning);
|
||||
|
||||
Dispatcher.Invoke(() => { TitleBarTitle.Text = "GUIConsole - powershell.exe"; });
|
||||
}
|
||||
|
||||
private void CopyConsoleToWindow()
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(_terminal.ConsoleOutStream))
|
||||
{
|
||||
// Read the console's output 1 character at a time
|
||||
int bytesRead;
|
||||
char[] buf = new char[1];
|
||||
while ((bytesRead = reader.ReadBlock(buf, 0, 1)) != 0)
|
||||
{
|
||||
// This is where you'd parse and tokenize the incoming VT100 text, most likely.
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
// ...and then you'd do something to render it.
|
||||
// For now, just emit raw VT100 to the primary TextBlock.
|
||||
TerminalHistoryBlock.Text += new string(buf.Take(bytesRead).ToArray());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (!e.Handled)
|
||||
{
|
||||
// This is where you'd take the pressed key, and convert it to a
|
||||
// VT100 code before sending it along. For now, we'll just send _something_.
|
||||
_terminal.WriteToPseudoConsole(e.Key.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private bool _autoScroll = true;
|
||||
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
|
||||
{
|
||||
// User scrolled...
|
||||
if (e.ExtentHeightChange == 0)
|
||||
{
|
||||
//...down to the bottom. Re-engage autoscrolling.
|
||||
if (TerminalHistoryViewer.VerticalOffset == TerminalHistoryViewer.ScrollableHeight)
|
||||
{
|
||||
_autoScroll = true;
|
||||
}
|
||||
//...elsewhere. Disengage autoscrolling.
|
||||
else
|
||||
{
|
||||
_autoScroll = false;
|
||||
}
|
||||
|
||||
// Autoscrolling is enabled, and content caused scrolling:
|
||||
if (_autoScroll && e.ExtentHeightChange != 0)
|
||||
{
|
||||
TerminalHistoryViewer.ScrollToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left) { DragMove(); }
|
||||
}
|
||||
|
||||
private void MaximizeRestoreButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (WindowState == WindowState.Normal)
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
MaximizeRestoreButton.Content = "\uE923";
|
||||
}
|
||||
else if (WindowState == WindowState.Maximized)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
MaximizeRestoreButton.Content = "\uE922";
|
||||
}
|
||||
}
|
||||
|
||||
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowState = WindowState.Minimized;
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GUIConsole.Wpf")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GUIConsole.Wpf")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
63
samples/ConPTY/GUIConsole/GUIConsole.WPF/Properties/Resources.Designer.cs
generated
Normal file
63
samples/ConPTY/GUIConsole/GUIConsole.WPF/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GUIConsole.Wpf.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GUIConsole.Wpf.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
26
samples/ConPTY/GUIConsole/GUIConsole.WPF/Properties/Settings.Designer.cs
generated
Normal file
26
samples/ConPTY/GUIConsole/GUIConsole.WPF/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GUIConsole.Wpf.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
61
samples/ConPTY/GUIConsole/GUIConsole.sln
Normal file
61
samples/ConPTY/GUIConsole/GUIConsole.sln
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26124.0
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUIConsole.WPF", "GUIConsole.WPF\GUIConsole.WPF.csproj", "{FD2109FE-F78A-4E31-8317-11D1B66B69AF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GUIConsole.ConPTY", "GUIConsole.ConPTY\GUIConsole.ConPTY.csproj", "{96634C74-0C52-4381-9477-97E1D58FE5B5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FD2109FE-F78A-4E31-8317-11D1B66B69AF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{96634C74-0C52-4381-9477-97E1D58FE5B5}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0066B3A2-194D-471B-A56D-E25BB5AC0EB4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
9
samples/ConPTY/GUIConsole/README.md
Normal file
9
samples/ConPTY/GUIConsole/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# GUIConsole
|
||||
|
||||
This is an example of what the skeleton of a custom WPF console might look like.
|
||||
|
||||
The `GUIConsole.WPF` project is a WPF application targeting .NET 4.6.1. It creates a single WPF `Window` that acts as the console, and keeps the underlying console visible.
|
||||
|
||||
The `GUIConsole.ConPTY` project is a .NET Standard 2.0 library that handles the creation of the console, and enables pseudoconsole behavior. `Terminal.cs` contains the publicly visible pieces that the WPF application will interact with. `Terminal.cs` exposes two things that allow reading from, and writing to, the console:
|
||||
* `ConsoleOutStream`, a `FileStream` hooked up to the pseudoconsole's output pipe. This will output VT100.
|
||||
* `WriteToPseudoConsole(string input)`, a method that will take the given string and write it to the pseudoconsole via its input pipe. This accepts VT100.
|
||||
25
samples/ConPTY/MiniTerm/MiniTerm.sln
Normal file
25
samples/ConPTY/MiniTerm/MiniTerm.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2035
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniTerm", "MiniTerm\MiniTerm.csproj", "{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EFB13BDE-C952-4311-9FE7-35EFDAC8F021}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
6
samples/ConPTY/MiniTerm/MiniTerm/App.config
Normal file
6
samples/ConPTY/MiniTerm/MiniTerm/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
60
samples/ConPTY/MiniTerm/MiniTerm/MiniTerm.csproj
Normal file
60
samples/ConPTY/MiniTerm/MiniTerm/MiniTerm.csproj
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{121D4818-BD57-433B-8AD5-C4E1ACE7E7C0}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>MiniTerm</RootNamespace>
|
||||
<AssemblyName>MiniTerm</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Native\ConsoleApi.cs" />
|
||||
<Compile Include="Native\ProcessApi.cs" />
|
||||
<Compile Include="Native\PseudoConsoleApi.cs" />
|
||||
<Compile Include="Processes\Process.cs" />
|
||||
<Compile Include="Processes\ProcessFactory.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PseudoConsole.cs" />
|
||||
<Compile Include="PseudoConsolePipe.cs" />
|
||||
<Compile Include="Terminal.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
38
samples/ConPTY/MiniTerm/MiniTerm/Native/ConsoleApi.cs
Normal file
38
samples/ConPTY/MiniTerm/MiniTerm/Native/ConsoleApi.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MiniTerm.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for win32 console api
|
||||
/// </summary>
|
||||
static class ConsoleApi
|
||||
{
|
||||
internal const int STD_OUTPUT_HANDLE = -11;
|
||||
internal const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
|
||||
internal const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern SafeFileHandle GetStdHandle(int nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetConsoleMode(SafeFileHandle hConsoleHandle, uint mode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool GetConsoleMode(SafeFileHandle handle, out uint mode);
|
||||
|
||||
internal delegate bool ConsoleEventDelegate(CtrlTypes ctrlType);
|
||||
|
||||
internal enum CtrlTypes : uint
|
||||
{
|
||||
CTRL_C_EVENT = 0,
|
||||
CTRL_BREAK_EVENT,
|
||||
CTRL_CLOSE_EVENT,
|
||||
CTRL_LOGOFF_EVENT = 5,
|
||||
CTRL_SHUTDOWN_EVENT
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
|
||||
}
|
||||
}
|
||||
86
samples/ConPTY/MiniTerm/MiniTerm/Native/ProcessApi.cs
Normal file
86
samples/ConPTY/MiniTerm/MiniTerm/Native/ProcessApi.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MiniTerm.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for win32 process api
|
||||
/// </summary>
|
||||
static class ProcessApi
|
||||
{
|
||||
internal const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct STARTUPINFOEX
|
||||
{
|
||||
public STARTUPINFO StartupInfo;
|
||||
public IntPtr lpAttributeList;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct STARTUPINFO
|
||||
{
|
||||
public Int32 cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public Int32 dwX;
|
||||
public Int32 dwY;
|
||||
public Int32 dwXSize;
|
||||
public Int32 dwYSize;
|
||||
public Int32 dwXCountChars;
|
||||
public Int32 dwYCountChars;
|
||||
public Int32 dwFillAttribute;
|
||||
public Int32 dwFlags;
|
||||
public Int16 wShowWindow;
|
||||
public Int16 cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool InitializeProcThreadAttributeList(
|
||||
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UpdateProcThreadAttribute(
|
||||
IntPtr lpAttributeList, uint dwFlags, IntPtr attribute, IntPtr lpValue,
|
||||
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CreateProcess(
|
||||
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
|
||||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
||||
out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
}
|
||||
33
samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs
Normal file
33
samples/ConPTY/MiniTerm/MiniTerm/Native/PseudoConsoleApi.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MiniTerm.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// PInvoke signatures for win32 pseudo console api
|
||||
/// </summary>
|
||||
static class PseudoConsoleApi
|
||||
{
|
||||
internal const uint PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct COORD
|
||||
{
|
||||
public short X;
|
||||
public short Y;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int CreatePseudoConsole(COORD size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out IntPtr phPC);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int ResizePseudoConsole(IntPtr hPC, COORD size);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern int ClosePseudoConsole(IntPtr hPC);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, IntPtr lpPipeAttributes, int nSize);
|
||||
}
|
||||
}
|
||||
74
samples/ConPTY/MiniTerm/MiniTerm/Processes/Process.cs
Normal file
74
samples/ConPTY/MiniTerm/MiniTerm/Processes/Process.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static MiniTerm.Native.ProcessApi;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an instance of a process.
|
||||
/// </summary>
|
||||
internal sealed class Process : IDisposable
|
||||
{
|
||||
public Process(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo)
|
||||
{
|
||||
StartupInfo = startupInfo;
|
||||
ProcessInfo = processInfo;
|
||||
}
|
||||
|
||||
public STARTUPINFOEX StartupInfo { get; }
|
||||
public PROCESS_INFORMATION ProcessInfo { get; }
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// dispose managed state (managed objects).
|
||||
}
|
||||
|
||||
// dispose unmanaged state
|
||||
|
||||
// Free the attribute list
|
||||
if (StartupInfo.lpAttributeList != IntPtr.Zero)
|
||||
{
|
||||
DeleteProcThreadAttributeList(StartupInfo.lpAttributeList);
|
||||
Marshal.FreeHGlobal(StartupInfo.lpAttributeList);
|
||||
}
|
||||
|
||||
// Close process and thread handles
|
||||
if (ProcessInfo.hProcess != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(ProcessInfo.hProcess);
|
||||
}
|
||||
if (ProcessInfo.hThread != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(ProcessInfo.hThread);
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~Process()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// use the following line if the finalizer is overridden above.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
98
samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs
Normal file
98
samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static MiniTerm.Native.ProcessApi;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// Support for starting and configuring processes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Possible to replace with managed code? The key is being able to provide the PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE attribute
|
||||
/// </remarks>
|
||||
static class ProcessFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Start and configure a process. The return value represents the process and should be disposed.
|
||||
/// </summary>
|
||||
internal static Process Start(string command, IntPtr attributes, IntPtr hPC)
|
||||
{
|
||||
var startupInfo = ConfigureProcessThread(hPC, attributes);
|
||||
var processInfo = RunProcess(ref startupInfo, "cmd.exe");
|
||||
return new Process(startupInfo, processInfo);
|
||||
}
|
||||
|
||||
private static STARTUPINFOEX ConfigureProcessThread(IntPtr hPC, IntPtr attributes)
|
||||
{
|
||||
// this method implements the behavior described in https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#preparing-for-creation-of-the-child-process
|
||||
|
||||
var lpSize = IntPtr.Zero;
|
||||
var success = InitializeProcThreadAttributeList(
|
||||
lpAttributeList: IntPtr.Zero,
|
||||
dwAttributeCount: 1,
|
||||
dwFlags: 0,
|
||||
lpSize: ref lpSize
|
||||
);
|
||||
if (success || lpSize == IntPtr.Zero) // we're not expecting `success` here, we just want to get the calculated lpSize
|
||||
{
|
||||
throw new InvalidOperationException("Could not calculate the number of bytes for the attribute list. " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var startupInfo = new STARTUPINFOEX();
|
||||
startupInfo.StartupInfo.cb = Marshal.SizeOf<STARTUPINFOEX>();
|
||||
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
|
||||
|
||||
success = InitializeProcThreadAttributeList(
|
||||
lpAttributeList: startupInfo.lpAttributeList,
|
||||
dwAttributeCount: 1,
|
||||
dwFlags: 0,
|
||||
lpSize: ref lpSize
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new InvalidOperationException("Could not set up attribute list. " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
success = UpdateProcThreadAttribute(
|
||||
lpAttributeList: startupInfo.lpAttributeList,
|
||||
dwFlags: 0,
|
||||
attribute: attributes,
|
||||
lpValue: hPC,
|
||||
cbSize: (IntPtr)IntPtr.Size,
|
||||
lpPreviousValue: IntPtr.Zero,
|
||||
lpReturnSize: IntPtr.Zero
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new InvalidOperationException("Could not set pseudoconsole thread attribute. " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return startupInfo;
|
||||
}
|
||||
|
||||
private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine)
|
||||
{
|
||||
int securityAttributeSize = Marshal.SizeOf<SECURITY_ATTRIBUTES>();
|
||||
var pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
|
||||
var tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
|
||||
var success = CreateProcess(
|
||||
lpApplicationName: null,
|
||||
lpCommandLine: commandLine,
|
||||
lpProcessAttributes: ref pSec,
|
||||
lpThreadAttributes: ref tSec,
|
||||
bInheritHandles: false,
|
||||
dwCreationFlags: EXTENDED_STARTUPINFO_PRESENT,
|
||||
lpEnvironment: IntPtr.Zero,
|
||||
lpCurrentDirectory: null,
|
||||
lpStartupInfo: ref sInfoEx,
|
||||
lpProcessInformation: out PROCESS_INFORMATION pInfo
|
||||
);
|
||||
if (!success)
|
||||
{
|
||||
throw new InvalidOperationException("Could not create process. " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return pInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
samples/ConPTY/MiniTerm/MiniTerm/Program.cs
Normal file
35
samples/ConPTY/MiniTerm/MiniTerm/Program.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// C# version of:
|
||||
/// https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
|
||||
///
|
||||
/// System Requirements:
|
||||
/// As of September 2018, requires Windows 10 with the "Windows Insider Program" installed for Redstone 5.
|
||||
/// Also requires the Windows Insider Preview SDK: https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewSDK
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Basic design is:
|
||||
/// Terminal UI starts the PseudoConsole, and controls it using a pair of PseudoConsolePipes
|
||||
/// Terminal UI will run the Process (cmd.exe) and associate it with the PseudoConsole.
|
||||
/// </remarks>
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var terminal = new Terminal();
|
||||
terminal.Run("cmd.exe");
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Console.Error.WriteLine(e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
samples/ConPTY/MiniTerm/MiniTerm/Properties/AssemblyInfo.cs
Normal file
36
samples/ConPTY/MiniTerm/MiniTerm/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MiniPty")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MiniPty")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("121d4818-bd57-433b-8ad5-c4e1ace7e7c0")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
39
samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs
Normal file
39
samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using static MiniTerm.Native.PseudoConsoleApi;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions around the new Pseudo Console APIs
|
||||
/// </summary>
|
||||
internal sealed class PseudoConsole : IDisposable
|
||||
{
|
||||
public static readonly IntPtr PseudoConsoleThreadAttribute = (IntPtr)PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE;
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
|
||||
private PseudoConsole(IntPtr handle)
|
||||
{
|
||||
this.Handle = handle;
|
||||
}
|
||||
|
||||
internal static PseudoConsole Create(SafeFileHandle inputReadSide, SafeFileHandle outputWriteSide, int width, int height)
|
||||
{
|
||||
var createResult = CreatePseudoConsole(
|
||||
new COORD { X = (short)width, Y = (short)height },
|
||||
inputReadSide, outputWriteSide,
|
||||
0, out IntPtr hPC);
|
||||
if(createResult != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Could not create psuedo console. Error Code " + createResult);
|
||||
}
|
||||
return new PseudoConsole(hPC);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ClosePseudoConsole(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs
Normal file
46
samples/ConPTY/MiniTerm/MiniTerm/PseudoConsolePipe.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using static MiniTerm.Native.PseudoConsoleApi;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// A pipe used to talk to the pseudoconsole, as described in:
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We'll have two instances of this class, one for input and one for output.
|
||||
/// </remarks>
|
||||
internal sealed class PseudoConsolePipe : IDisposable
|
||||
{
|
||||
public readonly SafeFileHandle ReadSide;
|
||||
public readonly SafeFileHandle WriteSide;
|
||||
|
||||
public PseudoConsolePipe()
|
||||
{
|
||||
if (!CreatePipe(out ReadSide, out WriteSide, IntPtr.Zero, 0))
|
||||
{
|
||||
throw new InvalidOperationException("failed to create pipe");
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
ReadSide?.Dispose();
|
||||
WriteSide?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
145
samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs
Normal file
145
samples/ConPTY/MiniTerm/MiniTerm/Terminal.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static MiniTerm.Native.ConsoleApi;
|
||||
|
||||
namespace MiniTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// The UI of the terminal. It's just a normal console window, but we're managing the input/output.
|
||||
/// In a "real" project this could be some other UI.
|
||||
/// </summary>
|
||||
internal sealed class Terminal
|
||||
{
|
||||
private const string ExitCommand = "exit\r";
|
||||
private const string CtrlC_Command = "\x3";
|
||||
|
||||
public Terminal()
|
||||
{
|
||||
EnableVirtualTerminalSequenceProcessing();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Newer versions of the windows console support interpreting virtual terminal sequences, we just have to opt-in
|
||||
/// </summary>
|
||||
private static void EnableVirtualTerminalSequenceProcessing()
|
||||
{
|
||||
var hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (!GetConsoleMode(hStdOut, out uint outConsoleMode))
|
||||
{
|
||||
throw new InvalidOperationException("Could not get console mode");
|
||||
}
|
||||
|
||||
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
|
||||
if (!SetConsoleMode(hStdOut, outConsoleMode))
|
||||
{
|
||||
throw new InvalidOperationException("Could not enable virtual terminal processing");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the psuedoconsole and run the process as shown in
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole
|
||||
/// </summary>
|
||||
/// <param name="command">the command to run, e.g. cmd.exe</param>
|
||||
public void Run(string command)
|
||||
{
|
||||
using (var inputPipe = new PseudoConsolePipe())
|
||||
using (var outputPipe = new PseudoConsolePipe())
|
||||
using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, (short)Console.WindowWidth, (short)Console.WindowHeight))
|
||||
using (var process = ProcessFactory.Start(command, PseudoConsole.PseudoConsoleThreadAttribute, pseudoConsole.Handle))
|
||||
{
|
||||
// copy all pseudoconsole output to stdout
|
||||
Task.Run(() => CopyPipeToOutput(outputPipe.ReadSide));
|
||||
// prompt for stdin input and send the result to the pseudoconsole
|
||||
Task.Run(() => CopyInputToPipe(inputPipe.WriteSide));
|
||||
// free resources in case the console is ungracefully closed (e.g. by the 'x' in the window titlebar)
|
||||
OnClose(() => DisposeResources(process, pseudoConsole, outputPipe, inputPipe));
|
||||
|
||||
WaitForExit(process).WaitOne(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads terminal input and copies it to the PseudoConsole
|
||||
/// </summary>
|
||||
/// <param name="inputWriteSide">the "write" side of the pseudo console input pipe</param>
|
||||
private static void CopyInputToPipe(SafeFileHandle inputWriteSide)
|
||||
{
|
||||
using (var writer = new StreamWriter(new FileStream(inputWriteSide, FileAccess.Write)))
|
||||
{
|
||||
ForwardCtrlC(writer);
|
||||
writer.AutoFlush = true;
|
||||
writer.WriteLine(@"cd \");
|
||||
|
||||
while (true)
|
||||
{
|
||||
// send input character-by-character to the pipe
|
||||
char key = Console.ReadKey(intercept: true).KeyChar;
|
||||
writer.Write(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Don't let ctrl-c kill the terminal, it should be sent to the process in the terminal.
|
||||
/// </summary>
|
||||
private static void ForwardCtrlC(StreamWriter writer)
|
||||
{
|
||||
Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true;
|
||||
writer.Write(CtrlC_Command);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads PseudoConsole output and copies it to the terminal's standard out.
|
||||
/// </summary>
|
||||
/// <param name="outputReadSide">the "read" side of the pseudo console output pipe</param>
|
||||
private static void CopyPipeToOutput(SafeFileHandle outputReadSide)
|
||||
{
|
||||
using (var terminalOutput = Console.OpenStandardOutput())
|
||||
using (var pseudoConsoleOutput = new FileStream(outputReadSide, FileAccess.Read))
|
||||
{
|
||||
pseudoConsoleOutput.CopyTo(terminalOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an AutoResetEvent that signals when the process exits
|
||||
/// </summary>
|
||||
private static AutoResetEvent WaitForExit(Process process) =>
|
||||
new AutoResetEvent(false)
|
||||
{
|
||||
SafeWaitHandle = new SafeWaitHandle(process.ProcessInfo.hProcess, ownsHandle: false)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Set a callback for when the terminal is closed (e.g. via the "X" window decoration button).
|
||||
/// Intended for resource cleanup logic.
|
||||
/// </summary>
|
||||
private static void OnClose(Action handler)
|
||||
{
|
||||
SetConsoleCtrlHandler(eventType =>
|
||||
{
|
||||
if(eventType == CtrlTypes.CTRL_CLOSE_EVENT)
|
||||
{
|
||||
handler();
|
||||
}
|
||||
return false;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void DisposeResources(params IDisposable[] disposables)
|
||||
{
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
samples/ConPTY/MiniTerm/README.md
Normal file
19
samples/ConPTY/MiniTerm/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# MiniTerm
|
||||
|
||||
Experimental terminal using the new PTY APIs from Microsoft. Written in C#, and heavily based on the native code examples.
|
||||
|
||||
## Status
|
||||
|
||||
Demonstrates the basic API calls required, but not intended for "real-world" usage.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Introductory blog post from Microsoft](https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/)
|
||||
- [MSDN Documentation](https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session)
|
||||
|
||||
## System Requirements
|
||||
|
||||
See the Introductory blog post from Microsoft for the full setup instructions. This project has been tested with:
|
||||
|
||||
- OS: Windows 10 Pro Build 17744
|
||||
- SDK: Windows_InsiderPreview_SDK_en-us_17749
|
||||
58
samples/ReadConsoleInputStream/ConcurrentBoundedQueue.cs
Normal file
58
samples/ReadConsoleInputStream/ConcurrentBoundedQueue.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Samples.Terminal
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a bounded queue that won't block on overflow; instead the oldest item is discarded.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ConcurrentBoundedQueue<T> : ConcurrentQueue<T>
|
||||
{
|
||||
public ConcurrentBoundedQueue(int capacity)
|
||||
{
|
||||
Capacity = GetAlignedCapacity(capacity);
|
||||
}
|
||||
|
||||
public ConcurrentBoundedQueue(IEnumerable<T> collection, int capacity) : base(collection)
|
||||
{
|
||||
Capacity = GetAlignedCapacity(capacity);
|
||||
}
|
||||
|
||||
private int GetAlignedCapacity(int n)
|
||||
{
|
||||
if (n < 2)
|
||||
{
|
||||
throw new ArgumentException("Capacity must be at least 2");
|
||||
}
|
||||
|
||||
var f = Math.Log(n, 2);
|
||||
var p = Math.Ceiling(f);
|
||||
|
||||
return (int) Math.Pow(2, p);
|
||||
}
|
||||
|
||||
public new void Enqueue(T item)
|
||||
{
|
||||
// if we're about to overflow, dump oldest item
|
||||
if (Count >= Capacity)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
while (Count >= Capacity)
|
||||
{
|
||||
TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Enqueue(item);
|
||||
}
|
||||
|
||||
public int Capacity
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
samples/ReadConsoleInputStream/NativeMethods.cs
Normal file
31
samples/ReadConsoleInputStream/NativeMethods.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Samples.Terminal
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
private static int MakeHRFromErrorCode(int errorCode)
|
||||
{
|
||||
// Don't convert it if it is already an HRESULT
|
||||
if ((0xFFFF0000 & errorCode) != 0)
|
||||
{
|
||||
Debug.Assert(false, "errorCode is already HRESULT");
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
return unchecked(((int)0x80070000) | errorCode);
|
||||
}
|
||||
|
||||
internal static Exception GetExceptionForWin32Error(int errorCode)
|
||||
{
|
||||
return Marshal.GetExceptionForHR(MakeHRFromErrorCode(errorCode));
|
||||
}
|
||||
|
||||
internal static Exception GetExceptionForLastWin32Error()
|
||||
{
|
||||
return GetExceptionForWin32Error(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
}
|
||||
147
samples/ReadConsoleInputStream/Program.cs
Normal file
147
samples/ReadConsoleInputStream/Program.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This is a demo that shows how we can have a stream-oriented view of characters from the console
|
||||
* while also listening to console events like mouse, menu, focus, buffer/viewport(1) resize events.
|
||||
*
|
||||
* This has always been tricky to do because ReadConsoleW/A doesn't allow retrieving events.
|
||||
* Only ReadConsoleInputW/A returns events, but isn't stream-oriented. Using both doesn't work because
|
||||
* ReadConsoleW/A flushes the input queue, meaning calls to ReadConsoleInputW/A will wait forever.
|
||||
*
|
||||
* I do this by deriving a new Stream class which wraps ReadConsoleInputW and accepts a provider/consumer
|
||||
* implementation of BlockingCollection<Kernel32.INPUT_RECORD>. This allows asynchronous monitoring of
|
||||
* console events while simultaneously streaming the character input. I also use Mark Gravell's great
|
||||
* System.IO.Pipelines utility classes (2) and David Hall's excellent P/Invoke wrappers (3) to make this
|
||||
* demo cleaner to read; both are pulled from NuGet.
|
||||
*
|
||||
* (1) in versions of windows 10 prior to 1809, the buffer resize event only fires for enlarging
|
||||
* the viewport, as this would cause the buffer to be enlarged too. Now it fires even when
|
||||
* shrinking the viewport, which won't change the buffer size.
|
||||
*
|
||||
* (2) https://github.com/mgravell/Pipelines.Sockets.Unofficial
|
||||
* https://www.nuget.org/packages/Pipelines.Sockets.Unofficial
|
||||
*
|
||||
* (3) https://github.com/dahall/Vanara
|
||||
* https://www.nuget.org/packages/Vanara.Pinvoke.Kernel32
|
||||
*
|
||||
* Oisin Grehan - 2019/4/21
|
||||
*
|
||||
* https://twitter.com/oising
|
||||
* https://github.com/oising
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Pipelines.Sockets.Unofficial;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace Samples.Terminal
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
// run for 90 seconds
|
||||
var timeout = TimeSpan.FromSeconds(90);
|
||||
|
||||
// in reality this will likely never be reached, but it is useful to guard against
|
||||
// conditions where the queue isn't drained, or not drained fast enough.
|
||||
const int maxNonKeyEventRetention = 128;
|
||||
|
||||
var source = new CancellationTokenSource(timeout);
|
||||
var token = source.Token;
|
||||
var handle = Kernel32.GetStdHandle(Kernel32.StdHandleType.STD_INPUT_HANDLE);
|
||||
|
||||
if (!Kernel32.GetConsoleMode(handle, out Kernel32.CONSOLE_INPUT_MODE mode))
|
||||
throw NativeMethods.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
|
||||
|
||||
mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_WINDOW_INPUT;
|
||||
mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_ECHO_INPUT;
|
||||
mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_LINE_INPUT;
|
||||
|
||||
if (!Kernel32.SetConsoleMode(handle, mode))
|
||||
throw NativeMethods.GetExceptionForLastWin32Error();
|
||||
|
||||
// base our provider/consumer on a bounded queue to keep memory usage under control
|
||||
var events = new BlockingCollection<Kernel32.INPUT_RECORD>(
|
||||
new ConcurrentBoundedQueue<Kernel32.INPUT_RECORD>(maxNonKeyEventRetention));
|
||||
|
||||
// Task that will consume non-key events asynchronously
|
||||
var consumeEvents = Task.Run(() =>
|
||||
{
|
||||
Console.WriteLine("consumeEvents started");
|
||||
|
||||
try
|
||||
{
|
||||
while (!events.IsCompleted)
|
||||
{
|
||||
// blocking call
|
||||
var record = events.Take(token);
|
||||
|
||||
Console.WriteLine("record: {0}",
|
||||
Enum.GetName(typeof(Kernel32.EVENT_TYPE), record.EventType));
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// timeout
|
||||
}
|
||||
|
||||
Console.WriteLine("consumeEvents ended");
|
||||
}, token);
|
||||
|
||||
// Task that will watch for key events while feeding non-key events into our provider/consumer collection
|
||||
var readInputAndProduceEvents = Task.Run(async () =>
|
||||
{
|
||||
//So, this is the key point - we cannot use the following or we lose all non-key events:
|
||||
// Stream stdin = Console.OpenStandardInput();
|
||||
|
||||
// get a unicode character stream over console input
|
||||
Stream stdin = new ReadConsoleInputStream(handle, events);
|
||||
|
||||
// wrap in a System.IO.Pipelines.PipeReader to get clean async and span/memory usage
|
||||
var reader = StreamConnection.GetReader(stdin);
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
// blocking call
|
||||
var result = await reader.ReadAsync(token);
|
||||
|
||||
if (result.IsCanceled)
|
||||
break;
|
||||
|
||||
var sequence = result.Buffer;
|
||||
var segment = sequence.Start;
|
||||
|
||||
while (sequence.TryGet(ref segment, out var mem))
|
||||
{
|
||||
// decode back from unicode
|
||||
var datum = Encoding.Unicode.GetString(mem.Span);
|
||||
Console.Write(datum);
|
||||
}
|
||||
|
||||
reader.AdvanceTo(sequence.End);
|
||||
}
|
||||
}, token);
|
||||
|
||||
Console.WriteLine("Running");
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(consumeEvents, readInputAndProduceEvents);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// timeout
|
||||
}
|
||||
|
||||
Console.WriteLine("press any key...");
|
||||
Console.ReadKey(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
samples/ReadConsoleInputStream/README.md
Normal file
16
samples/ReadConsoleInputStream/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# ReadConsoleInputStream Demo
|
||||
|
||||
This is a demo that shows how we can have a stream-oriented view of characters from the console while also listening to console events like mouse, menu, focus, buffer/viewport resize events. This is particularly useful when working with VT100 streams and ConPTY.
|
||||
|
||||
This has always been difficult to do because ReadConsoleW/A doesn't allow retrieving events. Only ReadConsoleInputW/A returns events, but is not stream-oriented. Using both does not work because ReadConsoleW/A flushes the input queue, meaning calls to ReadConsoleInputW/A will wait forever.
|
||||
|
||||
A workaround has been achieved by deriving a new Stream class which wraps ReadConsoleInputW and accepts a provider/consumer implementation of BlockingCollection<Kernel32.INPUT_RECORD>. This allows asynchronous monitoring of console events while simultaneously streaming the character input. Mark Gravell's great System.IO.Pipelines utility classes and David Hall's excellent P/Invoke wrappers are also used to make this demo cleaner to read; both are pulled from NuGet.
|
||||
|
||||
**Note:**
|
||||
|
||||
In versions of Windows 10 prior to 1809, the buffer resize event only fires for enlarging the viewport, as this would cause the buffer to be enlarged too. Now it fires even when shrinking the viewport, which won't change the buffer size.
|
||||
|
||||
NuGet packages used (GitHub links):
|
||||
|
||||
* [Pipelines.Sockets.Unofficial](https://github.com/mgravell/Pipelines.Sockets.Unofficial)
|
||||
* [Vanara P/Invoke](https://github.com/dahall/Vanara)
|
||||
198
samples/ReadConsoleInputStream/ReadConsoleInputStream.cs
Normal file
198
samples/ReadConsoleInputStream/ReadConsoleInputStream.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace Samples.Terminal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a Stream-oriented view over the console's input buffer key events
|
||||
/// while also collecting out of band events like buffer resize, menu etc in
|
||||
/// a caller-provided BlockingCollection.
|
||||
/// </summary>
|
||||
/// <remarks>The buffer contains unicode chars, not 8 bit CP encoded chars as we rely on ReadConsoleInputW.</remarks>
|
||||
public sealed class ReadConsoleInputStream : Stream
|
||||
{
|
||||
private const int BufferSize = 256;
|
||||
private const int BytesPerWChar = 2;
|
||||
private readonly BlockingCollection<Kernel32.INPUT_RECORD> _nonKeyEvents;
|
||||
private IntPtr _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of ReadConsoleInputStream over the standard handle for StdIn.
|
||||
/// </summary>
|
||||
/// <param name="nonKeyEvents">A BlockingCollection provider/consumer collection for collecting non key events.</param>
|
||||
public ReadConsoleInputStream(BlockingCollection<Kernel32.INPUT_RECORD> nonKeyEvents) :
|
||||
this(Kernel32.GetStdHandle(Kernel32.StdHandleType.STD_INPUT_HANDLE), nonKeyEvents)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of ReadConsoleInputStream over a caller-provided standard handle for stdin.
|
||||
/// </summary>
|
||||
/// <param name="handle">A HFILE handle representing StdIn</param>
|
||||
/// <param name="nonKeyEvents">A BlockingCollection provider/consumer collection for collecting non key events.</param>
|
||||
internal ReadConsoleInputStream(HFILE handle,
|
||||
BlockingCollection<Kernel32.INPUT_RECORD> nonKeyEvents)
|
||||
{
|
||||
Debug.Assert(handle.IsInvalid == false, "handle.IsInvalid == false");
|
||||
|
||||
_handle = handle.DangerousGetHandle();
|
||||
_nonKeyEvents = nonKeyEvents;
|
||||
}
|
||||
|
||||
public override bool CanRead { get; } = true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override long Length => throw new NotSupportedException("Seek not supported.");
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException("Seek not supported.");
|
||||
set => throw new NotSupportedException("Seek not supported.");
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_handle = IntPtr.Zero;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ValidateRead(buffer, offset, count);
|
||||
|
||||
Debug.Assert(offset >= 0, "offset >= 0");
|
||||
Debug.Assert(count >= 0, "count >= 0");
|
||||
Debug.Assert(buffer != null, "bytes != null");
|
||||
|
||||
// Don't corrupt memory when multiple threads are erroneously writing
|
||||
// to this stream simultaneously.
|
||||
if (buffer.Length - offset < count)
|
||||
throw new IndexOutOfRangeException("IndexOutOfRange_IORaceCondition");
|
||||
|
||||
int bytesRead;
|
||||
int ret;
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
bytesRead = 0;
|
||||
ret = Win32Error.ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
var charsRead = 0;
|
||||
bytesRead = 0;
|
||||
|
||||
var records = new Kernel32.INPUT_RECORD[BufferSize];
|
||||
|
||||
// begin input loop
|
||||
do
|
||||
{
|
||||
var readSuccess = Kernel32.ReadConsoleInput(_handle, records, BufferSize, out var recordsRead);
|
||||
Debug.WriteLine("Read {0} input record(s)", recordsRead);
|
||||
|
||||
// some of the arithmetic here is deliberately more explicit than it needs to be
|
||||
// in order to show how 16-bit unicode WCHARs are packed into the buffer. The console
|
||||
// subsystem is one of the last bastions of UCS-2, so until UTF-16 is fully adopted
|
||||
// the two-byte character assumptions below will hold.
|
||||
if (readSuccess && recordsRead > 0)
|
||||
{
|
||||
for (var index = 0; index < recordsRead; index++)
|
||||
{
|
||||
var record = records[index];
|
||||
|
||||
if (record.EventType == Kernel32.EVENT_TYPE.KEY_EVENT)
|
||||
{
|
||||
// skip key up events - if not, every key will be duped in the stream
|
||||
if (record.Event.KeyEvent.bKeyDown == false) continue;
|
||||
|
||||
// pack ucs-2/utf-16le/unicode chars into position in our byte[] buffer.
|
||||
var glyph = (ushort) record.Event.KeyEvent.uChar;
|
||||
|
||||
var lsb = (byte) (glyph & 0xFFu);
|
||||
var msb = (byte) ((glyph >> 8) & 0xFFu);
|
||||
|
||||
// ensure we accommodate key repeat counts
|
||||
for (var n = 0; n < record.Event.KeyEvent.wRepeatCount; n++)
|
||||
{
|
||||
buffer[offset + charsRead * BytesPerWChar] = lsb;
|
||||
buffer[offset + charsRead * BytesPerWChar + 1] = msb;
|
||||
|
||||
charsRead++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore focus events; not doing so makes debugging absolutely hilarious
|
||||
// when breakpoints repeatedly cause focus events to occur as your view toggles
|
||||
// between IDE and console.
|
||||
if (record.EventType != Kernel32.EVENT_TYPE.FOCUS_EVENT)
|
||||
{
|
||||
// I assume success adding records - this is not so critical
|
||||
// if it is critical to you, loop on this with a miniscule delay
|
||||
_nonKeyEvents.TryAdd(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
bytesRead = charsRead * BytesPerWChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(bytesRead == 0, "bytesRead == 0");
|
||||
}
|
||||
|
||||
} while (bytesRead == 0);
|
||||
|
||||
Debug.WriteLine("Read {0} character(s)", charsRead);
|
||||
|
||||
ret = Win32Error.ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
var errCode = ret;
|
||||
if (Win32Error.ERROR_SUCCESS != errCode)
|
||||
throw NativeMethods.GetExceptionForWin32Error(errCode);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException("Write operations not implemented.");
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException("Flush/Write not supported.");
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException("Seek not supported.");
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException("Seek not supported.");
|
||||
}
|
||||
|
||||
private void ValidateRead(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
if (offset < 0 || count < 0)
|
||||
throw new ArgumentOutOfRangeException(offset < 0 ? nameof(offset) : nameof(count),
|
||||
"offset or count cannot be negative numbers.");
|
||||
if (buffer.Length - offset < count)
|
||||
throw new ArgumentException("invalid offset length.");
|
||||
|
||||
if (!CanRead) throw new NotSupportedException("Get read not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<RootNamespace>Samples.Terminal</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="2.0.22" />
|
||||
<PackageReference Include="Vanara.PInvoke.Kernel32" Version="2.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28803.156
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReadConsoleInputStreamDemo", "ReadConsoleInputStreamDemo.csproj", "{62F500DE-4F06-4B46-B7AF-02AF21296F00}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{62F500DE-4F06-4B46-B7AF-02AF21296F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62F500DE-4F06-4B46-B7AF-02AF21296F00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62F500DE-4F06-4B46-B7AF-02AF21296F00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62F500DE-4F06-4B46-B7AF-02AF21296F00}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {55A9793B-D717-4A6E-A8FE-ABC6CD3B17BA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,32 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<InstrumentationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Instrumentation>
|
||||
<Regions>
|
||||
<Instrumentation>
|
||||
<Regions>
|
||||
<!-- The RegionRoot and Region GUID are made up. Make some up. -->
|
||||
<RegionRoot Guid="{ff1020e3-3f3a-3656-44a0-096b0b9b575a}" Name="TAEF">
|
||||
<Region Guid="{e1f18b03-d49c-6f10-3753-2dc36fa663f1}" Name="TestRun">
|
||||
<Start>
|
||||
<RegionRoot Guid="{ff1020e3-3f3a-3656-44a0-096b0b9b575a}" Name="TAEF">
|
||||
<Region Guid="{e1f18b03-d49c-6f10-3753-2dc36fa663f1}" Name="TestRun">
|
||||
<Start>
|
||||
<!-- This GUID corresponds to one of the TAEF providers that will send this start/stop event pair. -->
|
||||
<Event Provider="{70d27130-f2f3-4365-b790-d31223254ef4}" Name="RunTest_TestRunScope" Opcode="1"/>
|
||||
</Start>
|
||||
<Stop>
|
||||
<Event Provider="{70d27130-f2f3-4365-b790-d31223254ef4}" Name="RunTest_TestRunScope" Opcode="2"/>
|
||||
</Stop>
|
||||
<Match>
|
||||
<Event>
|
||||
<Payload FieldName="ScopeName"/>
|
||||
</Event>
|
||||
</Match>
|
||||
<Naming>
|
||||
<PayloadBased NameField="ScopeName"/>
|
||||
</Naming>
|
||||
<Event Provider="{70d27130-f2f3-4365-b790-d31223254ef4}" Name="RunTest_TestRunScope" Opcode="1"/>
|
||||
</Start>
|
||||
<Stop>
|
||||
<Event Provider="{70d27130-f2f3-4365-b790-d31223254ef4}" Name="RunTest_TestRunScope" Opcode="2"/>
|
||||
</Stop>
|
||||
<Match>
|
||||
<Event>
|
||||
<Payload FieldName="ScopeName"/>
|
||||
</Event>
|
||||
</Match>
|
||||
<Naming>
|
||||
<PayloadBased NameField="ScopeName"/>
|
||||
</Naming>
|
||||
<Metadata>
|
||||
<!-- At a bare minimum, you must collect something to get the wall time measurement. -->
|
||||
<!-- The WinPerf team recommended collecting CPU if you don't care about anything else. -->
|
||||
<WinperfWPAPreset.1>CPU</WinperfWPAPreset.1>
|
||||
<WinperfWPAPreset.1.ProcessName>te.processhost.exe</WinperfWPAPreset.1.ProcessName>
|
||||
</Metadata>
|
||||
</Region>
|
||||
</Region>
|
||||
<!-- Again, this GUID is just straight up made up. -->
|
||||
<!-- Also, just keep making regions. There's also a provision to stack a region inside a region. -->
|
||||
<!-- Go look up "Region of Interest" files in respect to WPA to learn more. -->
|
||||
@@ -56,9 +56,9 @@
|
||||
<!-- You must also go change the .wprp to COLLECT the relevant data in the first place. -->
|
||||
<WinperfWPAPreset.2>Commit</WinperfWPAPreset.2>
|
||||
<WinperfWPAPreset.2.ProcessName>conhost.exe;openconsole.exe</WinperfWPAPreset.2.ProcessName>
|
||||
</Metadata>
|
||||
</Metadata>
|
||||
</Region>
|
||||
</RegionRoot>
|
||||
</Regions>
|
||||
</Instrumentation>
|
||||
</RegionRoot>
|
||||
</Regions>
|
||||
</Instrumentation>
|
||||
</InstrumentationManifest>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<WindowsPerformanceRecorder Version="1.0" Comments="Test" Company="Microsoft Corporation" Copyright="Microsoft Corporation">
|
||||
<Profiles>
|
||||
<SystemCollector Id="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
|
||||
<BufferSize Value="1024"/>
|
||||
<Buffers Value="100"/>
|
||||
</SystemCollector>
|
||||
<Profiles>
|
||||
<SystemCollector Id="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
|
||||
<BufferSize Value="1024"/>
|
||||
<Buffers Value="100"/>
|
||||
</SystemCollector>
|
||||
<!-- Leave this system provider alone. It's for environmental statistics by the plugin only -->
|
||||
<SystemProvider Id="SystemProvider_Winperf_Environment_Light">
|
||||
<Keywords Operation="Add">
|
||||
@@ -11,21 +11,21 @@
|
||||
</Keywords>
|
||||
</SystemProvider>
|
||||
<!-- This one is our system provider to customize as we see fit. -->
|
||||
<SystemProvider Id="SystemProvider_ConsolePerf_Verbose" Base="SystemProvider_Base">
|
||||
<Keywords Operation="Add">
|
||||
<SystemProvider Id="SystemProvider_ConsolePerf_Verbose" Base="SystemProvider_Base">
|
||||
<Keywords Operation="Add">
|
||||
<!-- Add additional kernel system collection here if we decide we need it for the regions of interest file -->
|
||||
<Keyword Value="CSwitch"/>
|
||||
<Keyword Value="CSwitch"/>
|
||||
<Keyword Value="MemoryInfo"/>
|
||||
<Keyword Value="MemoryInfoWS"/>
|
||||
<Keyword Value="VirtualAllocation"/>
|
||||
</Keywords>
|
||||
</SystemProvider>
|
||||
</Keywords>
|
||||
</SystemProvider>
|
||||
<!-- Then add all the event providers we might possibly need. The first batch up here is boilerplate ones -->
|
||||
<EventProvider Id="EventProvider-Microsoft.OSGENG.Testing.TaefEngine" Name="70d27130-f2f3-4365-b790-d31223254ef4"/>
|
||||
<EventProvider Id="EventProvider-Microsoft-Windows-TestExecution" Name="Microsoft-Windows-TestExecution"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.TestExecution.WexLogger" Name="40c4df8b-00a9-5159-62bc-9bbc5ee78a29"/>
|
||||
<EventProvider Id="EventProvider-Microsoft-Windows-WinPerf" Name="7dba59f2-a1ff-40f0-b9f1-34b7531e9580"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.WinPerf" Name="BE6F04EA-3488-4543-8082-24843EAEC303"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.OSGENG.Testing.TaefEngine" Name="70d27130-f2f3-4365-b790-d31223254ef4"/>
|
||||
<EventProvider Id="EventProvider-Microsoft-Windows-TestExecution" Name="Microsoft-Windows-TestExecution"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.TestExecution.WexLogger" Name="40c4df8b-00a9-5159-62bc-9bbc5ee78a29"/>
|
||||
<EventProvider Id="EventProvider-Microsoft-Windows-WinPerf" Name="7dba59f2-a1ff-40f0-b9f1-34b7531e9580"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.WinPerf" Name="BE6F04EA-3488-4543-8082-24843EAEC303"/>
|
||||
<EventProvider Id="EventProvider-Census" Name="262CDE7A-5C84-46CF-9420-94963791EF69"/>
|
||||
<!-- These providers are relevant to specific Regions of Interest -->
|
||||
<EventProvider Id="EventProvider-Microsoft-Windows-Kernel-Memory" Name="Microsoft-Windows-Kernel-Memory"/>
|
||||
@@ -45,31 +45,31 @@
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
|
||||
<!-- Now define some profiles. We'll call them by ID when collecting. Also, the Base is where it is inheriting from and is a .wprpi file built... -->
|
||||
<!-- ... into WPR automatically. Go look in the WPR install directory or in the documentation to find it. -->
|
||||
<Profile Id="ConsolePerf.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
|
||||
<Collectors Operation="Add">
|
||||
<SystemCollectorId Value="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
|
||||
<Profile Id="ConsolePerf.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
|
||||
<Collectors Operation="Add">
|
||||
<SystemCollectorId Value="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
|
||||
<!-- You don't have to change anything here when system keywords are added above. -->
|
||||
<!-- Kernel collection is a bit more unified than user collection of events. -->
|
||||
<SystemProviderId Value="SystemProvider_ConsolePerf_Verbose"/>
|
||||
</SystemCollectorId>
|
||||
<EventCollectorId Value="EventCollector_WPREventCollectorInFile">
|
||||
<EventProviders Operation="Add">
|
||||
<SystemProviderId Value="SystemProvider_ConsolePerf_Verbose"/>
|
||||
</SystemCollectorId>
|
||||
<EventCollectorId Value="EventCollector_WPREventCollectorInFile">
|
||||
<EventProviders Operation="Add">
|
||||
<!-- Anything we need to collect for our perf test has to be referenced again here. -->
|
||||
<EventProviderId Value="EventProvider-Microsoft.OSGENG.Testing.TaefEngine"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft-Windows-TestExecution"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.TestExecution.WexLogger"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft-Windows-WinPerf"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.WinPerf"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.OSGENG.Testing.TaefEngine"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft-Windows-TestExecution"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.TestExecution.WexLogger"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft-Windows-WinPerf"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.WinPerf"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft-Windows-Kernel-Memory"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Launcher"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Host"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser"/>
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Render.VtEngine"/>
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
</Profile>
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
</Profile>
|
||||
<!-- Leave everything below here alone. They're needed for WinPerf to collect environmental census information -->
|
||||
<Profile Id="WinPerfEnvironment.Light.File" Base="" LoggingMode="File" Name="WinPerfEnvironment" DetailLevel="Light" Description="WinPerf environment profile">
|
||||
<Collectors Operation="Add">
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="Console Rules" Description="These rules enforce static analysis on console code." ToolsVersion="15.0">
|
||||
|
||||
<Include Path="cppcorecheckrules.ruleset" Action="Default" />
|
||||
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
|
||||
<Rule Id="C6001" Action="Error" />
|
||||
<Rule Id="C6011" Action="Error" />
|
||||
</Rules>
|
||||
|
||||
</RuleSet>
|
||||
|
||||
@@ -25,7 +25,7 @@ Author:
|
||||
|
||||
class InvalidCharInfoConversionException : public std::exception
|
||||
{
|
||||
const char* what() noexcept
|
||||
const char* what() const noexcept
|
||||
{
|
||||
return "Cannot convert to CHAR_INFO without explicit TextAttribute";
|
||||
}
|
||||
|
||||
@@ -238,8 +238,8 @@ void TextAttribute::SetDefaultBackground() noexcept
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if this attribute indicates it's foreground is the "default"
|
||||
// foreground. It's _rgbForeground will contain the actual value of the
|
||||
// - Returns true if this attribute indicates its foreground is the "default"
|
||||
// foreground. Its _rgbForeground will contain the actual value of the
|
||||
// default foreground. If the default colors are ever changed, this method
|
||||
// should be used to identify attributes with the default fg value, and
|
||||
// update them accordingly.
|
||||
@@ -253,8 +253,8 @@ bool TextAttribute::ForegroundIsDefault() const noexcept
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if this attribute indicates it's background is the "default"
|
||||
// background. It's _rgbBackground will contain the actual value of the
|
||||
// - Returns true if this attribute indicates its background is the "default"
|
||||
// background. Its _rgbBackground will contain the actual value of the
|
||||
// default background. If the default colors are ever changed, this method
|
||||
// should be used to identify attributes with the default bg value, and
|
||||
// update them accordingly.
|
||||
|
||||
@@ -32,7 +32,7 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - erases key and it's associated data from the storage
|
||||
// - erases key and its associated data from the storage
|
||||
// Arguments:
|
||||
// - key - the key to remove
|
||||
void UnicodeStorage::Erase(const key_type key) noexcept
|
||||
|
||||
@@ -21,7 +21,6 @@ Abstract:
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: ALL_CPPCORECHECK_WARNINGS)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#endif
|
||||
|
||||
@@ -1024,9 +1024,9 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
|
||||
}
|
||||
}
|
||||
|
||||
data.text.emplace_back(selectionText);
|
||||
data.FgAttr.emplace_back(selectionFgAttr);
|
||||
data.BkAttr.emplace_back(selectionBkAttr);
|
||||
data.text.emplace_back(std::move(selectionText));
|
||||
data.FgAttr.emplace_back(std::move(selectionFgAttr));
|
||||
data.BkAttr.emplace_back(std::move(selectionBkAttr));
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<ProjectGuid>{531C23E7-4B76-4C08-8BBD-04164CB628C9}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TextBufferUnitTests</RootNamespace>
|
||||
<ProjectName>TextBuffer.UnitTests</ProjectName>
|
||||
<TargetName>TextBuffer.UnitTests</TargetName>
|
||||
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
|
||||
<TargetName>TextBuffer.Unit.Tests</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
@@ -44,7 +44,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(WindowsTerminalReleaseBuild)'=='true'">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<AppxManifest Include="Package-Dev.appxmanifest" Condition="'$(WindowsTerminalReleaseBuild)'!='true'">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
@@ -96,6 +99,14 @@
|
||||
<Content Include="Images\Wide310x150Logo.scale-150.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-400.png" />
|
||||
<Content Include="ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-200.png" />
|
||||
<PRIResource Include="Resources\en-US\Resources.resw" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user