How to include argument with CreateProcess API in VC++? - c++

I have called the other application from my main application using createprocess API. But the other process also need some arguments as a parameter.
I created the process as:
BOOL ret= CreateProcess( NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL,&siStartInfo, &piProcInfo);
szCmdline is variable which contians the application full path.
Any idea how to pass the argument with this process.
Thanks,

CreateProcess has both lpApplicationName and szCommandLine arguments. You must pass at least one of the arguments. However you should pass both for security reasons.
lpApplicationName is the name of the executable you wish to run.
szCommandLine is the command line you wish to pass to that executable. It should include the executable as the first item. This will be received by the application as an argument to WinMain or retrieved by the GetCommandLine function (though the system may prepend a fully-qualified path if one is not supplied). For C programs using main or wmain, it will be parsed by the CRT into arguments.
If you pass NULL for lpApplicationName, the system will attempt to locate the executable in szCommandLine, and will use that.
If you pass NULL for szCommandLine, the system will use lpApplicationName for both.
So the command line is the command line. If you have arguments to pass to the command, put them on the command line.
If lpApplicationName is NULL, the first white space–delimited token of
the command line specifies the module name. If you are using a long
file name that contains a space, use quoted strings to indicate where
the file name ends and the arguments begin (see the explanation for
the lpApplicationName parameter).
It is preferable to pass both lpApplicationName and szCommandLine, to ensure that the command line is not misinterpreted by the system and the wrong executable is run. (There was a class of security problems caused by this a few years ago).
Also, when passing both lpApplicationName and szCommandLine, remember that szCommandLine still needs to include the application name as the first argument.
So for instance, if your program is C:\Program Files\My Application\Program.exe and the arguments are /the /arguments, you would set lpApplicationName to "C:\Program Files\My Application\Program.exe", and set szCmdline to "C:\Program Files\My Application\Program.exe" /the /arguments.
What were the security concerns?
Well imagine if someone created a file "C:\Program Files\My.exe". If you omit the quotes, the system interpreted C:\Program Files\My Application\Program.exe /the /arguments as: C:\Program Files\My.exe Application\Program.exe /the /arguments. And you will get a surprise. This type of trick can be used to fool administrators into running programs they did not wish to run, which is a security problem. This does not occur if you pass the lpApplicationName argument.

The CreateProcess function creates a new process, which runs independently of the creating process. However, for simplicity, the relationship is referred to as a parent-child relationship.
The first parameter, lpApplicationName, can be NULL, in which case the executable name must be in the white space–delimited string pointed to by lpCommandLine.
this way you can send more than one arguments in CreateProcess API.
sprintf(exePath,"Project.exe %s %s \"%s\" \"%s\" \", appName,serverid,srjPath,caseName);
if( !CreateProcess( NULL, // No module name (use command line).
exePath, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
FALSE, // Set handle inheritance to FALSE.
NORMAL_PRIORITY_CLASS,// No creation flags.
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi ) // Pointer to PROCESS_INFORMATION structure.
)
when revived this command line arguments you should parsing this argument and separated by
A double quotation mark preceded by a backslash, \", is interpreted as a literal double quotation mark (").
for parsing command line argument visit this site : http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
and creatprocess API visit this :http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx

You can form a string consisting of program name + arguments like this
wstring cmd;
cmd.assign(L"\"C:\\Program Files\\MyProgram.exe\" arg1 arg2 arg3 arg4");
if(!CreateProcess(NULL,(LPWSTR)cmd.c_str(),NULL,NULL,1,0,NULL,NULL,&si,&pi))
{
return -1;
}

Use ShellExecute(Ex). The Control is much simpler, and since we have UAC the Control how to Launch a process is easier to.
With ShellExecuteEx aou also get the process handle if you Need it.
Just my 2 Cents.

Related

Do I need to specify an exe path as the 1st parameter in lpCommandLine when calling CreateProcessAsUser?

