CreateProcess doesn't pass command line arguments
Asked Answered
N

8

35

Hello I have the following code but it isn't working as expected, can't figure out what the problem is.

Basically, I'm executing a process (a .NET process) and passing it command line arguments, it is executed successfully by CreateProcess() but CreateProcess() isn't passing the command line arguments

What am I doing wrong here??

int main(int argc, char* argv[])
{
    PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

    STARTUPINFO StartupInfo; //This is an [in] parameter

    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field

    LPTSTR cmdArgs = "[email protected]";

    if(CreateProcess("D:\\email\\smtp.exe", cmdArgs, 
        NULL,NULL,FALSE,0,NULL,
        NULL,&StartupInfo,&ProcessInfo))
    { 
        WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);

        printf("Yohoo!");
    }  
    else
    {
        printf("The process could not be started...");
    }

    return 0;
}

EDIT: Hey one more thing, if I pass my cmdArgs like this:

// a space as the first character
LPTSTR cmdArgs = " [email protected]";

Then I get the error, then CreateProcess returns TRUE but my target process isn't executed.

Object reference not set to an instance of an object
Neoplasm answered 16/7, 2009 at 6:34 Comment(5)
How do you detect that the arguments are not passed?Akira
I check the output of my executed process, if there aren't any arguments passed the target process prints an error and terminatesNeoplasm
I suppose there could be some other problem. Can you insert a delay into the beginning of the program started and attach a debugger to it after it starts?Akira
In the beginning of the program (static void Main() function) you insert smth like System.Threading.Thread.Sleep( 20000 ); and set a breakpoint onto a line following that statement. Then you compile the C# program and make your master program start the C# program. When the C# program starts it will be suspended on that statement for 20 seconds - that should be enough for you to do Tools->Debug Processes in VisualStudio and attach to the C# program. When 20 seconds pass the C# program will stop in the debugger and you can then debug it step-by-step.Akira
This explanation from Microsoft might help, did for me, was in the same confusion. support.microsoft.com/kb/175986Simplism
B
30

You should specify also the module name in parameters: LPTSTR cmdArgs = "App [email protected]"; It should be the whole command line (including argv[0]).

Birthwort answered 16/7, 2009 at 6:51 Comment(4)
I get the error Object reference not set to an instance of an objectNeoplasm
This helps. It would never know if I didn't come across this answer.Estragon
I have to say that MSDN isn't too clear on this: "Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line." and "If lpApplicationName is NULL, the first white space–delimited token of the command line specifies the module name." The latter doesn't seem to apply as lpApplicationName is supplied.Pieplant
Exactly, many programs start with the evaluation of arguments from argv [1] because argv [0] usually contains the module name. If module name is not in the commando line, this is missing. And eventually you lose the first Prameter, this is a big problem and it cost me too much time to figure it out.Sachiko
G
26

If the first parameter to CreateProcess() is non-NULL, it will use that to locate the image to launch.

If it is NULL, it will parser the 2nd argument to try to get the executable to launch from the 1st token.

In either case, the C runtime will use the second argument to populate the argv array. So the first token from that parameter shows up in argv[0].

You probably want something like the following (I've change the smtp.exe program to echoargs.exe - a simple utility I have to help figure out just this kind of issue):

int main(int argc, char* argv[])
{
    PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

    STARTUPINFO StartupInfo; //This is an [in] parameter
    char cmdArgs[] = "echoargs.exe [email protected]";

    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field


    if(CreateProcess("C:\\util\\echoargs.exe", cmdArgs, 
        NULL,NULL,FALSE,0,NULL,
        NULL,&StartupInfo,&ProcessInfo))
    { 
        WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);

        printf("Yohoo!");
    }  
    else
    {
        printf("The process could not be started...");
    }

    return 0;
}

Here's the output I get from that program:

echoargs.exe [email protected]
[0]: echoargs.exe
[1]: [email protected]

Yohoo!
Gallicanism answered 17/7, 2009 at 1:49 Comment(2)
He is right. I even used this solution for win32 createprocess() it worked as expected. thanks man.Supinator
The second parameter should be a pointer to modifiable memoryKriskrischer
G
7

It doesn't look like you are using CreateProcess correctly, see http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx.

  • The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.

  • The lpCommandLine parameter can be NULL. In that case, the function uses the string pointed to by lpApplicationName as the command line.

  • If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

So in your case, you need this as the command argument and should probably pass a NULL for the first parameter to get the behaviour your want.

