Search code examples
c#process.startprocessstartinfo

Application started by Process.Start() isn't getting arguments


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.


Solution

  • 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();
            }
    
        }
    }