-
Notifications
You must be signed in to change notification settings - Fork 9.1k
Description
Environment
Windows build number: [run "ver" at a command prompt]
Windows Terminal version (if applicable):
Any other software?
Today I modified the Windows Terminal configuration file and wanted to create a Pwsh with the emoji title. However, I encountered some confusion in this process. I checked the task manager and found that conhost did not escape when the process was started, causing the command line to parse the exception.
Steps to reproduce
Expected behavior
I need to start pwsh by the following command line (note that the following command line is in cmd, or you can start pwsh normally using CreateProcessW.)
"C:\Program Files\PowerShell\7-preview\pwsh.exe" -NoExit -Command "$Host.UI.RawUI.WindowTitle=\"Windows Pwsh 💙 (7 Preview)\""
Here is the Windows Terminal configuration file (the command line has been properly escaped):
{
"startingDirectory": "C:\\Users\\CharlieInc",
"guid": "{08a0be98-ff68-4e3a-a054-0fbd3969d3bb}",
"name": "Windows Pwsh 💙 (7 Preview)",
"colorscheme": "Campbell",
"historySize": 9001,
"snapOnInput": true,
"cursorColor": "#FFFFFF",
"cursorShape": "bar",
"commandline": "\"C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe\" -NoExit -Command \"$Host.UI.RawUI.WindowTitle=\\\"Windows Pwsh 💙 (7 Preview)\\\"\"",
"fontFace": "Consolas",
"fontSize": 12,
"acrylicOpacity": 0.75,
"useAcrylic": true,
"closeOnExit": false,
"padding": "0, 0, 0, 0",
"icon": "ms-appdata:///roaming/pwsh-32.png"
}Actual behavior
As expected, Pwsh should set the title correctly, but pwsh reported the error:

Let's take a look at the command line:
When you see the red line, the command line that pwsh starts is incorrect, and when you start conhost, the command line is still correct!
Currently I have found a suspicious code, most likely this code caused an error:
terminal/src/host/ConsoleArguments.cpp
Lines 263 to 288 in 71e19cd
| HRESULT ConsoleArguments::_GetClientCommandline(_Inout_ std::vector<std::wstring>& args, const size_t index, const bool skipFirst) | |
| { | |
| auto start = args.begin()+index; | |
| // Erase the first token. | |
| // Used to get rid of the explicit commandline token "--" | |
| if (skipFirst) | |
| { | |
| // Make sure that the arg we're deleting is "--" | |
| FAIL_FAST_IF(!(CLIENT_COMMANDLINE_ARG == start->c_str())); | |
| args.erase(start); | |
| } | |
| _clientCommandline = L""; | |
| size_t j = 0; | |
| for (j = index; j < args.size(); j++) | |
| { | |
| _clientCommandline += args[j]; | |
| if (j+1 < args.size()) | |
| { | |
| _clientCommandline += L" "; | |
| } | |
| } | |
| args.erase(args.begin()+index, args.begin()+j); | |
| return S_OK; |
In this code, command line composition is simply a simple connection rather than a concatenated string. If the string contains spaces, this will result in an incorrect command line.
Below is a code for the command line escaping, which may be useful:
inline std::wstring escape_argument(std::wstring_view ac) {
if (ac.empty()) {
return L"\"\"";
}
bool hasspace = false;
auto n = ac.size();
for (auto c : ac) {
switch (c) {
case L'"':
case L'\\':
n++;
break;
case ' ':
case '\t':
hasspace = true;
break;
default:
break;
}
}
if (hasspace) {
n += 2;
}
if (n == ac.size()) {
return std::wstring(ac.data(), ac.size());
}
std::wstring buf;
if (hasspace) {
buf.push_back(L'"');
}
size_t slashes = 0;
for (auto c : ac) {
switch (c) {
case L'\\':
slashes++;
buf.push_back(L'\\');
break;
case L'"': {
for (; slashes > 0; slashes--) {
buf.push_back(L'\\');
}
buf.push_back(L'\\');
buf.push_back(c);
} break;
default:
slashes = 0;
buf.push_back(c);
break;
}
}
if (hasspace) {
for (; slashes > 0; slashes--) {
buf.push_back(L'\\');
}
buf.push_back(L'"');
}
return buf;
}
