How can I embed a bat file in my project? - c++

Ive been using the following code to start a bat script.
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
std::wstring env = GetEnvString();
env += L"myvar=boo";
env.push_back('\0'); // somewhat awkward way to embed a null-terminator
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
wchar_t cmdline[] = L"cmd.exe /C C:\\Users\\jrowler\\Desktop\\test\\startsimulator.bat";
if (!CreateProcess(NULL, cmdline, NULL, NULL, false, CREATE_UNICODE_ENVIRONMENT,
(LPVOID)env.c_str(), NULL, &si, &pi))
{
std::cout << GetLastError();
abort();
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
This actually works perfectly, but I was wondering if it is possible to take that bat file and somehow include it in my project? Eventually this project will be distributed to a few different people, and I would like it to be set up in such a way that it does not require the user to download a .bat seperately and make sure it stays in the correct location.

You can write the file out using WriteFile. Since you're using C++ you can eschew the <strsafe.h> function I used in my code (I'm used to C) and build an std::wstring containing the file path with standard string operations, and then use the c_str() method to pass the first argument to CreateFile.
char batContent[] = "#echo off\r\necho Hello World\r\n";
wchar_t temp[MAX_PATH], path[MAX_PATH];
GetEnvironmentVariableW(L"Temp", temp, MAX_PATH);
int len = strlen(batContent);
DWORD dwWritten;
StringCchPrintfW(path, MAX_PATH, L"%s\\filename.bat", temp);
HANDLE hFile = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
// handle error
}
WriteFile(hFile, batContent, len, &dwWritten, NULL);
CloseHandle(hFile);
// Call CreateProcess with your existing code

Related

How can i make createprocess reindirect input success, I have successully get the reindirect out put result

I searched a lot on internet, but most of them are talking about reindirect output. No one gives any successful reindirect input example. For my codes below, it gave right output when I ran command "ipconfig" or "192.168.0.10" because child process ends after running these commands, no input needed. But when I ran command "ftp" instead of "ipconfig" child process which is console is waiting for the next input command. And I tried to write 11111 as input to console in this case as you can see. However console did not receive my input command and waiting for the input command forever. How can I successfully response to "ftp" command in this program and keep console running
#include <windows.h>
#include <fstream>
using namespace std;
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpComLine,
int nCmdShow)
{
SECURITY_ATTRIBUTES secAttr;
HANDLE hRead,hWrite;
char command[256];
char testBuf[256] = {0};
strcpy(command, "ipconfig");
// strcpy(command, "ping 192.168.0.10")
// strcpy(command, "ftp");
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.lpSecurityDescriptor = NULL;
secAttr.bInheritHandle = TRUE;
HANDLE hTxtFile = CreateFile("tmp.txt", GENERIC_ALL, 0, &secAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hTxtFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error createfile", NULL, MB_OK);
return 0;
}
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_WRITE, 0, &secAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hWriteFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error createWritefile", NULL, MB_OK);
return 0;
}
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
startupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfo(&startupInfo);
startupInfo.hStdError = hTxtFile;
startupInfo.hStdOutput = hTxtFile;
startupInfo.hStdInput = hWriteFile;
startupInfo.wShowWindow = SW_SHOW;
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
char output[10240] = {0};
DWORD bytesRead;
if (!CreateProcess(NULL, command,NULL,NULL,TRUE,NULL,NULL,NULL,&startupInfo,&processInfo))
{
MessageBox(NULL, "Error createprocess", NULL, MB_OK);
CloseHandle(hWrite);
CloseHandle(hRead);
return FALSE;
}
DWORD processExitCode = 0;
strcpy(testBuf, "11111\r\n");
while (GetExitCodeProcess(processInfo.hProcess, &processExitCode))
{
WriteFile(hWriteFile, testBuf, 7, &bytesRead, NULL);
if (processExitCode != STILL_ACTIVE)
{
// MessageBox(NULL, "End process", NULL, MB_OK);
break;
}
Sleep(1000);
}
SetFilePointer(hTxtFile, NULL, NULL, FILE_BEGIN);
ReadFile(hTxtFile, output, 10240, &bytesRead, NULL);
CloseHandle(hTxtFile);
MessageBox(NULL, output, NULL, MB_OK);
return 0;
}
Redirecting (not "reindirecting") input works just the same as redirecting output. Of course, the flow of data is in the opposite direction. This means that the process reads from the file. This in turn means when you open a handle for writing, as you do in the example code:
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_WRITE, ...);
the process will not be able to read from it. You must open the file for reading:
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_READ, ...);
But then, this also means that you must prepare the input that you want to send down to the process in advance. It does not help to write to the file after you have created the process.
If you do not know the data that you have to send to the process in advance, you cannot use a file for standard input, but you must use something else, such as a (named or anonymous) pipe.
you must redirect the console output and then write the buffer into file
1)
/* Create a pipe for the child process's STDOUT */
if(!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
BAIL_OUT(-1);
2)
/* Duplicate the pipe HANDLE */
if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
BAIL_OUT(-1);
3)
CHAR chBuf[BUFSIZE];
DWORD dwRead;
DWORD dwAvail = 0;
if (!PeekNamedPipe(hChildStdoutRdDup, NULL, 0, NULL, &dwAvail, NULL) || !dwAvail)
return;
if (!ReadFile(hChildStdoutRdDup, chBuf, min(BUFSIZE - 1, dwAvail), &dwRead, NULL) || !dwRead)
return;
chBuf[dwRead] = 0;
Please find more details here:
https://www.codeproject.com/Articles/5531/Redirecting-an-arbitrary-Console-s-Input-Output

