Convert argument to LPWSTR CreateProcess - c++

I am trying to perform some actions using cmd.exe but I want to hide cmd.exe. When I tried to use full path instead of cmd.exe I always get this error:
char Process[] = "C:\\WINDOWS\\System32\\cmd.exe";
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
sinfo.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
sinfo.hStdInput = sinfo.hStdOutput = sinfo.hStdError = (HANDLE)mySocket;
CreateProcess(NULL, Process, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo);
WaitForSingleObject(pinfo.hProcess, INFINITE);
CloseHandle(pinfo.hProcess);
CloseHandle(pinfo.hThread);
I always get:
CreateProcessW(LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION)': cannot convert argument 2 from 'char [28]' to 'LPWSTR' ConsoleApplication1

You are passing a narrow character array instead of a wide character array.
Change your project's character encoding setting to MultiByte instead of Unicode so that CreateProcess uses CreateProcessA instead of CreateProcessW.
Or, use wchar_t (or WCHAR, which is a typedef available in Windows for wchar_t) instead of char:
wchar_t Process[] = L"C:\\WINDOWS\\System32\\cmd.exe";
Or, you can change the code to use CreateProcessA manually:
char Process[] = "C:\\WINDOWS\\System32\\cmd.exe";
...
CreateProcessA(NULL, Process, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo);
...

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

CreateProcessA not working or returning any errors

I've tried to used both CreateProcessA and CreateProcess to create a instance of notepad, but to no success. CreateProcess always returns an error code of 2 when I run it, but CreateProccessA doesn't return anything at all.
This is what I have so far:
STARTUPINFOA startInfo;
PROCESS_INFORMATION processInfo;
ZeroMemory(&startInfo, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
ZeroMemory(&processInfo, sizeof(processInfo));
if (CreateProcessA(NULL, NULL,NULL,NULL,FALSE,NULL, NULL, "C:\\Windows\\notepad.exe", &startInfo, &processInfo)) {
DWORD Error = GetLastError();
MessageBoxA(NULL, "FAILED", "FAILED", MB_OK);
printf("%d", Error);
return 1;
}
Error 2 is ERROR_FILE_NOT_FOUND. You are passing the path to notepad.exe in the lpCurrentDirectory parameter, but it needs to be passed in the lpApplicationName or lpCommandLine parameter instead:
CreateProcessA("C:\\Windows\\notepad.exe", NULL, NULL, NULL, FALSE, NULL, NULL, NULL, &startInfo, &processInfo)
CreateProcessA(NULL, "C:\\Windows\\notepad.exe", NULL, NULL, FALSE, NULL, NULL, NULL, &startInfo, &processInfo)
Also, you are calling GetLastError() when CreateProcessA() is successful. You need to call it when CreateProcessA() fails instead:
if (!CreateProcessA(...)) { // <-- note the !
DWORD Error = GetLastError();
...
}
Lastly, the %d specifier of printf() expects an int, not a DWORD. Use %ul instead, which expects an unsigned long, which is what DWORD is defined as:
printf("%ul", Error);

TCHAR Array to a concatenate LPCSTR

I am reading a ini file and want to execute a external program (VBS File) after that. But I am having problems with the string types.
This is my code.
LPCTSTR path = _T(".\\my.ini");
TCHAR fileName[500];
int b = GetPrivateProfileString(_T("Paths"), _T("filename"), _T(""), fileName, 500, path);
// fileName = myscript.vbs
// I need to execute "wscript myscript.vbs arg1"
// Execute script file. Doesnt work.
WinExec("wscript " + fileName + " arg1", MINIMZIED);
// OR. Doesnt work.
system("wscript " + fileName + " arg1");
This doesnt work. WinExec wants a LPCSTR but I have the fileName in a TCHAR[] and want to concatenate with some other string.
How can I convert or concatenate it correctly?
From the WinExec() documentation:
This function is provided only for compatibility with 16-bit Windows. Applications should use the CreateProcess function.
Which is CreateProcessW() in your case.
Alternatively, you can use _wsystem().
You need to concatenate the strings using another buffer, for example:
LPCTSTR path = _T(".\\my.ini");
TCHAR fileName[500];
TCHAR command[520];
int b = GetPrivateProfileString(_T("Paths"), _T("filename"), _T(""), fileName, 500, path);
_stprintf_s(command, 520, _T("wscript %.*s arg1"), b, filename);
Then you can use command as needed, eg:
STARTUPINFO si = {};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_MINIMIZED;
PROCESS_INFORMATION pi = {};
if (CreateProcess(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
...
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Or:
#ifdef UNICODE
#define system_t(cmd) _wsystem(cmd)
#else
#define system_t(cmd) system(cmd)
#endif
system_t(command);

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

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