I have tried to use this example to run an external program using CreateProcessW() in C++, however, when I use multiple arguments this code seems to not work.
In my case, I pass the following path:
std::string pathToExe = "C:\\Users\\Aitor - ST\\Documents\\QtProjects\\ErgoEvalPlatform\\ErgonomicEvaluationPlatform\\FACTS\\xsim-runner.exe"
and the following arguments:
std::string arguments = "--model=facts_input.xml --output_xml=something.xml"
These parameters work from cmd, but they seem to not give any output (an xml should appear in the same folder) when I use them from C++.
Is there something I might be missing?
The following is an example for showing "How to run an exe with multiple arguments with CreateProcessW in C++". You can check if it helps.
The launcher application (a console app):
#include <iostream>
#include <windows.h>
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi; // The function returns this
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CONST wchar_t* commandLine = TEXT("arg1 arg2 arg3");
// Start the child process.
if (!CreateProcessW(
L"D:\\Win32-Cases\\TestTargetApp\\Debug\\TestTargetApp.exe", // app path
(LPWSTR)commandLine, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // 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
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
throw std::exception("Could not create child process");
}
else
{
std::cout << "[ ] Successfully launched child process" << std::endl;
}
}
The target application (another console app) that will be launched:
#include <iostream>
#include <windows.h>
int main(int argc, char *argv[])
{
if (argc > 0)
{
for (int index = 0; index < argc; index++)
{
std::cout << argv[index] << std::endl;
}
}
return 1;
}
There's two potential problems I can infer from the code you're showing.
Space before the arguments
Depending on how you're concatenating the arguments string to the executable string, you may miss a space before the arguments. Without the code, it's impossible to tell, but try changing the arguments string like this :
std::string arguments = " --model=facts_input.xml --output_xml=something.xml;"
Current directory
CreateProcess spawns a child process that inherits the current directory from it's parent process. The XML files you specify on the arguments use relative paths.
Try specifying the full path of the XML files you're passing in the arguments, something like this :
std::string arguments = " --model=\"C:\\Users\\Aitor - ST\\Documents\\QtProjects\\ErgoEvalPlatform\\ErgonomicEvaluationPlatform\\FACTS\\facts_input.xml\" --output_xml=\"C:\\Users\\Aitor - ST\\Documents\\QtProjects\\ErgoEvalPlatform\\ErgonomicEvaluationPlatform\\FACTS\\something.xml\"";
You have to pass the complete command line in the arguments as under:
std::string arguments = "C:\\Users\\Aitor-ST\\Documents\\QtProjects\\ErgoEvalPlatform\\ErgonomicEvaluationPlatform\\FACTS\\xsim-runner.exe --model=facts_input.xml --output_xml=something.xml"
The second parameter of CreateProcessW requires complete command line and not just arguments. It passes this to the process and if the target process is a C program taking agrs, then as usual the first parameter will be module name and others that follow will be args.
Hope this helps
Related
I am trying to write a text processing console application in C++ and I have met Windows API for the first time. But before defining a grammar or using the existing bison/flex or boost tools I want to realize a TDD approach and write all tests and interfaces (except here I have put a dummy test having hard time with even launching the .exe from test code).
My design is following:
- Unit tests from native test project call a CreateProcess() function from Windows API that I have put into CreateProc() macro. Example of this macro (I have slightly modified it) is taken from MSDN. Idea of this macro is execute my console application but from unit test to simulate user input after. Here is the code:
#define MYEXE L"my_Console_Application_Path.exe"
#ifdef MYEXE
PROCESS_INFORMATION CreateProc() {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcess
(
MYEXE,
NULL,
NULL,
NULL,
NULL, //,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi
)
)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return pi;
}
else
{
std::cout << "Unable to execute.";
return pi;
}
}
#endif
Test that runs and "uses" my console application is defined:
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(Should_Open_Console)
{
PROCESS_INFORMATION processInfo = CreateProc();
Assert::IsTrue(processInfo.dwProcessId); // check if process id is not zero
}
main function of console application looks like this:
int main()
{
std::cout << "test that function is running" << std::endl;
std::string myline = "";
std::getline(std::cin, myline);
return 0;
}
Basically my CreateProc() sucessfully launches the executable. However I want tests to simulate user input that main is listening to with getline(...). After I am going to parse this sequence with some parser.
How could I simulate this input within current console process? Of course I will be grateful if you can advise me some concrete instruments (is this SendInput() solution the best way to simulate input?) and the most important - how it should be done in terms of design? Ideally I see a string defined in a Unit test and passed through CreateProcess() as argument to main and the same string is somehow read from console after. Kind of this:
TEST_CLASS(UnitTest1)
{
public:
// parsing Hola as Hello
TEST_METHOD(Should_Return_Hello_If_Input_Equals_to_Hola)
{
CreateProc("Hola");
Assert::AreEqual(Console.readline() = "Hello"); // dummy code
}
Am I right? If yes, how could I actually do it? (if you can correct code snippets it would be great)
My code should take arguments, put "+" inbetween them, and search google chrome with this, however I get the error (Command line Argument= "Stack Overflow Site"):
http://www.google.com/search?q=Stack+Overflow+Site'C:\Program' is not
recognized as an internal or external command, operable program or
batch file.
Also in my program I get this error:
error C4996: 'strcpy': This function or variable may be unsafe.
Consider using strcpy_s instead. To disable deprecation, use
_CRT_SECURE_NO_WARNINGS. See online help for details. c:\users\user\documents\visual studio
2013\projects\project1\project1\main.cpp
I have been ignoring this, because I thought it was just a warning, but I'm not sure if it is relevant.
My code:
#include <Windows.h>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char** argv){
//Loop through arguments and put a "+" between them.
string out = "";
for (int i = 1; i < argc; ++i) {
if (i != 1){
out += "+";
}
out += argv[i];
}
string newout = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \"http://www.google.com/search?q=" + out + "\"";
// set the size of the structures
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
//set y to newout and convert
char *y = new char[newout.length() + 1];
strcpy(y, newout.c_str());
//Run Google Chrome with argument
CreateProcessA("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", // the path
y, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // 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
);
delete[] y;
cin.ignore();
}
I suspect you are not getting exactly the same error since you
stopped deleting the commandline buffer before using it, but
some different, equally unexpected result.
If you will read the documentation of CreateProcess
you will learn that the parameters:
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
can be supplied in one of three ways, as illustrated:-
Way 1
lpApplicationName = "\path\to\executable"
lpCommandLine = "args for the executable"
which results in a process initiated with the command:
\path\to\executable args for the executable
Way 2
lpApplicationName = NULL
lpCommandLine = "\path\to\executable args for the executable"
which results in the same process as Way 1
Way 3
lpApplicationName = "\path\to\executable"
lpCommandLine = NULL
which results in a process initiated with the command \path\to\executable.
You are not using Way 1 or Way 2 or Way 3, but:
lpApplicationName = "\path\to\executable"
lpCommandLine = "\path\to\executable args for the executable"
which in your case results in a process initiated with the command:
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
http://www.google.com/search?q=Stack+Overflow+Site"
in which the arguments passed to Chrome are:
C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
http://www.google.com/search?q=Stack+Overflow+Site"
Of course this does not have the anticipated outcome.
Should you wish to correct this by adopting Way 2, bear particularly
in mind what the documentation says about lpCommandLine:
...
If lpApplicationName is NULL, the first white space–delimited token of the command line specifies the module name.
...
I am trying to use msdn function (CreateProcess) in C++ in order to run an application. I don't get any errors but when I run it, it crashes. What I did also notice it that it created a process, but it didn't run the text file it was supposed to.
My code:
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
void createPro ();
int main()
{
createPro();
}
void createPro () {
LPCTSTR lpApplicationName = "C:/Users/Andyy/Desktop/hello.txt";
LPSTARTUPINFO lpStartupInfo;
LPPROCESS_INFORMATION lpProcessInfo;
memset(&lpStartupInfo, 0, sizeof(lpStartupInfo));
memset(&lpProcessInfo, 0, sizeof(lpProcessInfo));
if (!CreateProcess(lpApplicationName,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
lpStartupInfo,
lpProcessInfo
)
) {
cout << "Failed to create process" << lpApplicationName << endl;
}
cout << "Program exec: " << lpApplicationName << endl;
}
It creates the process but it fails to run the text file and no errors showed in compilers. Thanks in advance.
Return error: Process returned -1073741819 (0xC0000005)
CreateProcess is used to create process objects from executable files. You are passing a text file which is not an executable file. Presumably you are actually trying to show the text file in the default text editor. Use ShellExecuteEx to do that.
Beyond that, you don't allocate the two structs that are needed in your call to CreateProcess. Instead you declare pointers to structs, and leave the pointer uninitialized. I expect that this is the cause of the access violation error 0xC0000005. For future reference, you would allocate and initialise these structs like so:
STARTUPINFO StartupInfo = { sizeof StartupInfo };
PROCESS_INFORMATION ProcessInfo = { 0 };
Then pass &StartupInfo and &ProcessInfo to CreateProcess. Of course, I'm just adding this advice for completeness. You'll be throwing away all the code relating to CreateProcess and calling ShellExecuteEx instead.
What I need to do is to run (for example) 6 copies (independent processes) of example.exe (what is also my program) with different command line arguments from my Main program. This copies should work at the same time. I'm using code like this:
const int NumberOfProcesses= 6;
STARTUPINFO si[NumberOfProcesses];
PROCESS_INFORMATION pi[NumberOfProcesses];
srand((unsigned)time(NULL));
for(int i=0;i<NumberOfProcesses;i++)
{
char fname[MAX_PATH];
strncpy(fname,"\"",1);
fname[1] = '\0';
strcat(fname,"d:\\test\\example.exe");
strcat(fname,"\"");
int id = i;
strcat(fname," ");
strcat(fname,(std::to_string(id)).c_str());
int count = (rand()%1000) + 1;
strcat(fname," ");
strcat(fname,(std::to_string(count)).c_str());
int lb = 13;
strcat(fname," ");
strcat(fname,(std::to_string(lb)).c_str());
int ub = 666;
strcat(fname," ");
strcat(fname,(std::to_string(ub)).c_str());
printf(fname);
cout<<"\n";
//Here in fname I have correct command, that runs properly
bool t = false;
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
}
So if NumberOfProcesses==0 it will run "d:\test\example.exe" 1 2 3 4 from fname. But if NumberOfProcesses==6 (or something else) zero iteration will complete properly, but others will return false. Sometimes 4th iteration runs properly.
I think this is because when zero iteration run "d:\test\example.exe" 1 2 3 4 the example.exe is busy and can not be run one more time. So I changed code to this:
bool t = false;
getchar();
for(int i=0;i<5;i++)
{
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
if(t) break;
}
So I got some delay between starting example.exe - and it helped. All 6 copies starts and finishes, but they don't run parallel (I have output from example.exe).
But this is not the way I want my program work.
How can I avoid this problem?
Thanks.
UPD.
According to Werner Henze's answer I've just add couple of lines (to initialize STURTUPINFO) into loop
const int NumberOfProcesses= 6;
STARTUPINFO si[NumberOfProcesses];
PROCESS_INFORMATION pi[NumberOfProcesses];
for(int i=0;i<NumberOfProcesses;i++)
{
///Next two lines is important
ZeroMemory( &si[i], sizeof(si[i]) );
si[i].cb = sizeof(si);
/*Some actions*/
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
}
And it is work fine now.
Thanks again.
In the cases where CreateProcess returns FALSE you should check GetLastError(). In the code that you posted you are not initializing the si array. STARUPINFO is an input paramter to CreateProcess, so you must initialize it before passing it to CreateProcess.
I guess example.exe is running so fast that they are terminated before you come to look at them. If you printf a timing value like GetTickCount() after each CreateProcess, you will see that all CreateProcess calls occur very fast.
i have the following test code:
#define CMDLINE ".\\dummyFolder\\dummyProc.exe op1 op2 op3"
int main(int argc, char **argv) {
STARTUPINFO info;
info.cb = sizeof(STARTUPINFO);
info.lpReserved = NULL;
info.cbReserved2 = 0;
info.lpReserved2 = NULL;
PROCESS_INFORMATION processInfo;
SECURITY_ATTRIBUTES procAttr;
procAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
procAttr.lpSecurityDescriptor = NULL;
procAttr.bInheritHandle = false;
SECURITY_ATTRIBUTES threadAttr;
procAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
procAttr.lpSecurityDescriptor = NULL;
procAttr.bInheritHandle = false;
bool handlersInheritable = true;
char cmdLine2[sizeof(CMDLINE)];
strcpy(cmdLine2, CMDLINE);
char AppName[sizeof(".\\dummyFolder\\dummyProc.exe")];
strcpy(AppName, ".\\dummyFolder\\dummyProc.exe");
if (CreateProcess(AppName, cmdLine2, &procAttr, &threadAttr,
handlersInheritable, 0, NULL, NULL, &info, &processInfo)) {
//::WaitForMultipleObjects(procQty, handlers, waitForAll, waitInterval);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
CloseHandle(info.hStdError);
CloseHandle(info.hStdInput);
CloseHandle(info.hStdOutput);
} else {
std::cout << "Returned: " << GetLastError() << std::endl;
}
std::cout << "Exiting main process" << std::endl;
return 0;
}
This is just a test code for creating processes in windows. The problem is that when i launch "dummyProc.exe" i get a 0xc0000142 error.
The process dummyProc.exe runs fine from the command line, but not from within the code.
Here's the dummyProc code if it helps:
int main(int argc, char **argv) {
std::cout << "Working!!!!" << std::endl << "Receivedi: " << std::endl;
for (int i = 0; i < argc; ++i)
std::cout << argv[i] << std::endl;
return 0;
}
So, any ideas?
The most obvious thing that char cmdLine2[sizeof(CMDLINE)]; declares a string of length equal to your machine's pointer size. You need to use strlen(CMDLINE)+1 instead. Likewise for appName.
Note that the first parameter to CreateProcess does not need to be writeable. Just pass the string literal directly to it. No need for appName variable.
As for lpCommandLine which does need to be writeable it's easiest to do it like this:
char cmdline[] = "op1 op2 op3";
That gives you a writeable buffer. Note that you do not need to repeat the executable file name.
Another problem is that you have not initialized all the parameters to CreateProcess. For example the STARTUPINFO struct has 19 fields and you initialize only 3. You should initialize all your structs to 0 and then fill out any fields you need to be non-zero. Like this:
STARTUPINFO info = { 0 };
Do this for all the structs you pass.
You can, and should, pass NULL for the lpProcessAttributes and lpThreadAttributes parameters.
This answer is to relate another cause for 0xc0000142 - placed here (even though another answer was accepted for this question) because there is very little useful information on the intertubes about this error - and a shocking lack of any useful information on the subject from Microsoft - and so someone's internet search may get them here. (Well, mine did.)
So: You can get The application was unable to start correctly (0xc0000142) on starting a process written in C++ where you access though a null pointer in a constructor of a static object. (In my case it was in an initializer of a constructor of a static object.)
Your hint to this will be an event in the application log (event id 1000 source "Application Error") which has lines similar to the following:
Faulting module name: unknown, version: 0.0.0.0, time stamp: 0x00000000
Exception code: 0xc0000005
Fault offset: 0x0000000000000000
0xc0000005 is access violation, of course, and the offset of 0 (actually anything less than 0x10000 is a reference through a null pointer.
Anyway, the surprising thing (to me) is that evaluating statics happens before the debugger can attach (!!) so launching it with ImageFileExecutionOptions set or even directly within Visual Studio doesn't let you debug this thing!!
(And then of course you won't find 0xc0000142 in any Microsoft documentation whatsoever. Well done, NT team!)