Repository: microsoft/terminal (language: cpp)

Issue:
Can't bypass DOSkey macro with leading space
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

<!--
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.

If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.

Please use this form and describe your issue, concisely but precisely, with as much detail as possible.

-->

# Environment

```none
Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd]
Windows Terminal version (if applicable):
Microsoft Windows [Version 10.0.18362.535]
Any other software? A DosKEY macro
```

# Steps to reproduce

<!-- A description of how to trigger this bug. -->
Create a DOSkey macro, e.g., 
`doskey dir=dir /b`
Run the command,
`dir`
then run the command (note _space in front_)
` dir`

# Expected behavior

<!-- A description of what you're expecting, possibly containing screenshots or reference material. -->
In the 2nd run, you should get the default "dir" listing. This is the case in classic console.

# Actual behavior

<!-- What's actually happening? -->
You get the results of "dir /b" again!


Discussion / hints:
The ' dir' does not work in my default console (CMD), it will still use the alias?
> The ' dir' does not work in my default console (CMD), it will still use the alias?

Sorry, but are you a bot? If not, are you on the Experimental Console programming team trying to replicate my reported bug? The dir command is probably the most-used command in console operation, so how could you be without one?
It's not counter-intuitive that matching a [console alias](https://docs.microsoft.com/en-us/windows/console/console-aliases) would trim leading spaces. However, the [doskey](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490894(v=technet.10)) docs do state plainly that the user should "type the macro name starting at the *first* position on the command line". Moreover, they include the following instructions for creating a macro with same name as a command:

> If you always use a particular command with specific command-line options, you can create a macro that has the same name as the command. To specify whether you want to run the macro or the command, follow these guidelines:
> 
>    - To run the macro, begin typing the macro name immediately after the command prompt, with no space between the prompt and the command name.
> 
>   - To run the command, insert one or more white spaces between the command prompt and the command name.


What I mean to say is that this bug report is claiming that running ' dir', after defining an alias for dir, should output the normal behavior of what is expected in CMD.

But when I tested this outside WT, I did not see this behavior. The macro is always being run, and I do not have a way to run DIR with other options than the one given in the alias.

I am running the same version of Windows, Microsoft Windows [Version 10.0.18363.535].

@luuk34: ah, thanks for the clarification. But eryksun has found the chapter/verse of how it should work according to the docs - thanks! - and what it's doing is definitely NOT according to the docs.

Can't speak to your situation/configuration. I've replicated it on two different machines. You do need to completely exit, then restart, your console session after unchecking/checking "use legacy console" to make the changes stick.

Also: 
> The macro is always being run, and I do not have a way to run DIR with other options than the one given in the alias.

Note that my original post says the way around it is to _put a space_ before the "dir" command. That's the canonical method of bypassing a DOSkey macro with the same name as a nother command.
It looks like this should be easy to fix. I can get it working in a local build just by deleting this line:

https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/alias.cpp#L1154-L1155

But I'm curious why that trimming routine was added in the first place if it's not compatible with the legacy console. Someone from the core team will probably need to confirm whether that was a mistake and it's safe to remove.
> Author: Michael Niksa
> Date:   Fri Apr 27 20:38:26 2018 +0000
>
>    Merged PR 1747128: Make interactive mode command line trim leading spaces from aliases to improve usability
>
>    Per MSFT:16672211 trim spaces from alias lookups since they never resolved to anything anyway. Makes aliases a touch
more useful at the cooked command line.
>
>    Paul noticed this while I was doing the alias improvement code and suggested the enhancement.
>
>    Related work items: MSFT:16672211

