C++ Run Batch File Without Redirecting Output - c++

tempString = (string("cmd.exe /C \"") + tempString + "\"");
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
CreateProcess(NULL, (LPSTR)tempString.c_str(), 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT, NULL, 0, &si, &pi);
I am starting a batch script from within another process through a dll.
The issue is that the process is displaying:
error : Input redirection is not supported, exiting the process
immediately.
How can I start a batch script so that it is independent of the calling process (will not close when callee closes) and does not output in the console of the calling process?

If you don't want show the console window when executing command string, you can do as below:
tempString = (string(" /C \"") + tempString + "\"");
ShellExecute(NULL, NULL, "cmd.exe", tempString.c_str(), NULL, SW_HIDE);

The CREATE_NEW_CONSOLE flag allocates a new console for the process to use so that it does not use the same stdout as the calling process.

Related

CreateProcess api failing with error code 122 on windows 10

I am using CreateProcess api to start a batch file. The Code works fine on windows 7 but it is failing on Windows 10.
Below is the snippet of code:
CString param; //it holds the very long string of command line arguments
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = excFile + _T(" ");
exeWithParam = exeWithParam.append(param);
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
TCHAR lpExeWithParam[8191];
_tcscpy_s(lpExeWithParam, exeWithParam.c_str());
BOOL bStatus = CreateProcess(NULL, lpExeWithParam, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
DWORD err;
if (!bStatus)
{
err = GetLastError();
}
With the above code, it is invoking a batch file which will start an executable with given parameters. This code is not working only Windows 10 in our product.
GetLastError is returning error code 122 which code for error "The data area passed to a system call is too small." How to figure out what is causing this error and how it can be resolved?
However, when using the same code in a sample test application is not giving any error and passing.
Any clue/hint why is causing it to fail on Windows 10.
You need to execute cmd.exe with the .bat file as a parameter, don't try to execute the .bat directly.
Also, you don't need lpExeWithParam, you can pass exeWithParam directly to CreateProcess().
Try something more like this instead:
CString param; //it holds the very long string of command line arguments
...
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = L"cmd.exe /c \"" + excFile + L"\" ";
exeWithParam.append(param);
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = {};
BOOL bStatus = CreateProcessW(NULL, &exeWithParam[0]/*or exeWithParam.data() in C++17*/, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
if (!bStatus)
{
DWORD err = GetLastError();
...
}
else
{
...
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Error 122 equates to ERROR_INSUFFICIENT_BUFFER and I think the clue here is "it holds the very long string of command line arguments".
Just how long is it? The limit may be lower on Windows 10 - I recommend you experiment (binary chop).
Also, the documentation for CreateProcess states that you must launch cmd.exe explicitly to run a batch file, so I guess you should do what it says.
I think to run a batch file you must set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file

How to execute a command in cmd using CreateProcess?

I'm trying to launch the command line through my c++ program and then have cmd run a command. I'm not sure what I'm doing wrong. I've looked at the MSDN documentation but I'm unable to understand what to change in my code.
Below is the chunk of code that I have written. I'm trying to launch cmd and then run the command in cmdArgs. However, on running the program it just launches the cmd without running the nslookup part of it. I've tried with other commands as well like ipconfig, but they do not get executed. Could someone help me understand what I'm doing wrong.
When I launch the program, it just opens up cmd. What I'm trying to do is have the cmdArgs runs and view the output on the cmd screen.
I'm new to c++, so if this is trivial I apologize. I've looked at other questions on the site, but it seems that the format of cmdArgs is correct - program name followed by the arg.
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
LPTSTR cmdPath = _T("C:\\Windows\\System32\\cmd.exe");
LPTSTR cmdArgs = _T("C:\\Windows\\System32\\cmd.exe nslookup myip.opendns.com. resolver1.opendns.com");
if (!CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
return "Failed";
}
Your program does exactly what you asked it to to: you just start the cmd.exe executable. Just test in a console windows:
C:\Users\xxx>start /w cmd ipconfig
C:\Users\xxx>cmd ipconfig
Microsoft Windows [version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Tous droits réservés.
C:\Users\xxx>exit
C:\Users\xxx>
So cmd.exe ipconfig just pushed a new cmd.exe without executing the remaining of the line. It is then waiting for commands coming from its standard input.
You must use cmd.exe /c ipconfig to ask the new cmd.exe to execute a command, or cmd.exe /K ipconfig if you want cmd not to exit after first command:
C:\Users\serge.ballesta>cmd /c ipconfig
Configuration IP de Windows
...
So you should write in your code:
...
LPTSTR cmdArgs = _T("C:\\Windows\\System32\\cmd.exe /k nslookup myip.opendns.com. resolver1.opendns.com");
...
Try using this:
wchar_t command[] = L"nslookup myip.opendns.com. resolver1.opendns.com";
wchar_t cmd[MAX_PATH] ;
wchar_t cmdline[ MAX_PATH + 50 ];
swprintf_s( cmdline, L"%s /c %s", cmd, command );
STARTUPINFOW startInf;
memset( &startInf, 0, sizeof startInf );
startInf.cb = sizeof(startInf);
PROCESS_INFORMATION procInf;
memset( &procInf, 0, sizeof procInf );
BOOL b = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &startInf, &procInf );
DWORD dwErr = 0;
if( b )
{
// Wait till process completes
WaitForSingleObject( procInf.hProcess, INFINITE );
// Check process’s exit code
GetExitCodeProcess( procInf.hProcess, &dwErr );
// Avoid memory leak by closing process handle
CloseHandle( procInf.hProcess );
}
else
{
dwErr = GetLastError();
}
if( dwErr )
{
wprintf(_T(“Command failed. Error %d\n”),dwErr);
}

How do I hide a command window and display all potential errors in an EditBox control?

I am creating simple application in Borland C++ Builder.
My code looks like this:
void __fastcall TForm1::Button3Click(TObject *Sender)
{
system("java -jar decompile.jar -o Source/ file.jar");
}
Now I want to hide the command window and show all potential errors in an EditBox control. The EditBox control should remain empty if there are no errors.
Edit1->Text= "ERROR";
use TMemo instead of TEdit box for log
It have multiple lines and support scrollbars. It is far better for logs.
see how to redirect commnd promt output to file
I do not use JAVA but you can try:
system("java -jar decompile.jar -o Source/ file.jar > errorlog.txt");
or:
system("java -jar decompile.jar -o Source/ file.jar >> errorlog.txt");
You can also include the application path
AnsiString exepath=ExtractFilePath(Application->ExeName);
So you save the file to known location nstead of actual path that could change easily during runtime...
see How can I run a windows batch file but hide the command window?
So you need to use CreateProcess with java.exe or command.com directly. I never done the hiding thing so follow the answer in the linked Q/A. If you do not know how to use CreateProcess (it can be overwhelming for beginers) then this is how I use it (it does not hide just starts an exe...)
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES attr0,attr1;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb=sizeof(si);
attr0.nLength=sizeof(SECURITY_ATTRIBUTES);
attr0.bInheritHandle=TRUE;
attr0.lpSecurityDescriptor=NULL;
attr1=attr0;
CreateProcess(NULL,"some_app.exe > logfile.txt",&attr0,&attr1,TRUE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
And you can use:
TerminateProcess(pi.hProcess,0);
To force terminate the app ....
Now when I put all together I got this:
AnsiString s,logfile;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES attr0,attr1;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb=sizeof(si);
// hide the process
si.wShowWindow=SW_HIDE;
si.dwFlags=STARTF_USESHOWWINDOW;
attr0.nLength=sizeof(SECURITY_ATTRIBUTES);
attr0.bInheritHandle=TRUE;
attr0.lpSecurityDescriptor=NULL;
attr1=attr0;
// Application local path log filename
logfile=ExtractFilePath(Application->ExeName)+"logfile.txt";
DeleteFileA(logfile); // delete old log just to be sure
// command line string to run (instead of "dir" use "java -jar decompile.jar -o Source/ file.jar")
s="cmd /c dir > \""+logfile+"\"";
CreateProcess(NULL,s.c_str(),&attr0,&attr1,TRUE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
// wait for execution with some timeout...
for (int i=0;i<100;i++)
{
if (FileExists(logfile)) break;
Sleep(100);
}
// copy the log into TMemo mm_log ...
if (FileExists(logfile)) mm_log->Lines->LoadFromFile(logfile); else mm_log->Text="No log file found";
Where mm_log is memo where I copy the log file. This example just run the dir command to show directory info... so instead use your JAVA ... as I suggested in rem. If you have older OS then instead of cmd use command. you can also use FileExists to determine which one is it ...
You need to use the Win32 CreateProcess() function instead of the C system() funtion. CreateProcess() allows you to redirect STDIN, STDOUT, and STDERR of the launched process.
Creating a Child Process with Redirected Input and Output
You can launch java.exe directly with the desired input parameters, redirecting its output using anonymous pipes created with CreatePipe(), and then read output from those pipe using ReadFile() until the process terminates. You can parse and display the output however you want.
As for hiding the command window, when calling CreateProcess(), you can either:
specify the CREATE_NO_WINDOW flag in the dwCreationFlags parameter of CreateProcess().
in the STARTUPINFO structure passed to the lpStartupInfo parameter of CreateProcess(), include the STARTF_USESHOWWINDOW flag in the STARTUPINFO::dwFlags field, and set the STARTUPINFO::wShowWindow field to SW_HIDE.
Try something like this:
HANDLE hStdErrRd = NULL;
HANDLE hStdErrWr = NULL;
SECURITY_ATTRIBUTES saAttr = {0};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hStdErrRd, &hStdErrWr, &saAttr, 0)) {
// error handling
}
if (!SetHandleInformation(hStdErrRd, HANDLE_FLAG_INHERIT, 0)) {
// error handling
}
String CmdLine = "\"C:\\path to\\java.exe\" -jar \"C:\\path to\\decompile.jar\" -o Source/ \"C:\\path to\\file.jar\"";
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = hStdErrWr;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
if (CreateProcess(NULL, CmdLine.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// must close our handle to the write-side of the pipe so the pipe will close
// when the child process terminates so ReadFile() will know to stop expecting data...
CloseHandle(hStdErrWr);
AnsiString output;
char buffer[1024];
DWORD dwBytesRead;
while (ReadFile(hStdErrRd, buffer, sizeof(buffer), &dwBytesRead, NULL))
{
if (dwNumRead == 0)
break;
output += AnsiString(buffer, dwBytesRead);
do
{
int pos = output.Pos("\n");
if (pos == 0)
break;
Memo1->Lines->Add(output.SubString(1, pos-1).TrimRight());
output.Delete(1, pos);
}
while (!output.IsEmpty());
}
if (!output.IsEmpty())
Memo1->Lines->Add(output.TrimRight());
}
else
{
// error handling
CloseHandle(hStdErrWr);
}
CloseHandle(hStdErrRd);

grep: input file ">":System cannot find the file specified

when I tried to execute a grep command in c++, I got the following error:
grep: Input file: ">":System cannot find the file specified
Can anyone help me to resolve this?
wchar_t* grepArg= L"\"D:\\grep\" -f \"C:\\Users\\ic014733\\AppData\\Local\\Temp\\patterns.tmp\" \"D:\\LOG2014_10_05.LOG\" >\"D:\\share\\result.txt\"";
if ( !CreateProcessW(NULL, grepArg, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
{
DWORD errorMessageID = ::GetLastError();
}
else
{
DWORD res = WaitForSingleObject(pi.hProcess, INFINITE);
TerminateProcess(pi.hProcess, 0);
PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0) ;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
You gave the answer in your comment : Error is like, grep considers '>' too as input file. Of course it does! Why should it not?
Let me explain a little: When you write grep -f strings_file file > result at a shell prompt (or cmd prompt under Windows), the shell parses the input line, indentifies the > redirection and processes the redirection:
open result file
start program grep with arguments (or command line for Windows) -f strings_file file and standard output redirected to result
But you are just starting the grep program with all the arguments in its command line, so no redirection can happen.
So what can you do? As none of your path contains spaces, you could just make cmd.exe do the redirection for you :
wchar_t* grepArg= L"cmd.exe /C \"D:\\grep -f C:\\Users\\ic014733\\AppData\\Local\\Temp\\patterns.tmp D:\\LOG2014_10_05.LOG > D:\\share\\result.txt";
That means that you start a new cmd.exe program with two parameters: /c saying execute that and your original command enclosed in quotes. But I never found the way to cleanly include quotes in quotes in Windows. That's the reason why I removed all the inner quotes from your command. It was possible because there was no space in any path.
The alternative would be to do the redirection by hand: open the output file, pass its handle in the hStdOutput member of si and declare it by setting STARTF_USESTDHANDLES in dwFlags member of same struct:
wchar_t* grepArg= L"\"D:\\grep\" -f \"C:\\Users\\ic014733\\AppData\\Local\\Temp\\patterns.tmp\" \"D:\\LOG2014_10_05.LOG\"";
HANDLE hstdout = CreateFile("D:\\share\\result.txt", "GENERIC_WRITE", 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// control hstdout is not null...
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdOutput = hstdout;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); // stdinput and stderr unchanged
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if ( !CreateProcessW(NULL, grepArg, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) ) {
...

pass filename from existing Process to new Process MFC c++

I'm trying to figure out how to pass filename from within an existing executable to a newly generated executable of same type & then the new exe load said file name. Following is something I'm working on but I'm bit lost really.
CString cstrExePathLoc;
GetModuleFileName(NULL, cstrExePathLoc.GetBuffer(MAX_PATH), MAX_PATH);
wchar_t szCommandLine[1024] = _T("C:\\Users\\Home\\Desktop\\testfile.tmp");
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
CreateProcess(
cstrExePathLoc, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&startupInfo, &processInfo
);
EDIT: this still doesn't open the file. A new ExeApp is started but no file is loaded. No errors generated at all.
I've searched net but no examples I've come across clearly explain how to do this. Any help would be appreciated. Thanks.
EDIT: simple solution here that's worked thanks to Robson Filho Colodeti below.
CString cstrExeFilePathAndFilePath2Open = cstrExePathLoc;
cstrExeFilePathAndFilePath2Open += L" \"";
cstrExeFilePathAndFilePath2Open += cstrFilePath2Open;
cstrExeFilePathAndFilePath2Open += L"\"";
CreateProcess(csExePath, cstrExeFilePathAndFilePath2Open.GetBuffer(0), NULL, NULL, TRUE, NULL, NULL, NULL, &sui, &pi);
Opening the other program
Using CreateProcess
you are in the right way, you can use the CreateProcess method.
BOOL fSuccess;
CString csDir = L"c:\your\working\directory\";
CString csParameters = L"parameter1 parameter2 parameter3 /parameter4=value";
CString csCommand = L"c:\folder\of\the\executable\executable.exe";
csCommand+= L" ";
csCommand+= csParameters;
// Create the child process.
fSuccess = CreateProcess(NULL, csCommand.GetBuffer(0), NULL, NULL, TRUE, 0, NULL,
csDir, &startupInfo, &processInfo);
Using ShellExecute
an easier way is to use the ShellExecute method because the create process method is a more "advanced" way to call a process since it gives you a lot of possibilities to control the results etc...
Reading the parameters inside the other program
then you will have to read these parameters from the other executable: check this thread