Accessing the commandline using C# - c#

I need to access the commandline from within a C# application. I can't find a way to detect mismatched double quotes if I use the "args" parameter.
The application I'm working on the moment has an option to encrypt a string that you pass to it via the commandline.
eg.
program.exe -encrypt somestring
Results in:
EZs/6rWxvJ82+VE8unJ0Xw==
Now if the user types in this:
program.exe -encrypt somestring extra characters
it ignores everything after "somestring" (you can still access the rest of the values through the "args" parameter).
You can workaround this easily using double quotes:
program.exe -encrypt "somestring extra characters"
And of course if you want to use embedded quotes you can escape them with "\":
program.exe -encrypt "somestring \"extra\" characters"
The problem occurs when the user inputs something like this:
program.exe -encrypt "somestring extra characters
or:
program.exe -encrypt somestring ex"tra characters
The program will completely ignore the double quote, which not be what the user was expecting. I would like to detect cases like these and inform the user about the mismatched/unescaped quote, otherwise they might end up with an encrypted version of the wrong string.

This is how the command line passes arguments to your C# program. You cannot change that. You can however access the command arguments string as a whole and perform the validation yourself.
MSDN: Environment.CommandLine Property

Just do it manually: check that either you have only one argument after the -encrypt argument, and if not that the next argument starts with double quotes and the last argument ends with double quotes. If none of these happens throw a custom error to the user.
When the next argument after -encrypt starts with double quotes and the last argument ends with double quotes build the string yourself using Array.Copy or something of that kind.

Related