Looks like our usability improvement was a usability detriment. :smile:
Hi, it's been 2+ years and now I note this issue is still present in Windows 11! Looking at the status, I see it continues to be pushed out, and the last milestone Feb. 2 is past. Can we ensure this gets put in this time? The fix can't be that hard - don't left trim the command :-)
@cniggeler this is currently slated for the current milestone, but is fairly low priority relative to some of the other bugs in the milestone. I do believe this is a fairly easy-starter issue if you'd like to try submitting a fix yourself 😉 
Uh, OK I'll think about it. Windows being a for-profit product I'm surprised at the suggestion of a user-contributed, free solution ;-)
@zadjii-msft I just found that you are in charge of this project - congrats! That being the case, what is your strategy for handling bugs? This IS a functional bug - I'm not asking about font rendering or cursor blink rate. When does the fact that this is a bug, that it's been aging two years, and that it's probably a one-line change (since you or your team would know where the line is and I don't) factor into your decision to just fix this and close it?

Lol I wish I was in charge, I just happen to be the person who drives triage. 

We handle bugs more or less based off of severity, but the process is pretty arbitrary. There's plenty of bugs on the repo and our little team just doesn't have time to get to them all.  This is probably an easy fix, sure, but the impact is pretty low so it's just never really bubbled up in priority. I'd guess that there's not a super large population of users that are actually using the built-in aliases functionality. If there were more folks hitting this, then it'd be more important to fix. I'd bet that the line of code identified in https://github.com/microsoft/terminal/issues/4189#issuecomment-573447367 is the one that's probably responsible. We just haven't had the time to do the diligence to confirm. 

@zadjii-msft, Hee! Got that view from article titled, ["Guy in charge of it, Mike Griese, Software Engineer at Microsoft, writing on Hacker News"](https://news.ycombinator.com/item?id=15905409) 

Anyway, I could at least take out the trim and try to run it, then provide you feedback. It is true that just removing the trim may not fix the issue. Would appreciate your reply on the following:

1. Is there a makefile so I can just compile from Windows command line? I don't want to waste half a day loading it into Visual Studio and learning all the ins and outs of the code. I just wanna compile/run. (I guess you can see why a working console is important to me :-) )
2. Where does a new terminal go once built, and how is it invoked? I have a shortcut to cmd.exe and it gives you a choice to use the legacy console or not.
Well, I'm flattered, but in 2017 I was decidedly less senior than I am even now. Fun to think people on HN thought I was "in charge of {the terminal}" for the last 5 years 😝 


[building.md](https://github.com/microsoft/terminal/blob/main/doc/building.md) is the definitive guide, but if you're using CMD then the easiest way to build conhost would be:

```cmd
git clone https://github.com/microsoft/terminal.git
cd terminal
git submodule update --init --recursive
.\tools\razzle.cmd
nuget restore OpenConsole.sln

@rem at this step you may need to open VS to get it to pull down dependencies, the SDK, etc.
@rem start OpenConsole.sln

@rem To build just conhost, switch to this directory and run `bx`
cd src\host\exe
bx

@rem this will launch the conhost you just built
opencon
```
You shouldn't need to build the whole terminal for this bug, just building `conhost` (`openconsole.exe`) should be enough to test this fix. The Terminal takes _quite a bit longer_ to build. 

The single line change to alias.cpp as specified in [the above comment](https://github.com/microsoft/terminal/issues/4189#issuecomment-573447367) seems to have done the trick! I will do some further testing, but both my issues - a leading space to prevent invoking DOSkey macro, and case sensitivity from history (separately fixed) - appear to be working fine! 

<prattle>OK, didn't I say I didn't want to waste 1/2 a day in Visual Studio? Well, I did :-) Things are very brittle if you have >1 Visual Studio installed, and it took me a while to get the environment set correctly (hint: rerun razzle.cmd once you get 2019 vs. 2017 set as the default build environment, which I do in a batch file on startup).</prattle>

Anyway, thanks for the pointers, hope this confirmation helps!
@zadjii-msft How do I get the built app to run on a different machine? I studied opencon.cmd and tried the following, but I do not get anything: no messages, no new window, and nothing in tasklist:

1. two of the files, VtPipeTerm.exe and Nihilist.exe do not exist (apparently were not built) but that does not affect running the app on the build machine
2. copied over openconsole.exe
3. copied it again and renamed as conhost.exe 
4. copied over conhost.dll
5. added vcruntime140_1d.dll which doesn't exist on the other machine and got an error dialog trying to start openconsole without it.
Update: I've also done the following:
- checked all the DLLs as reported by listdlls and the file size and date of each DLL are identical.
- the same for console.dll, which was created at the same time as openconsole.exe
- just in case, copied all the .lib files from the build directory to the temp directory holding openconsole. No dice.
- Started openconsole.exe in windbg and the failure is at ntdll.exe, as shown in the attached jpeg.
![opencon_abort](https://user-images.githubusercontent.com/16118897/160446967-f7ba1ff2-ee47-4b63-9277-a654fd7dbf9f.jpg)

_Can anyone help a guy out here? Thanks!_ 
Sorry, wasn't checking mail last week. Copying openconsole to another machine _should_ work all by itself. It should be able to be doubleclicked. Did you build it as Release or Debug? (if you just ran `bx`, you probably got Debug. `bx rel` will build the Release version).

> 1. two of the files, VtPipeTerm.exe and Nihilist.exe do not exist (apparently were not built) but that does not affect running the app on the build machine

That's fine, those are just test binaries 

I'm pretty sure the only thing that's needed is openconsole.exe, and the right version of the c runtime. `console.dll` is only needed for changes to the property sheet. 

The C Runtime is required though. Pretty sure VS installs that for you with the rest of the tools, but you should be able to get it from [here](https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-160) manually.
QUICK COMMENT: Sorry about formatting, tried to copy/paste info into code tags.

Still no dice :-(  bx rel built fine with one warning (spacing is straight copy from console):
`       "c:\devel\terminal\OpenConsole.sln" (Conhost\Host_EXE target) (1) ->
       "c:\devel\terminal\src\host\exe\Host.EXE.vcxproj.metaproj" (default targ
       et) (2) ->
       "c:\devel\terminal\src\host\proxy\Host.Proxy.vcxproj" (default target) (
       3) ->
       (Link target) ->
         LINK : warning LNK4266: missing load config symbol for image built wit
       h /GUARD [c:\devel\terminal\src\host\proxy\Host.Proxy.vcxproj]

    1 Warning(s)
    0 Error(s)`

The C runtimes are a common requirement, the version on my laptop was older, but installing the latest did not help. In comparing what was installed in Programs and Features, I found some discrepancies - perhaps .NET framework or its SDK is required? Or what about the version of the Windows SDK? Here are the diffs '<' is build system, '>' is test system:
`< Microsoft Visual C++ 2015-2019 Redistributable (x64) - 14.29.30139
> older version:                                         14.26.28720
> updated to latest:                                     14.31.31103

< Microsoft .NET Framework 4.8 SDK
<  Targeting Pack
<  Targeting Pack (ENU)
> None!
< Microsoft .NET SDK 5.0.406 (x64) from Visual Studio
> None!

< Windows Software Development Kit - Windows 10.0.14393.33 to 10.0.22000.194
>                                            10.0.17134.12 to 10.0.17763.132

< CLR types for SQL server 2019  CTP2.2
> CLR types for SQL server vNext CTP 1.6                          
< Sync Framework 2.0 Core and Provider
> Same`

Oh that might do it. Hard to exactly parse that, but `10.0.17134.12` is _quite a bit_ out of date at this point. I would not be surprised at all if the current conhost builds don't run on that version of windows 10 anymore. 
Huh, this is pretty curious.

Would you be able to run it under the debugger after enabling _loader snaps_?

* Windbg comes with a tool called "Global Flags" (gflags) - launch it, navigate to _Image File_, and enter `OpenConsole.exe`
* Enable "Show loader snaps"
   * <img width="376" alt="image" src="https://user-images.githubusercontent.com/189190/160467604-dd264972-1324-4158-b5b7-4945ef930344.png">
* [Apply]

When you run OpenConsole under the debugger, it should emit a terrible volume of text about library loading, symbol binding, etc. that might help figure out why it won't load on this machine. :smile:
> Oh that might do it. Hard to exactly parse that, but `10.0.17134.12` is _quite a bit_ out of date at this point. I would not be surprised at all if the current conhost builds don't run on that version of windows 10 anymore.

_Hmm_. Quite possible... but to my knowledge we haven't made any changes that would exclude running on that version.

In addition, I think @cniggeler was only stating that they have that SDK installed (not that they're running on that version of Windows!)
I'm running Windows 10 Pro 21H1. Microsoft Windows [Version 10.0.19044.1586]. @DHowett  is correct that that was the version for the SDK. 

I will run openconsole through windbg with loader snaps and reply next.
@DHowett I loaded gflags and checked loader snaps as requested. I pressed Launch, which had no visual feedback, so I returned to Windbg. There is now 2155 lines of info, which I've attached as a text file.
[opencon_snap.txt](https://github.com/microsoft/terminal/files/8365721/opencon_snap.txt)

I have VS2017 loaded on the test machine. Would it be possible/ advisable to build with debug info on the VS2019 build machine and then open the debug .exe in VS2017 on the test machine?
It looks like everything we need is loading; any libraries that can't be found (Status `c0000135`) later get picked up from the system directory.

Can you generate a backtrace when you get here? I wonder if we're exiting in an orderly fashion for some reason...

<img width="299" alt="image" src="https://user-images.githubusercontent.com/189190/160473869-386340cc-cd40-4770-9fd1-347fc55c74e0.png">

A debug build might help. You'll have to copy over the debug CRT (from the `VC\` directory in your VS 2019 install!)
I'm assuming you meant View - Call stack:
![opencon_bt](https://user-images.githubusercontent.com/16118897/160475365-a1600b3c-87ee-4983-9b75-8e276fa7eddd.jpg)

Also, here's the output after selecting GO, which adds another 1050 lines of output:
[opencon_go.txt](https://github.com/microsoft/terminal/files/8365812/opencon_go.txt)


Indeed!

It looks like the console is choosing to exit at `+0xdb7a4`. If you copy the pdb for OpenConsole from your build machine to the run you're running on, you'll get a bit more info. It could just be returning from main after failing to do something, admittedly... but nothing in that loader snap log looks wrong.
I think this took. I kept getting message WARNING: Non-directory path: 'C:\temp\OpenConsole.pdb' even after using "Browse" in windbg. Anyway, slightly larger file attached:
[opencon_w_pdb.txt](https://github.com/microsoft/terminal/files/8365971/opencon_w_pdb.txt)


And what's the call stack at termination for that build? :)
Has a function name in it :-)

![opencon_bt2](https://user-images.githubusercontent.com/16118897/160480364-7c505d9f-ddf2-48f5-bd2a-b944a61f21b1.jpg)

Update: I also tried this on a brand new Win11 machine. Installed the latest C runtime and copied the release version of OpenConsole.exe to it. It also does not run.
You know, I'm still stumped by this. Sorry to leave you on read.

Here's what I'll say: You can send out a pull request if you'd like, without further validation. The CI will take care of producing a runnable copy of this kit and kaboodle and then _actually run it_ on your behalf :smile:

To be fair, trying to validate something on multiple machines is farther than I suspect most of the team usually goes :grin:
Thanks. At this stage I was more interested in why the build wasn't able to run on different machines, because maybe that would point out some corrective action in the build scripts or execution prerequisites. But I would love to have it on my main machine where I work every day - been waiting 2.25 years for it!

The change was literally the [one line change](https://github.com/microsoft/terminal/issues/4189#issuecomment-573447367) above, and a pull request seems heavyweight. Is there an alternative to just add it to the branch for the next scheduled release? My build/test has managed to show it works ;-)

Fix patch:
diff --git a/src/host/alias.cpp b/src/host/alias.cpp
index 9032630755b..b7c885bc9f4 100644
--- a/src/host/alias.cpp
+++ b/src/host/alias.cpp
@@ -817,18 +817,6 @@ void Alias::s_ClearCmdExeAliases()
     CATCH_RETURN();
 }
 
-// Routine Description:
-// - Trims leading spaces off of a string
-// Arguments:
-// - str - String to trim
-void Alias::s_TrimLeadingSpaces(std::wstring& str)
-{
-    // Erase from the beginning of the string up until the first
-    // character found that is not a space.
-    str.erase(str.begin(),
-              std::find_if(str.begin(), str.end(), [](wchar_t ch) { return !std::iswspace(ch); }));
-}
-
 // Routine Description:
 // - Trims trailing \r\n off of a string
 // Arguments:
@@ -1148,9 +1136,6 @@ std::wstring Alias::s_MatchAndCopyAlias(const std::wstring& sourceText,
     // Trim trailing \r\n off of sourceCopy if it has one.
     s_TrimTrailingCrLf(sourceCopy);
 
-    // Trim leading spaces off of sourceCopy if it has any.
-    s_TrimLeadingSpaces(sourceCopy);
-
     // Check if we have an EXE in the list that matches the request first.
     auto exeIter = g_aliasData.find(exeName);
     if (exeIter == g_aliasData.end())
diff --git a/src/host/alias.h b/src/host/alias.h
index ba0d0b27f50..1a745c601f7 100644
--- a/src/host/alias.h
+++ b/src/host/alias.h
@@ -29,7 +29,6 @@ class Alias
                                             size_t& lineCount);
 
 private:
-    static void s_TrimLeadingSpaces(std::wstring& str);
     static void s_TrimTrailingCrLf(std::wstring& str);
     static std::deque<std::wstring> s_Tokenize(const std::wstring& str);
     static std::wstring s_GetArgString(const std::wstring& str);
diff --git a/src/host/ut_host/AliasTests.cpp b/src/host/ut_host/AliasTests.cpp
index b6326ebb2a4..6443dd22f63 100644
--- a/src/host/ut_host/AliasTests.cpp
+++ b/src/host/ut_host/AliasTests.cpp
@@ -340,10 +340,10 @@ class AliasTests
         auto rgwchTargetBefore = std::make_unique<wchar_t[]>(cchTarget);
         wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get());
         size_t cbTargetUsed = 0;
-        const auto cbTargetUsedExpected = cbTarget;
+        const auto cbTargetUsedBefore = cbTargetUsed;
 
         DWORD dwLines = 0;
-        const auto dwLinesExpected = dwLines + 1;
+        const auto dwLinesBefore = dwLines;
 
         // Register the correct alias name before we try.
         std::wstring exe(L"exe.exe");
@@ -351,9 +351,7 @@ class AliasTests
         std::wstring target(L"someTarget");
         Alias::s_TestAddAlias(exe, source, target);
 
-        auto targetExpected = target + L"\r\n";
-
-        // We should be able to match through the leading spaces. They should be stripped.
+        // Leading spaces should bypass the alias. This should not match anything.
         Alias::s_MatchAndCopyAliasLegacy(pwszSource,
                                          cbSource,
                                          rgwchTarget.get(),
@@ -362,9 +360,9 @@ class AliasTests
                                          exe,
                                          dwLines);
 
-        VERIFY_ARE_EQUAL(cbTargetUsedExpected, cbTargetUsed, L"No target bytes should be used.");
-        VERIFY_ARE_EQUAL(String(targetExpected.data(), gsl::narrow<int>(targetExpected.size())), String(rgwchTarget.get(), cchTarget), L"Target string should match expected.");
-        VERIFY_ARE_EQUAL(dwLinesExpected, dwLines, L"Line count be updated to 1.");
+        VERIFY_ARE_EQUAL(cbTargetUsedBefore, cbTargetUsed, L"No bytes should be used if nothing was found.");
+        VERIFY_ARE_EQUAL(String(rgwchTargetBefore.get(), cchTarget), String(rgwchTarget.get(), cchTarget), L"Target string should be unmodified.");
+        VERIFY_ARE_EQUAL(dwLinesBefore, dwLines, L"Line count should pass through.");
     }
 
     TEST_METHOD(TrimTrailing)


Test patch: