Repository: microsoft/terminal (language: cpp)

Issue:
Terminal crashes on `wt .`
### Windows Terminal version

1.12.10393.0

### Windows build number

19043.1469

### Other Software

- Command Prompt
- Windows Explorer
![screenshot](https://user-images.githubusercontent.com/73048226/154818813-a4be2ab0-e3ab-48c7-b7ed-1bf98a84acac.png)


### Steps to reproduce

1. Open Windows Terminal.
2. Open Command Prompt using a non-Windows Terminal context.
3. Execute `wt .` in Command Prompt.
    (May also happen with Git Bash)

### Expected Behavior

I was expecting to have Windows Terminal open in the current directory.

### Actual Behavior

Windows Terminal crashed and I lost about 30 minutes of work.


Discussion / hints:
Just got another error.
```
[error 2147942402 (0x80070002) when launching `%cd%']
```
> I was expecting to have Windows Terminal open in the current directory.

```
wt --startingDirectory .
```
Yes, the argument after `wt` is a command to run (as in, `wt powershell`.)

You need to use `wt -d xxx` to set the starting directory. 
> Yes, the argument after `wt` is a command to run (as in, `wt powershell`.)
> 
> You need to use `wt -d xxx` to set the starting directory.

If that is the case, then what about programs that use Windows Terminal without making sure the target binary exists?
It should always display an error (as you noted in your second comment) in a new terminal tab/window/pane. If it crashes, that is a bug we should fix.

Fix patch:
diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
index 4f1ce422c14..ad2463bbb70 100644
--- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp
@@ -85,7 +85,8 @@ namespace SettingsModelLocalTests
         for (int i = 0; i < expectedArgc; ++i)
         {
             const bool useQuotes = static_cast<bool>(rng(2));
-            const auto count = static_cast<size_t>(rng(64));
+            // We need to ensure there is at least one character
+            const auto count = static_cast<size_t>(rng(64) + 1);
             const auto ch = static_cast<wchar_t>(rng('z' - 'a' + 1) + 'a');
 
             if (i != 0)
@@ -107,6 +108,7 @@ namespace SettingsModelLocalTests
                 input.push_back(L'"');
             }
         }
+        Log::Comment(NoThrowString().Format(input.c_str()));
 
         int argc;
         wil::unique_hlocal_ptr<PWSTR[]> argv{ ::CommandLineToArgvW(input.c_str(), &argc) };
@@ -168,10 +170,18 @@ namespace SettingsModelLocalTests
         touch(file1);
         touch(file2);
 
-        const auto commandLine = file2.native() + LR"( -foo "bar1 bar2" -baz)"s;
-        const auto expected = file2.native() + L"\0-foo\0bar1 bar2\0-baz"s;
-        const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine.c_str());
-        VERIFY_ARE_EQUAL(expected, actual);
+        {
+            const auto commandLine = file2.native() + LR"( -foo "bar1 bar2" -baz)"s;
+            const auto expected = file2.native() + L"\0-foo\0bar1 bar2\0-baz"s;
+            const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine.c_str());
+            VERIFY_ARE_EQUAL(expected, actual);
+        }
+        {
+            const auto commandLine = L"C:\\";
+            const auto expected = L"C:\\";
+            const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine);
+            VERIFY_ARE_EQUAL(expected, actual);
+        }
     }
 
     void TerminalSettingsTests::GetProfileForArgsWithCommandline()
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
index 4935cee4b9c..b3f49688a26 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
@@ -744,12 +744,17 @@ std::wstring CascadiaSettings::NormalizeCommandLine(LPCWSTR commandLine)
                 break;
             }
         }
+        // All other error types aren't handled at the moment.
+        else if (status != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+        {
+            break;
+        }
         // If the file path couldn't be found by SearchPathW this could be the result of us being given a commandLine
-        // like "C:\foo bar\baz.exe -arg" which is resolved to the argv array {"C:\foo", "bar\baz.exe", "-arg"}.
+        // like "C:\foo bar\baz.exe -arg" which is resolved to the argv array {"C:\foo", "bar\baz.exe", "-arg"},
+        // or we were erroneously given a directory to execute (e.g. someone ran `wt .`).
         // Just like CreateProcessW() we thus try to concatenate arguments until we successfully resolve a valid path.
         // Of course we can only do that if we have at least 2 remaining arguments in argv.
-        // All other error types aren't handled at the moment.
-        else if ((argc - startOfArguments) < 2 || status != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+        if ((argc - startOfArguments) < 2)
         {
             break;
         }


Test patch: