13

Using C#, I am trying to pass command-line arguments to a new process using Process.Start():

string path = @"C:\Demo\Demo.exe";
string arguments = "one two three";
ProcessStartInfo startInfo = new ProcessStartInfo
   {
      FileName = path,
      Arguments = arguments
   };
var process = Process.Start(startInfo);

My C application Demo.exe just echos the command line arguments:

int main( int argc, char *argv[] )
{
   int count=0;

   // Display each command-line argument.
    printf( "\nCommand-line arguments:\n" );
    for( count = 0; count < argc; count++ )
        printf( "  argv[%d]   %s\n", count, argv[count] );

    while(1);
}

If I start my application from cmd.exe, I get reasonable output:

Command-line arguments:
 argv[0]   Demo.exe
 argv[1]   one
 argv[2]   two
 argv[3]   three

When I use the C# application, the only thing I get is the path argument at argv[0]:

Command-line arguments:
  argv[0]   C:

Task Manager shows command line arguments for each method of starting Demo.exe: enter image description here

Why isn't my C application receiving the command-line arguments from the C# application?

Edit @hvd suggested I use GetCommandLine(). Here is the code and result of that:

char* ar = GetCommandLine();
printf( "\nGetCommandLine arguments:\n" );
printf("  %s", ar);

Output:

GetCommandLine arguments:
  "C:

Is it possible that the C app is receiving the args as one string, but ignores everything after the first \ in the path?

Edit: I've added an answer below. It is a workaround, but I'm not sure the cause of my issue.

GreenRibbon
  • 299
  • 2
  • 16
  • 2
    Please show your C# code. – Jon Skeet Jun 02 '14 at 18:16
  • @JonSkeet The C# is in the first code box above. Is there something else you wanted to see? – GreenRibbon Jun 02 '14 at 18:30
  • 1
    Thank you for including a wealth of information in this question that can be used to find the issue. – usr Jun 02 '14 at 18:37
  • @GreenRibbon: Ah, I see what you mean now. I thought there was *also* a C# app started by the first app... – Jon Skeet Jun 02 '14 at 18:57
  • 1
    When you use cmd.exe to run the process are you doing so from the same directory as your application path in your C# app? I have a feeling your C# app can not see or read the c:\Demo directory. – Bit Jun 02 '14 at 19:11
  • @Bit It is from a different directory. The C# app is able to start the Demo.exe. The problem is that Demo.exe doesn't seem to receive the command line parameters. – GreenRibbon Jun 02 '14 at 19:17
  • @GreenRibbon your right I think I see the problem. – Bit Jun 02 '14 at 19:30
  • What happens when you use the filename/argument overload of `Process.Start` instead? – MicroVirus Jun 02 '14 at 19:46
  • What do you see if your C application uses [`GetCommandLineArgs`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms683156%28v=vs.85%29.aspx) instead of `argv`? –  Jun 02 '14 at 19:50
  • @MicroVirus I tried the filename.argument constructor overload first, and got the same result. – GreenRibbon Jun 02 '14 at 19:53
  • Your code works fine for me. It seems likely that you are doing something that is not listed in your info. – LVBen Jun 02 '14 at 21:34
  • How did you get arguments to show up in Task Manager. When I do it, the arguments do not show up in task manager, but the program does print out the arguments as expected. – LVBen Jun 02 '14 at 21:45
  • @GreenRibbon Try running your C# program from the command line, and tell us if you get the same results. – LVBen Jun 02 '14 at 21:55
  • The code works fine for me, too... – Jon Skeet Jun 03 '14 at 05:57
  • 1
    I used your code first to launch a C# console app and it displayed the arguments fine. Then I used a C++ Console app and it only displayed the first letters of each argument like your "C" when you called GetCommandLine(). Then I realized, its likely some sort of Unicode Issue, so I changed my C++ from printf to _tprintf to display the Unicode correctly in the array, and thats when it worked for me. It appears when you do printf it reads the first Byte and displays the correct first character, but the next Bytes is 0's (UTF), so it thinks its the end of the string, since 0 is NULL. – CodeCowboyOrg Jun 10 '14 at 16:57

3 Answers3

5

I've gotten back to this today and have a workaround working. I don't understand why my original attempt didn't work.

Here is the difference on the command line between typing Demo.exe and “Demo.exe.”

C:\Users\me\Desktop\Work\Builds\Win32>Demo.exe one two three
There are 4 arguments.
Command-line arguments:
argv[0]: Demo.exe
argv[1]: one
argv[2]: two
argv[3]: three

C:\Users\me\Desktop\Work\Builds\Win32>"Demo.exe" one two three
There are 1 arguments.
Command-line arguments:
argv[0]: Demo.exe

The Process.Start() call seemed to be doing the “Demo.exe” variety.

Doesn’t work:

ProcessStartInfo startInfo = new ProcessStartInfo
{
   FileName = @"Demo.exe",
   WorkingDirectory = @"C:\Users\me\Desktop\Work\Builds\Win32",
   Arguments = "one two three"
 };
 var process = Process.Start(startInfo);

There are 1 arguments.
Command-line arguments:
argv[0]: C:

Does work:

ProcessStartInfo startInfo = new ProcessStartInfo
{
   FileName = "cmd.exe",
   WorkingDirectory = @"C:\Users\me\Desktop\Work\Builds\Win32",
   Arguments = "/C Demo.exe one two three"
 };
 var process = Process.Start(startInfo);
There are 4 arguments.
Command-line arguments:
argv[0]: Demo.exe
argv[1]: one
argv[2]: two
argv[3]: three

Does anyone have any ideas why the first method doesn't work?

GreenRibbon
  • 299
  • 2
  • 16
4

I was able to reproduce your issue. I didn't have access to C, so I used C++ in Visual Studio 2013. It appears that C# using StartInfo passes the arguments as Unicode characters, so the first byte is non-zero, while the 2nd byte is likely 0 bits resulting in displaying only the first character since that indicates the string termination character. When I used printf it did not work, I had to use _tprintf to see what is passed. And printf does not handle Unicode. Not only does printf not handle it, your C program when populating argv will not translate Unicode to a string using 1 byte characters. While TCHAR (wide char) and tprintf in C++ does, as does C# natively.

So, when you did it the other way, using "cmd.exe" to call "/C Demo.exe one two three" cmd was not passing the string as Unicode. That's my hypothesis, given the results I am getting.

Related Question on StackOverflow

The C++ code that displayed the Arguments correctly (tprintf) and incorrectly (printf)

#include "stdafx.h"
#include "string.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int count=0;

    // Display each command-line argument.
    printf( "\nCommand-line arguments:\n" );
    for( count = 0; count < argc; count++ )
        //Correct. This statement worked, displaying the arguments
        //_tprintf( _T("  argv[%d]   %s\n"), count, argv[count] );

        //Incorrect. Displayed only the first character of each argument
        //printf( "  argv[%d]   %s\n", count, argv[count] );

    getchar();
    return 0;
}

This is the C# code that called it

namespace ProcessPassArguments
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Temp\Demo.exe";
                    string arguments = "one two three";
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                FileName = path,
                Arguments = arguments
            };
            var process = Process.Start(startInfo);
        }
    }
}

For informational purposes only, C# calling the C# also worked. Again the suspected cause is that C# is passing the arguments to your C program as Unicode Characters.

The C# code that works as the target programmed called.

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            foreach (string arg in args)
            {
                i++;
                Console.WriteLine("Argument {0}: {1}", i, arg);
            }
            Console.ReadLine();
        }

    }
}
Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
CodeCowboyOrg
  • 2,983
  • 1
  • 15
  • 12
-1

Try this

Arguments = "\"arg1\" \"arg2\" \"arg3\"";
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
Hesham
  • 361
  • 2
  • 11