Aldaviva / Toca3DifficultyEditor

🏎️ Change the difficulty of all AI drivers in ToCA Race Driver 3
Apache License 2.0
0 stars 0 forks source link

Work around broken .NET argv splitting #1

Open Aldaviva opened 3 months ago

Aldaviva commented 3 months ago

When running a program, you may pass the path to a directory in an argument. On Windows, PowerShell tab-completes and quotes directories. For example, if you type

.\MyProgram.exe --dir c:\progr

and then press Tab, PowerShell will complete this to

.\MyProgram.exe --dir 'C:\Program Files\'

When you press Enter, PowerShell will start a program with the executable and arguments

MyProgram.exe --dir "C:\Program Files\"

So far, PowerShell and Windows are not doing anything incorrect. The command line arguments passed to MyProgram.exe will be correct as viewed in Task Manager, Process Explorer, Process Monitor. If the program to launch is a .NET program written in C#, Environment.GetCommandLineArgs() and the string[] parameter to Main will contain the last item C:\Program Files", with an extra trailing quotation mark, which is incorrect. If you try to open or check if this directory exists, it fails, because the directory name is misspelled. The original trailing quotation mark was closing the opening one, which is now correctly gone. However, the trailing one was incorrectly interpreted as an escaped quotation mark because PowerShell preceded it with \ as a directory separator, in case you wanted to type more path segments easily without typing \ yourself. This is also trivially easy to do in Command Prompt as well, although PowerShell is more likely to do so because of its tab completion behavior. .NET then finds it has an unmatched quotation mark pair and terminates the string anyway, leaving the trailing quotation mark in the string.

I think in the past I have worked around this issue with regular expressions, but I would like a more generalized and bulletproof solution, because this keeps happening and creating more duplicate, unnecessary work for me in each program I write.

Aldaviva commented 3 months ago

This seems to also have affected RetrospectClientNotifier, and I fixed it with string replacement before parsing.

Aldaviva commented 3 months ago

This seems like a CoreCLR runtime VM defect and not a BCL defect because the issue is present in the char** argv argument passed from the VM to Environment.InitializeCommandLineArgs(char*,int,char**):

new string(argv[15]) // G:\Toca Race Driver 3"
// note the erroneous trailing quotation mark

Could the CLR be adamantly doing the right but dumb thing with the quotation mark escaping, and PowerShell is emitting pretty but illegal strings during directory tab completion?

Aldaviva commented 3 months ago

The same problem also reproduces with a minimal C++ program that just reads the char* argv[] value passed into main, so this problem is not in the .NET VM or BCL. I think Microsoft shot themselves in the foot with their stupid Windows argument escaping rules, and PowerShell is technically doing the wrong thing according to those dumb rules.

Aldaviva commented 3 months ago

I guess preprocessing the array in process is best. An extension method that takes the place of McMaster.Extensions.CommandLineUtils.CommandLineApplication.Parse works well and is fairly portable.