Can separate calls two CreateProcess() share the same startup and process info? - c++

if I am using CreateProcess() multiple times, is okay to share the PROCESS_INFORMATION and STARTUPINFO variables? Or is it really bad practice? I've read quite a bit of documentation, but I can't find any examples about handling CreateProcess() calls more than once.
As an example, say I have the fake function below:
int SampleClass::sampleFn1(){
//Variables
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
//let us assume cmdPath = cmd.exe directory, and cmdTxtPtr has correct text
if(!CreateProcess(cmdPath, cmdTxtPtr, NULL, NULL, false, 0,
NULL, NULL, &siStartInfo, &piProcInfo)){
return 1; //failed at step 1
}
if(!CreateProcess(cmdPath,_T("/C ant debug"),NULL,NULL,false,0,NULL,
(LPCTSTR)directory,&siStartInfo,&piProcInfo)){
return 2; //failed at debug
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;//finished
}
A similar function happens to work in my program, but I'd like to make it as safe as possible.
Or... Should I do something like the code below instead:
int SampleClass::sampleFn2(){
//Variables
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
//let us assume cmdPath = cmd.exe directory, and cmdTxtPtr has correct text
if(!CreateProcess(cmdPath, cmdTxtPtr, NULL, NULL, false,
0, NULL, NULL, &siStartInfo, &piProcInfo)){
return 1; //failed at update project
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
if(!CreateProcess(cmdPath,_T("/C ant debug"),NULL,NULL,
false,0,NULL,(LPCTSTR)directory,&siStartInfo,&piProcInfo)){
return 2; //failed at debug
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;//finished
}
Or do they both being handled poorly? Thank you.

AFAIK the CreateProcess function writes into both the STARTUPINFO and PROCESSINFO structure, so unless you don't care about any of that info, which I think you should, you could do the second example you give.
By doing doing memset with 0, you reset all data in the structs to 0.
I'm not sure that this is very good practice, but maybe someone else can give more insight.

Related

CreateProcess calling cmd.exe incl. arguments with no showing (flashing) window?

The saga continues...
I've searched the web, i've searched on StackOverflow, i found many hope giving answers/solutions, but somehow they have all failed (up)on me (including the ones related to ShellExecute(Ex) ).
How to hide a (flashing) CMD window (incl. arguments) using CreateProcess??
I basically want to call/execute a set of conditional/native cmd.exe commands (i.e. FOR /F, and ||), but also an external command FIND(STR).exe. And this, without showing a (flashing) CMD window.
But even hiding something as simple as "cmd.exe /C ECHO ...flashing window is bad..." seems impossible to do.
The code i've tried (including many variations related to the dwFlags and wShowWindow flags
#include <windows.h>
int main()
{
char cmdline[] = "cmd.exe /c ECHO ...flashing window is bad...";
PROCESS_INFORMATION pi;
STARTUPINFO si;
// memset(&si,0,sizeof(STARTUPINFO));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// si.dwFlags = STARTF_USESTDHANDLES;
// si.dwFlags = CREATE_NO_WINDOW;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
// si.wShowWindow = CREATE_NO_WINDOW;
CreateProcess(NULL, (LPSTR) cmdline, NULL, NULL, 0, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// ExitProcess;
return 0;
}
I don't want to rely on external programs i.e. .vbs (Windows Scripting Host) or shortcut tricks, but simply a standalone compiled .exe.
Is this (really) too much to ask, or am i doing it (completely) wrong?
Thanks...
Update: You also seem to confuse CreateProcess flags (its dwCreationFlags argument) with the member of STARTUPINFO structure. These are different flags, CREATE_NO_WINDOW should not be in STARTUPINFO.
You have to pass the CREATE_NO_WINDOW flag, then the console window won't show. Originally I've answered that you have to redirect the standard handles which is not correct (but still highly recommanded).
Set STARTF_USESTDHANDLES and fill in appropriate handles. If you are interested in the output of the process, create pipes, otherwise you can just open nul an pass that.
Try Using ProcessBuilder. Here is an example of some code that I have that seems to work just fine. In my code below, the shellScript is a StringBuilder that I am dynamically creating that contains the command and it's parameters that I want to execute.
String[] scriptArray = shellScript.toString().split(" ");
ProcessBuilder builder = new ProcessBuilder(scriptArray);
File outputFile = new File("/logs/AgentOutputLog.txt");
File errorFile = new File("/logs/AgentErrorLog.txt");
builder.redirectOutput(outputFile);
builder.redirectError(errorFile);
Process process = builder.start();
int errCode = process.waitFor();
//errCode = 0 means online
if(errCode == 0){
success = true;
break;
//errCode = 1 means offline
} else if (errCode == 1){
success = false;
break;
}

CreateProcess fails with error 0

I want to start a process. To do so I called the CreateProcess method like this:
wchar_t *path = (wchar_t*) malloc(sizeof(wchar_t) * 500);
const char* const_path = "C:/Windows/notepad.exe";
for (int i = 0; i < strlen(const_path); i++)
{
path[i] = const_path[i];
}
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(NULL,
path,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi))
{
std::stringstream s;
s << "Could not start - Code " << GetLastError();
ui.processStateLabel->setText(s.str().c_str());
return;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
Problem is: it gives me "Could not start - Code 0", which makes no sense, because error code 0 is successful operation and obviously (no window opened) the operation was not successful. What do I have to do to get this working?
As you already figured out, the string is not terminated. You might also want to quote the path.
You should call GetLastError before the std::stringstream object is created because you don't know what the constructor does under the hood, that will hopefully provide a better error code.
I forgot that strlen Method does not include the 0 termination, which is needed. Thus changing strlen(const_path) to strlen(const_path) + 1 in the for-loop is the solution.

Is it possible to prevent system() from stealing focus? [duplicate]

I am coding a C program in Dev-C++, and I need to use a couple of Windows (CMD) commands. It is easy, but when the command in the system() function is executed, the program runs the console in the execution.
An example:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
system("if not exist c:\my_docs\doc.txt (xcopy /Y doc.txt c:\my_docs\)"); // Cmd command
system("pause");
return 0;
}
Exists other function, or a modification that do not shows the console?
Thanks you! Best regards.
You can use WinExec("your cmd command", SW_HIDE); instead of system("cmd command").
You can do it with CreateProcess.
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcessW(command, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
As FigBug stated, CreateProcess() is the way to go, but I don't think that CreateProcess() can execute a shell if statement. You may need to pass it something like this as a command:
"cmd.exe /c \"if not exist c:\my_docs\doc.txt (xcopy /Y doc.txt c:\my_docs\)\""
But a better solution might be to use CreateFile() to test if a file exists and CopyFile() to copy it.
NOTE: My answer is not necessarily tailored to your specific question, but this Q&A is the top Google result for "Windows system without command prompt" and other similar queries.
Here's a way to execute commands without a new cmd.exe window. Based on Roland Rabien's answer and MSDN, I've written a working function.
#include "AtlBase.h"
#include "AtlConv.h"
int windows_system(const char *cmd) {
PROCESS_INFORMATION p_info;
STARTUPINFO s_info;
DWORD ReturnValue;
CA2T programpath(cmd);
memset(&s_info, 0, sizeof(s_info));
memset(&p_info, 0, sizeof(p_info));
s_info.cb = sizeof(s_info);
if (CreateProcess(programpath, NULL, NULL, NULL, 0, 0, NULL, NULL, &s_info, &p_info)) {
WaitForSingleObject(p_info.hProcess, INFINITE);
GetExitCodeProcess(p_info.hProcess, &ReturnValue);
CloseHandle(p_info.hProcess);
CloseHandle(p_info.hThread);
}
return ReturnValue;
}
Works on all Windows platforms. Call just like you would system().
int win_system(const char *command)
{
// Windows has a system() function which works, but it opens a command prompt window.
char *tmp_command, *cmd_exe_path;
int ret_val;
size_t len;
PROCESS_INFORMATION process_info = {0};
STARTUPINFOA startup_info = {0};
len = strlen(command);
tmp_command = malloc(len + 4);
tmp_command[0] = 0x2F; // '/'
tmp_command[1] = 0x63; // 'c'
tmp_command[2] = 0x20; // <space>;
memcpy(tmp_command + 3, command, len + 1);
startup_info.cb = sizeof(STARTUPINFOA);
cmd_exe_path = getenv("COMSPEC");
_flushall(); // required for Windows system() calls, probably a good idea here too
if (CreateProcessA(cmd_exe_path, tmp_command, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &process_info)) {
WaitForSingleObject(process_info.hProcess, INFINITE);
GetExitCodeProcess(process_info.hProcess, &ret_val);
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
free((void *) tmp_command);
return(ret_val);
}

trying to run commend on cmd throw c++ using createprocces (API)?

bool execute()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
bool flag = true;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
string f = "dir desktop"
if (CmdLine.parameter != "")
{
LPSTR l1 = const_cast<char *>(f.c_str());
CreateProcess(NULL, l1, NULL, NULL, false, 0, NULL, NULL, &si, &pi);
flag = true;
// WaitForSingleObject(pi.hProcess, INFINITE);
// // Close process and thread handles.
// CloseHandle(pi.hProcess);
// CloseHandle(pi.hThread);
//}
}
return flag;
}
I'm trying to run cmd command by visual studio.
I'm using createprocces (API) in order to run this thing
but I can't understand why it doesn't run anything.
dir is a command understood by cmd.exe, it's not a program you can execute.
You can try the command cmd /k "dir desktop", properly expressed as a C++ string.
E.g.,
auto execute()
-> bool
{
STARTUPINFO si = { sizeof( si ) };
PROCESS_INFORMATION pi = {};
string f = "cmd /k \"dir desktop\"\0";
bool const ok = !!CreateProcess( 0, &f[0], 0, 0, false, 0, 0, 0, &si, &pi );
if( !ok ) { return false; }
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
Note how the calls to ZeroMemory have been replaced with C++ initialization.
Just by letting the compiler do its job you get shorter, more clear code that is more likely correct, and just as efficient (possibly more). Win win win.
Disclaimer: code not reviewed by compiler.
If the intent is to list the contents of the user's desktop folder, then note that dir desktop doesn't do that. As an interactive command in the command interpreter you could use dir %userprofile%\desktop, and that also works via the Windows Run-dialog. Depending on the command interpreter's behavior for command line arguments it may work directly via CreateProcess, or not.
Generally, when using Windows API level functions it's preferable to use the wchar_t-based text based functions, i.e. define UNICODE before including <windows.h> (or use the ...W functions explicitly).
If you call CreateProcess() with the first parameter set to NULL, then you have to make sure that l1 starts with the module name to call.
As dir is an internal command of the command processor and not an executable, you have to use cmd as module name and give the rest of the parameter as cmd expects them.
So try the following:
string f = "cmd /c=dir desktop";

TerminateProcess() doesn't close the application

I am trying to use the TerminateProcess to terminate an app launched by ShellExecuteEX like this:
SHELLEXECUTEINFO ExecuteInfo;
ExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI; /* Odd but true */
ExecuteInfo.hwnd = NULL;
ExecuteInfo.cbSize = sizeof(ExecuteInfo);
ExecuteInfo.lpVerb = NULL;
ExecuteInfo.lpFile = "http://www.microsoft.com";
ExecuteInfo.lpParameters = "";
ExecuteInfo.lpDirectory = NULL;
ExecuteInfo.nShow = SW_SHOW;;
ShellExecuteEx(&ExecuteInfo);
//WaitForSingleObject(ExecuteInfo.hProcess, 0);
Sleep(4000);
TerminateProcess(ExecuteInfo.hProcess, 0);
IE gets opened but it never closes. Am I doing something wrong?
According to MSDN, fMask must be set to SEE_MASK_NOCLOSEPROCESS for .hProcess to get set. I would add a test to see if it is NULL. As a side note I've always had better luck using CreateProcess.
Edit:
This is how you do it using CreateProcess:
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
CreateProcess( NULL,
"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.google.com/",
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi );
Sleep(4000);
TerminateProcess(pi.hProcess, 0);
You should add error-checking and could query the path of the default browser using: AssocQueryString like this:
AssocQueryString(0,ASSOCSTR_EXECUTABLE,"http","open", szExe, &cchExe);
You can get more information by checking the returned hProcess (e.g., in a debugger). Also make sure you have the SEE_MASK_NOCLOSEPROCESS flag set.
But my psychic powers say: Opening an IE document doesn't necessarily create a new process. And, if it does, it may not be the one you think it is: The process you may have created may have spawned yet another process to actually host the document. Raymond Chen mentions that about halfway down this blog post.