Unable to redirect standard output using CreateProcess

I'm maintaining an MFC program and I'm launching a simple Win32 console program (just a "Hello World" program, source below) with CreateProcess and I'm unable to redirect the standard output of that program to a file.
This is the launching code, don't bother about the Fatal function, it just
displays a message and aborts the program, this is only test code.
HANDLE hfile = CreateFile("output.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE)
{
Fatal("Fatal error: CreateFile");
}
static const char TestText[] = "Test\r\n";
DWORD written;
if (!WriteFile(hfile, "Test\r\n", sizeof(TestText) - 1, &written, NULL))
{
Fatal("Fatal error: CreateProcess");
}
STARTUPINFO startupinfo = {0};
startupinfo.cb = sizeof(STARTUPINFO);
startupinfo.lpTitle = "Some Title";
startupinfo.dwFlags = STARTF_USESTDHANDLES;
startupinfo.hStdOutput = hfile;
PROCESS_INFORMATION processInfo;
if (!CreateProcess("S:\\Git\\TestRedirect\\TestConsole1\\Debug\\TestConsole1.exe", "cmdline", NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupinfo, &processInfo))
{
Fatal("Fatal error: CreateProcess");
}
if (WaitForSingleObject(processInfo.hProcess, 10000) != WAIT_OBJECT_0)
{
Fatal("Fatal error: WaitForSingleObject");
}
if (!CloseHandle(hfile))
{
Fatal("Fatal error: CloseHandle");
}
It almost works as expected:
it opens "output.txt"
it writes "Test\r\n" into "output.txt"
it launches TestConsole1.exe
the console window of TestConsole1.exe does not display "Hello Word", which is expected because the standard output is supposed to be redirected into "output.txt"
WaitForSingleObject waits for TestConsole1.exe to be completed
CloseHandle closes "output.txt"
Now I expect "output.txt" to contain this:
Test
Hello World!
but actually it's content is
Test
Source code of TestConsole1.exe:
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
Sleep(2000); // wait 2 seconds so I can see what happens
return 0;
}
your hfile is not inheritable - you need use SECURITY_ATTRIBUTES in call CreateFile
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
HANDLE hfile = CreateFile("output.txt", GENERIC_WRITE, 0, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

CreateProcess execute EXE

I have an application where the user uploads a file to the remote server, the same server to receive this file should run this application. I'm using the CreateProcess method. The problem is, the file directory is already defined in a std :: string, and I'm having difficulties to pass this directory as a parameter to the CreateProcess.
How do I that this directory can be passed to the CreateProcess without errors?
//the client remotely sends the directory where the file will be saved
socket_setup.SEND_BUFFER("\nRemote directory for upload: ");
char *dirUP_REMOTE = socket_setup.READ_BUFFER();
std::string DIRETORIO_UP = dirUP_REMOTE; // variable where it stores the remote directory
//after uploading this is validation for executing file
if (!strcmp(STRCMP_EXECUTE, EXECUTE_TIME_YES))
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
std::wstring wdirectory;
int slength = (int)directory.length() + 1;
int len = MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, 0, 0);
wdirectory.resize(len);
MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, &wdirectory[0], len);
if (!CreateProcess(NULL, wdirectory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
}
There are two versions of CreateProcess: CreateProcessA and CreateProcessW (like most similar windows APIs). The right version is used depending on whether you have Unicode enabled.
Here you need to convert your std::string to a std::wstring first, because that CreateProcess is actually a CreateProcessW.
std::wstring wdirectory;
int slength = (int)directory.length() + 1;
int len = MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, 0, 0);
wdirectory.resize(len);
MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, &wdirectory[0], len);
//...
if (!CreateProcess(NULL,(LPWSTR)wdirectory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
You could also try replacing CreateProcess by a manual call to CreateProcessA and passing the cstring like you tried to do in the question, but then you won't support wide characters :
if (!CreateProcessA(NULL, directory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));

File re-direction operator ">" doesn't work with CreateProcess() API

I've a SupportApp.EXE which if I launch manually from windows CMD prommpt like this ::
SupportApp.EXE -t 100 > AFile.csv
works perfcetly fine & it generates a CSV file for me.
Now I want to automate same thing inside a VC++ code.
So, I use CreateProcess() API for this.
Code snippet below ::
TCHAR launcher[512];
_tgetcwd(launcher, _MAX_PATH);
TCHAR workDir[512];
_tgetcwd(workDir, _MAX_PATH);
_tcscat(launcher, "\\App\\SupportApp.exe");
TCHAR cmdlineoption[512];
_tcscpy(cmdlineoption, " -t 120 > AFile.csv");
LPTSTR appPath = (LPTSTR)cmdlineoption;
STARTUPINFO sInfo;
memset(&sInfo, 0, sizeof(sInfo));
sInfo.cb = sizeof(sInfo);
sInfo.dwFlags = STARTF_USESHOWWINDOW;
sInfo.wShowWindow = SW_SHOWMAXIMIZED;
PROCESS_INFORMATION pInfo;
memset(&pInfo, 0, sizeof(pInfo));
if (!CreateProcess(launcher, appPath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, workDir, &sInfo, &pInfo))
{
... // log error
}
// success
I see that CreateProcess() API succeeds and also i see that the -t 120 option I'm giving is also taken by this "SupportApp.exe"
BUT the file redirection operator ">" is not working with CreateProcess() API.
Instead the output is directed to CMD itself. But I want output to be sent to a CSV file.
Can anyone please help me in how do I redirect the output of my
"SupportApp.exe" to a file using CreateProcess() API from within my
VC++ code ?
UPDATE 2:
The inputs given by reviewers are incorporated in this & the modified code snippet is below which takes the file hnadle in STARTUPINFO structure as follows::
The file is getting created but the file is empty & it doesn't have any contents from createProcess()?
TCHAR launcher[512];
_tgetcwd(launcher, _MAX_PATH);
TCHAR workDir[512];
_tgetcwd(workDir, _MAX_PATH);
_tcscat(launcher, "\\App\\SupportApp.exe");
TCHAR cmdlineoption[512];
_tcscpy(cmdlineoption, " -t 120 > AFile.csv");
LPTSTR appPath = (LPTSTR)cmdlineoption;
STARTUPINFO sInfo;
memset(&sInfo, 0, sizeof(sInfo));
sInfo.cb = sizeof(sInfo);
sInfo.dwFlags |= STARTF_USESTDHANDLES; //newly added
sInfo.wShowWindow = SW_SHOWMAXIMIZED;
PROCESS_INFORMATION pInfo;
memset(&pInfo, 0, sizeof(pInfo));
sInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
sInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
HANDLE hn;
if(INVALID_HANDLE_VALUE != (hn = CreateFile(L"DoneDone.csv", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)))
{
sInfo.hStdOutput = =hn;
}
if (!CreateProcess(launcher, appPath, NULL, NULL, FALSE, 0, NULL, workDir, &sInfo, &pInfo))
{
... // log error
}
// success
Output redirection is a shell feature, i.e. the shell sets that up before starting the child.
You're not using a shell, instead going directly to the kernel asking it to start a process, so you don't get that service.
You need to set up the required redirection yourself. This is done in the STARTUPINFO's hStdOutput member. See the documentation, of course.
That's because the redirection operations (as well as the pipe operation) is part of the command prompt program, not part of the CreateProcess call.
However, you can do exactly what the command prompt program does when it does redirection, and set the file handles in the STARTUPINFO structure.

C++ CreateProcess - System Error #2 can't find file - what is wrong with my file path?

I am trying to open a PDF via Firefox with CreateProcess(), I am a beginner and know nothing about using CreateProcess, but in my last question someone pointed out the MSDN on it... it shows that:
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.
Therefore I created a batch file that runs perfectly fine with the system() command, there are no problems with the batch file.
I can't figure out why the system can't find the file and I don't know if its the batch file, the exe in the batch file, the PDF doc in the batch file or the location of cmd.exe... Any help is greatly appreciated...
void openPDF(char scansQueue[][MAX_NAME], int index)
{
// build batch file
char openPath[300];
char procCommand[MAX_NAME]="C:\\firefox";
char cmdEXE[MAX_NAME]="C:\\Windows\\System32\\cmd.exe";
fstream outfile;
outfile.open("C:\\firefox.bat");
copyCString(openPath,"\"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe\"");
outfile << openPath;
outfile << ' ';
copyCString(openPath,"\"C:\\Scans\\");
catArray(openPath,scansQueue[index]);
catArray(openPath,"\"");
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
cout<<"PROCESS ATTEMPT"<<endl;
if(!CreateProcess((LPCTSTR)cmdEXE ,(LPWSTR)procCommand, NULL, NULL, false, 0, NULL, NULL, &si, &pi))cout << GetLastError();cout<<"PROCESS FAILED TO EXECUTE!!!";
}
This assumes the whole batch file thing is part of an XY problem, in that you don't really need to make a batch file, you really just want to launch Firefox with a command line parameter.
I also assume you don't really need to pass the whole array of filenames with an index for which to use, instead you should just pass the filename by itself as I did where I called the function.
#include <Windows.h>
#include <stdio.h>
void MsgBoxLastError()
{
LPWSTR lpMsgBuf = NULL;
if(FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpMsgBuf,
0, NULL ) != 0)
{
MessageBox(NULL, lpMsgBuf, L"Error", MB_OK);
}
LocalFree(lpMsgBuf);
}
void OpenWithFirefox(const char* Filename)
{
const WCHAR pathToFirefox[] = L"C:/Program Files (x86)/Mozilla Firefox/firefox.exe";
const WCHAR scanPrefix[] = L"file://C:/Scans/";
WCHAR fullCommandLine[MAX_PATH] = {0};
//Build full command line
swprintf_s(fullCommandLine, L"\"%s\" \"%s%S\"", pathToFirefox, scanPrefix, Filename);
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
BOOL success = CreateProcess(NULL, fullCommandLine, NULL, NULL, false, 0, NULL, NULL, &si, &pi);
if(success)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
MsgBoxLastError();
}
}
int main()
{
const int MAX_NAME = 13;
char scansQueue[][MAX_NAME] =
{
"file1.pdf",
"file2.pdf"
};
for(int i = 0; i < 2; ++i)
{
OpenWithFirefox(scansQueue[i]);
}
return 0;
}