// NOTE THE Null-Terminated string too!
LPTSTR cmdArgs = "D:\\email\\smtp.exe [email protected]\0";
Generative answered 16/7, 2009 at 6:51 Comment(3)
Your code snippet at the end defines a string with TWO null terminators - no need to explicitly put in a second one.Berkowitz
I get the error Object reference not set to an instance of an objectNeoplasm
@Earwicker, been a long time since I've done C++, still the result is the same...Generative
S
6

Below is a cut down version of the code used by the Zeus IDE to run external processes:

bool createProcess(const char *pszTitle, const char *pszCommand)
{
  STARTUPINFO StartInfo;

  memset(&StartInfo, 0, sizeof(StartInfo));

  StartInfo.cb      = sizeof(StartInfo);
  StartInfo.lpTitle = (pszTitle) ? (char *)pszTitle : (char *)pszCommand;

  StartInfo.wShowWindow = SW_NORMAL;
  StartInfo.dwFlags    |= STARTF_USESHOWWINDOW;

  if (CreateProcess(0, (char *)pszCommand, 
                    0, 0, TRUE,
                    CREATE_NEW_PROCESS_GROUP, 0, 0, 
                    &StartInfo, &ProcessInfo))
  {
    lErrorCode = 0;
  }
  else
  {
    lErrorCode = GetLastError();
  }

  return (lErrorCode == 0);
}

The pszCommand would be the full executable path and file name and arguments so for example:

pszCommand = "D:\\email\\smtp.exe [email protected]";

From what I can tell, the only real difference between the two is that in the Zeus example, the dwCreationFlags argument is set to the CREATE_NEW_PROCESS_GROUP value.

Stanwin answered 21/7, 2009 at 5:53 Comment(3)
casting a const char * to char * is dangerous and invokes UBBasinger
I'm tempted to say it is okay in this case. Rationale: the 2nd argument to CreateProcess is LPTSTR (not LPCTSTR) presumably because of main(int argc, char *argv[]). The API does not copy the arguments. Bottom line, as long as the process being called does not violate its argv[] the (char*) should be fine.Bunnie
@jussij, what does pszTitle consist of ? What data does it store?Bor
C
5

You can add a space as first character of the cmdArgs string:

LPTSTR cmdArgs = " [email protected]";

Apparently Windows appends the 2nd argument string to the application name represented by the first argument, and the result is passed as command line arguments to the executable. So adding a space will properly separate the arguments.

Corder answered 26/6, 2017 at 22:7 Comment(0)
T
3

Try this:

LPTSTR cmdArgs = "[email protected]";
CString szcmdline("D:\\email\\smtp.exe");
szcmdline += _T(" ") + cmdArgs ;

//Leave first param empty and pass path + argms in 
    if(CreateProcess(NULL, szcmdline, second
Theodolite answered 16/7, 2009 at 6:50 Comment(2)
Error in that code, you're putting the value into szcmdline_in but using szcmdline. Any reason why to bring MFC strings into the example?Generative
No specific reason.It was just for symbolic. I will change the szcmdline_in to szcmdline. Thanks.Theodolite
H
1

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

Therefore you can try using LPTSTR cmdArgs = _tcsdup("[email protected]").

Another problem is: how does the target process reads the arguments? using argv[0] as application name? Then you shoud append the application name as the first parameter too.

Hinayana answered 16/7, 2009 at 7:6 Comment(3)
Or drop the memory management responsibility back where it belongs, the compiler: TCHAR[] cmdArgs = _T("[email protected]");Dawna
False sense of security here. Ask yourself why you have to bend around the API and Microsoft hasn't fixed it for 30 years or so. As I mentioned earlier it is likely Windows requires a char* (or LPTSTR) because of int main(int argc, char* argv[]). Just think of it. There's no real solution for the Win32 API should the callee write to argv[]. Actually, by declaring LPTSTR the leave that as an option.Bunnie
The new process runs in a different address space; what it does to argv does not affect the caller of CreateProcess() in any way.Surname
M
0

You are not allocating memory for your string.

Instead of:

LPTSTR cmdArgs = "[email protected]";

try:

TCHAR cmdArgs[] = "[email protected]";

Edit: then call:

 CreateProcess("D:\\email\\smtp.exe", &cmdArgs[0], ...

This will create a local array on the stack and then pass a pointer to that array.

Moralize answered 16/7, 2009 at 7:48 Comment(3)
error C2440: 'initializing' : cannot convert from 'char (*)[20]' to 'char *'Neoplasm
Actually just cmdArgs would be fine; &cmdArgs[0] is just a more complicated way of saying the same thing.Triclinium
Clear your concepts constant strings literals doesnt require memory allocation like arrays char str[], you can use pointers like char *str = "Arguements" aswell and indeed better solution here.Dogear

© 2022 - 2024 — McMap. All rights reserved.