CreateProcess execute EXE - c++

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));

Related

why isn't CreateProcessW() performing the Command provided?

To present the minimal reproducible code I wrote a code to delete a file from a given location using CreateProcessW(). The file does not get deleted. Some help would be really useful in knowing why this isn't working.
dprintf(("Error %d", GetLastError()));
STARTUPINFO si = { sizeof(STARTUPINFO), 0 };
si.cb = sizeof(si);
PROCESS_INFORMATION pi = { 0 };
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe";
string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
define TRANSCRIPT_LOCATION "C:\Users\Administrator\Desktop\" this is the location of the file to be deleted
GetLastError() keeps returning 50(ERROR_NOT_SUPPORTED) and the value of res = 1
My first thought is that
LPWSTR Command = new WCHAR[bstr.length()];
is not right. Perhaps
LPWSTR Command = new WCHAR[bstr.length() + 1];
will work. A better alternative is to use wchars_num for allocating memory.
instead of
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
use
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
LPWSTR Command = new WCHAR[wchars_num];
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
A second issue is that perhaps you missed a space character when composing the del command.
string bstr = "C:\\Windows\\System32\\cmd.exe /C del " + trans_loc + "a.rtf";
// ^^
I see a number of problems with your code:
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe"; does not compile in C++11 and later. You need to (and should) use LPCWSTR instead, since a string literal is const data, and LPCWSTR is a pointer to const WCHAR data, but LPWSTR is a pointer to non-const WCHAR data.
In string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";, you are missing a required space character between the del command and the filename to delete.
In LPWSTR Command = new WCHAR[bstr.length()];, you are not allocating enough space for a null terminator. Also, you should not be using bstr.length() for the converted length anyway, because there is no guarantee that the converted string will not be larger than the original string. You should call MultiByteToWideChar() one time with a NULL output buffer to calculate the actual converted length (which you ARE doing), THEN allocate the memory (which you are NOT doing - you are allocating too soon!), THEN call MultiByteToWideChar() again to do the actual conversion.
You are leaking the allocated memory (you are not calling delete[] Command;). I would suggest using std::wstring or std::vector<WCHAR> instead of new WCHAR[].
You say that res is being set to 1, which means CreateProcessW() is actually successful in running cmd.exe (now, whether cmd.exe is successful in executing your command is a different matter - use GetExitCodeProcess() to find that out), and thus the return value of GetLastError() is meaningless! It is certainly meaningful to call GetLastError() before calling CreateProcessW()
You are calling WaitForSingleObject() regardless of whether CreateProcessW() succeeds or fails.
Try this instead:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
std::string bstr = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
{
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
}
else
{
std::vector<WCHAR> Command(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), Command.data(), wchars_num);
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessW Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
Or, if you are using Windows 10 build 17035 or later and have enabled the "Beta: Use Unicode UTF-8 for worldwide language support" option in your Windows settings (or, if trans_loc does not contain any non-ASCII, non-user-locale characters), then no MultiByteToWideChar() conversion is needed at all:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
std::string Command = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
if (!CreateProcessA(nullptr, const_cast<char*>(Command.c_str()), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessA Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
That being said, a better option would be to simply use std::wstring instead of std::string to begin with:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = L"C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + L"a.rtf\"";
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessW Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Of course, the simplest solution would be to just not use cmd.exe / C del at all, but instead use DeleteFileW():
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = trans_loc + L"a.rtf";
if (!DeleteFileW(bstr.c_str()))
{
dprintf(("DeleteFileW Error %d", GetLastError()));
}
Or, if you insist on using a UTF-8 encoded std::string:
std::string bstr = trans_loc + "a.rtf";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
{
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
}
else
{
std::vector<WCHAR> wstr(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), wstr.data(), wchars_num);
if (!DeleteFileW(wstr.c_str()))
{
dprintf(("DeleteFileW Error %d", GetLastError()));
}
}
Or, if you are using Windows 10 with UTF-8 support enabled (or, if trans_loc does not contain any non-ASCII, non-user-locale characters):
std::string bstr = trans_loc + "a.rtf";
if (!DeleteFileA(bstr.c_str()))
{
dprintf(("DeleteFileA Error %d", GetLastError()));
}

How can I embed a bat file in my project?

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

Executing a program on PocketPC Today Items doesn't work

I have the following problem. I am using a DLL to add a new menu item for the Today Item List on a PocketPC 2002. I have tested the written DLL on a Windows Mobile 6.5.3 an it works on there.
On the PocketPC the path contains a space and I guess that I don't escape the string right. Consider the following snippet where the problem occurs:
char commandline[100];
strcpy(commandline, "\\SDMMC Disk\\Test\\Test.exe");
STARTUPINFO si = { sizeof(si) };
int len;
int slength = (int)strlen(commandline) + 1;
len = MultiByteToWideChar(CP_ACP, 0, commandline, slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, commandline, slength, buf, len);
PROCESS_INFORMATION pinfo;
::CreateProcess(buf, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pinfo);
Now I was trying to escape the path to the executable like this:
strcpy(commandline, "\"\\SDMMC Disk\\Test\\Test.exe"");
But unfortunately this does not work. Did I escape the string in a wrong way?
I'd appreciate any help, thank you.

How to convert std::wstring to LPCTSTR in C++?

I have Windows registry key value in wstring format. Now I want to pass it to this code (first argument - path to javaw.exe):
std::wstring somePath(L"....\\bin\\javaw.exe");
if (!CreateProcess("C:\\Program Files\\Java\\jre7\\bin\\javaw.exe", <--- here should be LPCTSTR, but I have a somePath in wstring format..
cmdline, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
0, // Set handle inheritance to FALSE.
CREATE_NO_WINDOW, // ON VISTA/WIN7, THIS CREATES NO WINDOW
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\n");
return 0;
}
How can I do that?
Simply use the c_str function of std::w/string.
See here:
http://www.cplusplus.com/reference/string/string/c_str/
std::wstring somePath(L"....\\bin\\javaw.exe");
if (!CreateProcess(somePath.c_str(),
cmdline, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
0, // Set handle inheritance to FALSE.
CREATE_NO_WINDOW, // ON VISTA/WIN7, THIS CREATES NO WINDOW
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\n");
return 0;
}
LPCTSTR is an old relic. It's a hybrid typedef that either defines char* if you are using multi-byte strings or wchar_t* if you are using Unicode. In Visual Studio, this can be changed in general project's settings under "Character Set".
If you are using Unicode, then:
std::wstring somePath(L"....\\bin\\javaw.exe");
LPCTSTR str = somePath.c_str(); // i.e. std::wstring to wchar_t*
If you are using multi-byte, then use this helper:
// wide char to multi byte:
std::string ws2s(const std::wstring& wstr)
{
int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), 0, 0, 0, 0);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), &strTo[0], size_needed, 0, 0);
return strTo;
}
i.e. std::wstring to std::string that will contain multi-byte string and then to char*:
LPCTSTR str = ws2s(somePath).c_str();
The safest way when interacting from stdlib classes with TCHARs is to use std::basic_string<TCHAR> and surround raw strings with the TEXT() macro (since TCHAR can be narrow and wide depending on project settings).
std::basic_string<TCHAR> somePath(TEXT("....\\bin\\javaw.exe"));
Since you won't win style contests doing this ... another correct method is to use explicitly the narrow or wide version of a WinAPI function. E.g. in that particular case:
with std::string use CreateProcessA (which uses LPCSTR which is a typedef of char*)
with std::u16string or std::wstring use CreateProcessW (which uses LPCWSTR which is a typedef of wchar_t*, which is 16-bit in Windows)
In C++17, you could do:
std::filesystem::path app = "my/path/myprogram.exe";
std::string commandcall = app.filename.string() + " -myAwesomeParams";
// define si, pi
CreateProcessA(
const_cast<LPCSTR>(app.string().c_str()),
const_cast<LPSTR>(commandcall.c_str()),
nullptr, nullptr, false, CREATE_DEFAULT_ERROR_MODE, nullptr, nullptr,
&si, &pi)
Finally decided to use CreateProcessW as paulm mentioned with a little corrections - values need to be casted (otherwise I get error):
STARTUPINFOW si;
memset(&si, 0, sizeof (STARTUPINFOW));
si.cb = sizeof (STARTUPINFOW);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = FALSE;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof (PROCESS_INFORMATION));
std::wstring cmdline(L" -jar install.jar");
if (!CreateProcessW((LPCWSTR)strKeyValue.c_str(),
(LPWSTR)cmdline.c_str(), // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
0, // Set handle inheritance to FALSE.
CREATE_NO_WINDOW, // ON VISTA/WIN7, THIS CREATES NO WINDOW
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\n");
return 0;
}

Small issue with creating a process ( CreateProcess or ShellExecuteEx) with parameters

Related question: CreateProcess doesn't pass command line arguments.
Is there a difference between passing an argument vs. passing a parameter to an EXE when using CreateProcess (and/or ShellExecuteEx)?
I'm trying to call something like:
myExe.exe /myparam
with the code like :
TCHAR Buffer[MAX_PATH];
DWORD dwRet;
dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
CString sCmd;
sCmd.Format ( "%s\\%s", Buffer, command);
CString sParam( "/myparam" );
sCmd += " " + sParam;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (CreateProcess( NULL, sCmd.GetBuffer() , NULL, NULL, TRUE, 0, NULL, Buffer, &si, &pi))
{
::WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
LPVOID lpMsgBuf = NULL;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
CString msg;
msg.Format("Failed to start command line (error: %s) : %s\n",lpMsgBuf,sCmd);
AfxMessageBox(msg);
LocalFree(lpMsgBuf);
}
From what I understand from the other thread and MSDN is that it should be working properly and call the EXE with the parameter; doing the above code without adding the "/myparam" works like it should.
I've tried the EXE from the command line and from Explorer (by creating a shortcut and adding /myparam to the target name) and it's working alright.
Try this in case there are spaces in the path:
CString sCmd;
sCmd.Format ( "\"%s\\%s\"", Buffer, command);
Or else pass the parameters via the parameters argument.