Passing string[] from batch file (which contains double quotes ") to powershell script

I have a powershell script which has a string[] parameter and try to pass value to this from batch file
PowerShell Script
[string[]]$ScriptArgs
Batch File
powershell -File Foobar.ps1 -ScriptArgs -versn="""1.0.0.0""",pattern="""FooBar.*"""
(3 Quotes - For escaping/showing 1 quote)
But no matter what I do Write-Host $ScriptArgs.Count prints 1
I want the variable to receive 2 elements
[0] will be -versn="1.0.0.0"
[1] will be -pattern="FooBar.*"
In batch file, I even tried setting variables individually
set #sArgs = -versn="""1.0.0.0"""
set #sArgs = %sArgs%;-pattern="""FooBar.*"""
powershell -File Foobar.ps1 -ScriptArgs %sArgs%
but the console somehow runs as if the variable is not created and run it as
powershell -File Foobar.ps1 -ScriptArgs
and throws error
Missing an argument for parameter 'ScriptArgs'. Specify a parameter of type System.String[] and try again
What should I change to achieve this?
When calling a PowerShell script from from a batch file (from cmd.exe) with -File, there is no way to directly pass an array of values to a PowerShell array parameter:
If you specify the array values without spaces, they are considered a SINGLE element; e.g.:
one,two,three becomes a SINGLE array element
If you do use spaces, the trailing commas become part of the arguments, and only the FIRST value is bound to the array parameter:
one, two, three - one, - including the trailing , - becomes the only array element, two, and three, are considered separate arguments.
The workarounds are to:
Either: use -Command to invoke the script - see bottom section.
Or, with -File, declare the parameter differently and pass arguments differently:
decorate the array parameter with attribute
[Parameter(ValueFromRemainingArguments)]
and to pass the array elements as individual, space-separated arguments, without their parameter name.
In your case:
Declare the parameter:
[Parameter(ValueFromRemainingArguments)]
[string[]] $ScriptArgs
Invoke the script with separate, space-separate arguments not preceded by the parameter name to bind them as an array to $ScriptArgs:
powershell -File Foobar.ps1 "-versn=\"1.0.0.0\"" "pattern=\"FooBar.*\""
Note the quoting:
Each argument as a whole is enclosed in "..." - this isn't strictly necessary, but guards against --prefixed values such as -version being mistaken for parameter names.
" chars. to be retained as literals are \-escaped (which contrasts with the `-escaping that must be used from within PowerShell).
The inherent limitations of this approach:
The approach doesn't support multiple array parameters, because only one can be decorated with ValueFromRemainingArguments.
You cannot pass any other arguments positionally (i.e., you must precede arguments to pass to other parameters with their parameter name).
Alternative: Using -Command instead of -File:
powershell -command "./Foobar.ps1 '-versn=\"1.0.0.0\"', '-pattern=\"FooBar.*\"'"
Note:
What you pass to -Command is treated as a piece of PowerShell code - whether you pass a single argument or multiple ones - in the latter case, they are simply concatenated with spaces and the result is then interpreted as PowerShell code.
The implications are:
To execute a script in the current directory, you must refer to it with a path (.\) - in order to execute it.
Additionally, if the script path contains whitespace or other shell metacharacters, you must quote it and invoke it via &, the call operator; e.g., & '.\Foo Bar.ps1'
You can pass array arguments as usual, except that " chars. must again be escaped as \", not `".
Also note that again the arguments are quoted as a whole (too), which in this is case required to prevent PowerShell from interpreting -versn and -pattern as parameter names.
Given that the overall command must be passed inside "...", performing the embedded quoting with ", which still possible, is quite awkward, because you then have to combine \- and `-escaping; e.g., \"-pattern=`\"FooBar.*`\"\"
Generally, be aware that all usual PowerShell parsing rules apply, so that, for instance, to use a literal $, you must escape it as `$.

Running complex command line from C#

I'm trying to run the following command line from C#:
Process.Start("C:\\Program Files\\GoToTags\\GoToTags Encoder\\GoToTags.Encoder.exe --records "{'Url':'http://petshop.intato.com/index.php?id='" + TxtBoxIDCode.Text + "'','RecordType':'Website'}"");
Obviously it is not working.
The problem is that I need to keep the proper signs such as the : in order to make it work properly.
The original command is:
C:\Program Files\GoToTags\GoToTags Encoder\GoToTags.Encoder.exe --records "{'Url':'http://petshop.intato.com/index.php?id=29','RecordType':'Website'}"
I have to run that command and at the same time, replace that 29 with the content of a textbox
Would anyone be able to help me with that?
The string.Format command is your friend...
string path = #"C:\Program Files\GoToTags\GoToTags Encoder\GoToTags.Encoder.exe";
string args = string.Format("--records \"{'Url':'http://petshop.intato.com/index.php?id={0}','RecordType':'Website'}\"", TxtBoxIDCode.Text);
Process.Start(path, args);
You have several pitfalls awaiting you.
Firstly, as you've already discovered, the backslashes in path names cause problems in the strings, as they could also indicate C# escape sequences. It's usually good practice to use C#'s #"..." syntax for file names, partly to avoid needing to double up the backslashes and make it easier to read, and partly because you could inadvertently leave a \t in there and it'd go unnoticed for ages.
Secondly, the single-parameter call to Process.Start only takes a command - it cannot accept command arguments - so you have to call the two-parameter overload.
Thirdly, the quotes around of the value of the records argument need handling so that the C# syntax knows what you want with them - i.e. to pass them to the command. I've separated out the command arguments into two parts to make that clearer. I have elected to use backslashes to escape them, though using the alternative #"...""..." would be just as good, and the choice is largely down to personal preference unless the context points you strongly one way rather than the other.
string cmd = #"C:\Program Files\GoToTags\GoToTags Encoder\GoToTags.Encoder.exe";
string url = "http://petshop.intato.com/index.php?id=" + TxtBoxIDCode.Text;
string cmdArgs = "--records \"{'Url':'" + url + "','RecordType':'Website'}\"";
Process.Start(cmd, cmdArgs);
[edited to add:]
If for some reason you find you either want or need to use string.Format to help build your cmdArgs, there's a fourth gotcha waiting in the wings for you, in that string.Format looks for the brace ({ and }) characters to delimit insertion parameter specifications, but your records command-line argument wants braces characters in the string. The way to achieve that would be to double up the braces that you want, like this:
string cmdArgs =
string.Format("--records \"{{'Url':'{0}','RecordType':'Website'}}\", url)";
In addition to the other answers, you should use the two-argument overload of Process.Start. The first argument is the executable, and the second argument is the command line arguments.
Normally, if you insist on using a single argument call, you should enclose your executable in double quotes, as such:
"\"C:\\Program Files\\GoToTags\\GoToTags Encoder\\GoToTags.Encoder.exe\" ...arguments here..."
However, this form does not work for Process.Start(string) because it specifically disallows it.

Passing filepath into Main(string[] args) not working

I am trying to pass a file path into a C# Console Application but am having problems with the string being incorrect by the time it reaches the console application.
If I run my application from the command line, with a file path parameter:
MyApp "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject\"
A windows dialogue pops up and informs me that my application has stopped working, and when I click the Debug option, I can see that the result of args[0] is:
C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject"
Note there is still a trailing quote at the end.
If I pass a second argument:
MyApp "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject\" "any old string"
I get an error again, and after viewing in debug I see that args[0] is:
C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject" any
I am baffled as to why this is happening. My only guess is that the backslashes in the string are causing some kind of escape sequence from the string? Edit: I notice that the same is happening in the string example above! It seems \" is causing problems here.
I just want to pass in the file path of the current solution directory and am calling my app from a pre-build event using $(SolutionDir), and know that I can get the path of the current solution in other ways. But this is simplest and I am curious as to why it does not work as expected.
Yes, the rules for commandline arguments are a little murky.
The \ is the escape char and you can use it to escape quotes ("). You'll have to escape a backslash but only when it is preceding a quote. So use (note the '\\' at the end):
MyApp "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject\\"
or, simpler but you'll have to deal with it in C# somehow:
MyApp "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject"
Also see this question
That's why it's always better to use / in path
"C:/Users/DevDave/Documents/Visual Studio 2012/Projects/MyProject/" "any old string"
take a look: http://en.wikipedia.org/wiki/Path_(computing) , you can use both \ and / in path but if you want any shell compatibility I suggest to use /
Anything that comes after MyApp will be the first argument (args[0]). In your case, the first argument is "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject\". Also, the quote at the end of the string seems to happen because \" means that you want to want to escape the quote and write it as a string. In this case, the quote is not closing the string. That is the reason that your args[0] is the whole thing that comes after MyApp
If you don't want to scape the quote and have a slash behind it, you should do \\"
You could try this and tell me what happens:
MyApp "C:\Users\DevDave\Documents\Visual Studio 2012\Projects\MyProject\\"
(look at the double slash)
Hope it helps.
Continuation of Henk's answer, and choose to add a \ at the end of the path, then:
If you choose to hack the bug by choosing this code in Henk's answer ("simpler but you'll have to deal with it in C# somehow:"), then you should realize some bugs that will occur:
args[0] will only be set, even if you pass multiple parameters in. The length of args will be equal to 1. So you have to split args[0] into multiple pieces for your hack.
You have to replace any " characters with a \ if they are at the end of the pieces you split.

Console Application input parameters - string identification

I have a console app that takes in string parameters. The app runs through the command line like so:
C:\ExampleApp.exe this is a "test"
In the above example, there are 4 different strings read in as parameters.. Is there any way to determine which parameter had quotes around it? When I do a Console.WriteLine(args[3]), it prints out as test and not "test".
Environment.CommandLine
returns a complete command line as a single string, just parse it and you will get the original parameters, quoted or not.
As far as I know the quotes are stripped by the framework before beeing passed to your main function, have you tried to escape the qoutes using a backslash?

Spaces and backslashes in Visual Studio build events

I have an application that is supposed to aid my project in terms of pre- and post-build event handling. I'm using ndesk.options for command line argument parsing. Which gave me weird results when my project path contains spaces. I thought this was the fault of ndesk.options but I guess my own application is to blame. I call my application as a post-built event like so:
build.exe --in="$(ProjectDir)" --out="c:\out\"
A simple foreach over args[] displays the following:
--in=c:\my project" --out=c:\out"
What happened is that the last " in each parameter was treated as if it was escaped. Thus the trailing backslash was removed. And the whole thing is treated as a single argument.
Now I thought I was being smart by simply escaping the first " as well, like so:
build.exe --in=\"$(ProjectDir)" --out=\"c:\out\"
In that case the resulting args[] look like this:
--path="c:\my
project"
--out="c:\out"
The trailing backslash in the parameters is still swallowed and the first parameter is now split up.
Passing this args[] to ndesk.options will then yield wrong results.
How should the right command line look so that the correct elements end up in the correct args[] slots? Alternatively, how is one supposed to parse command line arguments like these with or without ndesk.options? Any suggestion is welcome.
Thanks in advance
Did you try to escape the last backslash?
build.exe --in="$(ProjectDir)\" --out="c:\out\\"
This works probably only, as long as the ProjectDir ends in \, which should be given.
This is just an idea, but I did not give it a try
EDIT:
I found a comment which suggests to leave out the trailing "
I actually used "." to solve this same problem:
build.exe --in="$(ProjectDir)." --out="c:\out\."
primarily because otherwise it might look like you are trying to escape the second quote...which you're not, you're escaping the final \ (which is hidden).
I also added a REM in the postbuild command describing why I did that.

Categories