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

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.

Related

How to to open a program with CreateProcess if only its name is known?

Like in the following example I'm trying to start Googles Chrome browser from a Windows application using the Windows API function CreateProcess.
The problem I have is that I dont know the path to the Chrome application (or any other application in the Program path). How can I get this?
In the code below I commented three different examples. In case I start "calc", the Calculator is started as it is in the Windows/System32 path. In case I start Chrome with the full path to the application it runs too. But if I omit the path and just try to start "chrome" I get an error #2.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
void _tmain()
{
char* cmd = "calc"; // works... calc.exe is in windows/system32
// char* cmd = "chrome"; // doesn't work... how can I add the path if it's not known (e.g. windows installed on D:\)
// char* cmd = "c:/program files (x86)/google/chrome/application/chrome"; // works (even without extension .exe)
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd, // 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());
return;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
Note: if I enter "chrome" (without the quotes) in the Windows Run command window, Chrome starts too. What I'm looking for is the same functionality. However, my application can reside anywhere and is not necessarily located on the same drive as Chrome.
If you really must use CreateProcess then you will need to find out where it is installed and pass the full path to the executable. That's going to require some registry hacking.
However, I feel that there is an easier and more robust way. Chrome registers itself in the AppPaths registry so ShellExecuteEx with the file specified as L"chrome" and the default verb will do the job.
This is likely to be unrelated to WinAPI and the CreateProcess function, but only to the environment variable PATH. By default, it contains the path for all standard windows commands like calc or notepad, but you must add the path for other commands that you add later, be it under Program Files or anywhere else.
What to do:
carefully note the actual path of chrome
open Control Panel / System / Advanced system parameters
click Environment variables: you will find a PATH (case does not matter) in user and system variables.
add the path for chrome on one (system is meant meant for all users)
It should now be possible to launch chrome without specifying its full path.
NB: unsure for the actual labels for all of the above, my own box speaks french...

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.

diskpart does not process script correctly when executed from CreateProcess()

diskpart "myScript.txt":
select disk 1
convert dynamic noerr
select disk 2
convert dynamic noerr
create volume stripe disk=1,2 noerr
assign letter=X noerr
.
.
When running from the command prompt: diskpart /s myScript.txt it works as expected.
However, when run using win api's CreateProcess() both the convert commands do work but when it gets to
create volume, it displays:
"The arguments you specified for this command are not valid"
.
.
Now, to make things more interesting:
If the script is executed again from CreateProcess() a 2nd time (given the disks are now converted and it gives a correct error for the convert comamnds),
when it gets to the create volume, it does work.
This makes me think it has something do with the disks and or executable?
Any point in the right direction is appreciated as this is very confusing. Thanks.
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
strncpy( command, "diskpart.exe /s myScript.txt", (sizeof(command) - 1) );
CreateProcess( "c:\\WINDOWS\\system32\\diskpart.exe",
command,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
&pi ) );
end original question________________________________________________________
EDIT:
Updates and more info:
Added about 15 - 20 seconds worth of delay before create volume command, still got the same error message.
Also, split the work into two scripts, with two calls to
CreateProcess(). On the second script, just calling "create volume"
and assign, it hung for a while and then came back with a "this
command cannot be completed at this time"..or something to the
effect.
Another thing to note: On the first script, putting them into
dynamic, it was running about twice as slow compared to running with
command prompt.
Maybe should just run the whole thing twice (with errors on the second run) as that did work
EDIT2
The 2 scripts is now working, or worked when I tried it again. Not sure why it didn't work the first time.
Because your script works the second time through it seems the most likely cause is timing related -- The volumes are not ready by the time the create volume command is executed.
Based on that assumption:
You can add a detail disk command before the create volume command to find out the state of the disk. That will tell you something about the current state of the disk. Select the first disk as well to show its details too if disk 2 doesn't show you anything interesting. The information you get from this is likely to be helpful.
As for actually resolving the problem, introducing a delay by taking the disks online and offline may help. For example:
select disk 1
convert dynamic
select disk 2
convert dynamic
select disk 1
offline disk
select disk 2
offline disk
select disk 1
online disk
select disk 2
online disk
create volume stripe disk=1,2
assign letter=X
Here is an excerpt of what http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx says about the first parameter:
The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
If the executable module is a 16-bit application, lpApplicationName should be NULL, and the string pointed to by lpCommandLine should specify the executable module as well as its arguments.
given the above, Suggest:
(first, assure command[] buffer is plenty large enough for the following)
(may have to add the path for the 'myStript.txt)
(may be necessary to add some wait time before calling CreateProcess()
to assure the prior commands have completed.
strncpy( command, "c:\\Windows\\System32\\diskpart.exe /s myScript.txt", (sizeof(command) - 1) );
CreateProcess( NULL,
command,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
&pi ) );
As it could be a timing problem, you should ensure that you are correctly waiting the end of the diskpart command before proceeding further in parent program.
I always use WaitForSingleObject after CreateProcess when I want to simulate cmd.exe processing :
CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
This sequence is closer the the good old system function ...

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

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.

How do I open an .exe from another C++ .exe?

What I want to do is open an .exe from another .exe. I really don't know how to do this, so I searched the internet. I tried some suggested methods from the internet, but it didn't work.
Here's my code:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
system ("OpenFile.exe");
system ("pause");
return 0;
}
When I run it in DEV C++, it compiles, but I get a error. Can someone please help me?
You should always avoid using system() because
It is resource heavy
It defeats security -- you don't know you it's a valid command or does the same thing on every system, you could even start up programs you didn't intend to start up.
The danger is that when you directly execute a program, it gets the same privileges as your program -- meaning that if, for example, you are running as system administrator then the malicious program you just inadvertently executed is also running as system administrator. If that doesn't scare you silly, check your pulse.
Anti virus programs hate it, your program could get flagged as a virus.
You should use CreateProcess().
You can use Createprocess() to just start up an .exe and creating a new process for it.
The application will run independent from the calling application.
Here's an example I used in one of my projects:
#include <windows.h>
VOID startup(LPCTSTR lpApplicationName)
{
// additional information
STARTUPINFO si;
PROCESS_INFORMATION pi;
// set the size of the structures
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// start the program up
CreateProcess( lpApplicationName, // the path
argv[1], // 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 (removed extra parentheses)
);
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
EDIT: The error you are getting is because you need to specify the path of the .exe file not just the name. Openfile.exe probably doesn't exist.
I've had great success with this:
#include <iostream>
#include <windows.h>
int main() {
ShellExecute(NULL, "open", "path\\to\\file.exe", NULL, NULL, SW_SHOWDEFAULT);
}
If you're interested, the full documentation is here:
http://msdn.microsoft.com/en-us/library/bb762153(VS.85).aspx.
Try this:
#include <windows.h>
int main ()
{
system ("start notepad.exe") // As an example. Change [notepad] to any executable file //
return 0 ;
}
You are getting this error because you are not giving full path. (C:\Users...\file.exe)
If you want to remove this error then either give full path or copy that application (you want to open) to the folder where your project(.exe) is present/saved.
#include <windows.h>
using namespace std;
int main()
{
system ("start C:\\Users\\Folder\\chrome.exe https://www.stackoverflow.com"); //for opening stackoverflow through google chrome , if chorme.exe is in that folder..
return 0;
}
When executable path has whitespace in system, call
#include<iostream>
using namespace std;
int main()
{
system("explorer C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe ");
system("pause");
return 0;
}
Provide the full path of the file openfile.exe
and remember not to put forward slash / in the path such as
c:/users/username/etc....
instead of that use
c:\\Users\\username\etc
(for windows)
May be this will help you.
I know this is a bit late but this is to help all the new the c++ devs.
Basically I found that if you set the file path to the location then call the program you can bypass the error.
cout << "Opening Firefox";
system("cd C:\\Program Files\\Mozilla Firefox");
Sleep(1000);
system("start firefox.exe -P");
As you can see I set the file path to the location of Firefox then launch it. In my case I'm launching the Profile manager of Firefox, if you want to launch just Firefox remove the -P. I also put in a Sleep() to give my computer time to switch file paths. if you want to go back to the default file path use, system(cd C:\\Windows\\System32);. I made this by replicating commands in the command line for windows, though you would use Linux specific commands + file paths if that is what you are using.