I can't seem to find a definitive answer to this. My goal is to start a process using a user token. Say, the process in question is started as such:
"C:\My folder\My proc.exe" param=1
So when I specify lpCommandLine parameter for the CreateProcessAsUser API, do I need to specify executable path as the 1st parameter as such:
LPCTSTR pStrExePath = L"C:\\My folder\\My proc.exe";
TCHAR buffCmdLine[MAX_PATH];
if(SUCCEEDED(::StringCchPrintf(buffCmdLine, MAX_PATH,
L"\"%s\" %s", pStrExePath, L"param=1")))
bResult = CreateProcessAsUser(
hToken, // client's access token
pStrExePath, // file to execute
buffCmdLine, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
envBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
Or can I omit the exe path and do this?
LPCTSTR pStrExePath = L"C:\\My folder\\My proc.exe";
TCHAR buffCmdLine[MAX_PATH];
if(SUCCEEDED(::StringCchCopy(buffCmdLine, MAX_PATH, L"param=1")))
bResult = CreateProcessAsUser(
hToken, // client's access token
pStrExePath, // file to execute
buffCmdLine, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
envBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
They both seem to work.
Reading the documentation, both cases should work.
From MSDN
If both lpApplicationName and lpCommandLine are non-NULL,
*lpApplicationName specifies the module to execute, and *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.
I agree that the documentation may be clearer saying that it accepts the argument part of the command line or the full command line in lpCommandLine when lpApplicationName is non-NULL.
UPDATE :
The documentation is better in the case lpApplicationName is NULL
If lpApplicationName is NULL, the first white space–delimited token of the command line specifies the module name...
UPDATE 2 :
There is a nice documentation about these arguments Understanding CreateProcess and Command-line Arguments.
Reading this documentation, I understand that there is a difference between your two cases. When you provide lpApplicationName and arguments in lpCommandLine the child process will parse the command line as it is in lpCommandLine. So if you do not duplicate the exe path, argv[0] will not represent the exe path as usual but param=1.

Starting Speech Recognition with CreateProcess() in C++

I need help with my simple program, which tries to create a new process running Speech recognition.
When I open cmd and type in the command C:\Windows\Speech\Common\sapisvr.exe -SpeechUX then the speech recognition would successfully start. It will start even when running through system(C:\\Windows\\...) which basically just mimics cmd.
However, when creating the new process with CreateProcess() as below, the function fails. If I put whole path and argument into second parameter CreateProcess(NULL, TEXT("C:\\Windows...\\sapisvr.exe -SpeechUX"), ...), then I get a runtime exception: Access violation writing location
#include <windows.h>
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(
TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe"), //Module name
TEXT(" -SpeechUX"), //command line params
NULL, //Process attributes
NULL, //Thread attributes
FALSE, //Handle inheritance
0, //No creation flags
NULL, //Use parent's environment
NULL, //Use parent's starting directory
&si, //Pointer to STARTUPINFO structure
&pi )) //Pointer to PROCESS_INFORMATION structure
{
printf("error creating process\n");
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
First I tried to test CreateProcess function with running notepad with an argument to open an existing file. When I put path to notepad.exe to first parameter and name of the file to the command line parameter, it didn't recognise it and opened a new file instead.
This whole applies as well to trying to run msconfig.exe from my program, which doesn't take any parameters, so I guess the problem is somewhere else, I just have no idea where.
I searched the web and none of the answers actually worked for me. I'm working in Visual Studio 2015 on Windows 8.1.
Thanks for help.
The CreateProcess function has a second argument as an LPTSTR. For the CreateProcessW version of this function, this must be a writeable buffer, not a string literal. Thus your program's behavior is undefined. Since you are getting an access violation writing to a location when calling CreateProcess, we will assume that CreateProcess is being mapped to CreateProcessW.
At the link posted, here is the quote:
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.
So the fix is simply define an array, not a literal:
TCHAR commandParam[] = TEXT(" -SpeechUX");
if (!CreateProcess(TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe"),
commandParam,
...
}
or if passing NULL as the first argument:
TCHAR commandParam[] = TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe");
//...
if (!CreateProcess(NULL, commandParam, ...
Also, if CreateProcess returns an error, you should call GetLastError and optionally FormatMessage, to get the error that occurred, and not simply output that there is an error.

C++ Winapi CreateProcess issue with command line

I'm using CreateProcess this way:
resultCreate = CreateProcess(Command, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
//"Command" contains the executable file to execute
//"CommandLine" contains the parameters to pass to that executable
The parameters are the following:
Param1: "C:\Users\myuser\Desktop\file.dll"
Param2: "file" (module name)
Param3: " " (blank)
So the full CommandLine string would be:
"C:\Users\myuser\Desktop\file.dll" file " "
CreateProcess runs the executable successfully and applies the first two parameters, but when reaches the third one, it throws error
The specified process could not be found.
Function " " could not be called, due to " " doesn't exist in the DLL "(null)"
How can I pass the desired parameters correctly?
When both lpApplicationName and lpCommandLine are used, you need to include the executable path as the first parameter of your lpCommandLine value, even though you are specifying the executable path in the lpApplication value. The lpCommandLine is passed as-is to the spawned process, and most RTLs (especially C-based RTLs, but others as well) expect the first command-line parameter to be the executable path since that is how command-line consoles operate. This is even mentioned in the CreateProcess() documentation:
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.
This is also covered in MSDN Support:
INFO: Understanding CreateProcess and Command-line Arguments
Behavior of CreateProcess() When Creating a 32-bit Process
Case 1:
If the ApplicationName parameter is passed and the CommandLine parameter is NULL, then the ApplicationName parameter is also used as the CommandLine. This does not mean that you can pass additional command-line parameters in ApplicationName string. For example, the following call will fail with a "File Not Found" error:
CreateProcess( "c:\\MyApp.exe Param1 Param2", NULL, ... )
Case 2:
On the other hand, if the CommandLine parameter is non-NULL and the ApplicationName parameter is NULL, then the API attempts to extract the application name from the CommandLine parameter.
Case 3:
The flexibility of the CreateProcess() function (and a possible point of confusion) arises when you pass a valid string pointer to both the ApplicationName and CommandLine parameters. This allows you to specify the application to be executed as well as the complete command line that is passed to the application. One might assume that the command line passed to the created application is a composite of the ApplicationName and CommandLine parameters, but this is not the case. As a result, a process created by CreateProcess can receive a value other than its .exe name as its "argv[0]" parameter. The following is an example of a call to CreateProcess that produces this "abnormal" behavior:
CreateProcess( "c:\\MyApp.exe", "Param1 Param2 Param3", ...)
MyApp's arguments will be as follow:
argv[0] == "Param1"
argv[1] == "Param2"
argv[2] == "Param3"
NOTE: ANSI specifications require that argv[0] should be equal to the application name, but CreateProcess gives the calling application the flexibility to override this rule for 32-bit processes.
Case #3 applies to your situation.
Use something like this instead:
std::string Command = "<exe path>";
std::string CommandLine = "\"" + Command + "\" <parameters>";
// std::string::c_str() returns a const pointer. The first parameter
// of CreateProcessA() is const, but the second parameter must be a
// non-const pointer to writable memory, because CreateProcessW() can
// modify the data...
//
resultCreate = CreateProcessA(Command.c_str(), &CommandLine[0], ...);
std::wstring Command = L"<exe path>";
std::wstring CommandLine = L"\"" + Command + L"\" <parameters>";
// std::wstring::c_str() returns a const pointer. The first parameter
// of CreateProcessW() is const, but the second parameter must be a
// non-const pointer to writable memory, because CreateProcessW() can
// modify the data...
//
resultCreate = CreateProcessW(Command.c_str(), &CommandLine[0], ...);
Alternatively, omit lpApplicationName and specify the complete command line to lpCommandLine only:
std::string CommandLine = "\"<exe path>\" <parameters>";
resultCreate = CreateProcessA(NULL, &CommandLine[0], ...);
std::wstring CommandLine = L"\"<exe path>\" <parameters>";
resultCreate = CreateProcessW(NULL, &CommandLine[0], ...);

ShellExecute: Verb "runas" does not work for batch files with spaces in path

I am using ShellExecuteW to start a batch file. Code looks somewhat like this:
ShellExecuteW(GetDesktopWindow(), wide_verb.c_str(), wide_filename.c_str(), wide_parameters.c_str(), NULL, SW_SHOW);
Where the wide_ variables are of type wstring. This code works fine for arbitrary combinations of file path and verb, except for verb "runas" (to get administrator rights) and a batch file with spaces in the given path wide_filename. The UAC prompt will pop up and, after confirmation, for a short moment the command prompt will flash up saying that the path could not be found, cutting off at the first space in the path.
I already tried wrapping the file name in additional quotes, but that didn't help.
The reason is probably that .bat files aren't really "executables", but documents, and therefor
ShellExecuteW(GetDesktopWindow(), L"runas", L"C:\\Path To\\file.bat", L"Parameters For Batch", NULL, SW_SHOW);
is internally mapped to an elevated execution of
cmd.exe /C "C:\Path To\file.bat" "Parameters for Batch"
while it should be something along the lines of
cmd.exe /S /C " "C:\Path To\file.bat" Parameters for Batch "
And indeed, if I execute something like this:
ShellExecuteW(GetDesktopWindow(), L"runas", L"cmd.exe", L"/S /C \" \"C:\\Path To\\file.bat\" Parameters For Batch \"", NULL, SW_SHOW);
it does the trick!
However, it doesn't seem like the cleanest approach to "hardcode" the executable (cmd.exe in this case) like that. For batch files it really doesn't matter, but for other script-type documents that take parameters it might become relevant.
So the question is: Is it possible to execute a batch file (with spaces in its path) elevated via ShellExecute or a similar mechanism, without explicitly passing it to cmd.exe?
CreateProcessWithLogonW is what you need here. It has a lot of arguments, but at the end of the page you'll see an Examples section instructing how to initialize the args.
As for hardcoding cmd.exe here's what CreateProcess official doc says (at the end of the section describing the lpApplicationName argument):
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
So i think you're good. Of course the path enclosing in "s still applies.

Trying to use CreateProcess() ; no compiler errors but main keeps crashing

I'm trying to create a function which opens a PDF in firefox separate from the main process. I believe I am having trouble with the parameters for createProcess... any help is greatly appreciated
EDIT: the batch file is being created, I have tested it several times, and to explain a bit:
The batch file is because I really don't know what I am doing, I am a student in computer science and this is a side project to help me at my job. I work at a law office and file the mail electronically as it comes in. I wanted to make a simple program that would loop through the scans directory, display the scan and prompt the user for information about the document. Therefore I need to be able to build a file path dynamically. Originally I was using "system" to open firefox and display the document. After a bit of trying I got it to work with a batch file. I then learned that system is a blocking command and that I would need to start a separate thread. This is where I ran into createprocess. I simply continued to use the batch file from my old system idea... And the more I think about it I can't remember which professor suggested the batch file or why...
void openPDF(char scansQueue[][MAX_NAME], int index)
{
// build bat file
fstream outfile;
outfile.open("C:\\firefox.bat");
if(outfile.good())cout<<"outfile good!!!!"<<endl;
outfile<<"\"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe\" \"C:\\Scans\\" <<scansQueue[index]<<"\"";
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if(!CreateProcess(NULL, L"C:\\firefox", NULL, NULL, false, 0, NULL, NULL, &si, &pi))cout<<"PROCESS FAILED TO EXECUTE!!!";
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
There are several problems with this code. A few has already been pointed out in the comments (closing potentially invalid handles on failure, possibility that the batch file can't be created, and the rather questionable command line). Here's a few more issues.
First, you can't run a batch file this way.
The documentation for CreateProcess clearly states:
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
Second, you are passing a string literal for lpCommandLine, something that's also explicitly outlawed by the documentation:
lpCommandLine [in, out, optional]
...
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.
Finally, why are you creating a temporary batch file to run a single command? You could easily have written the CreateProcess call to start Firefox directly.