How to trim the space from the lpApplicationName of the CreateProcessW function? - c++

I am using CreateProcessW function to create a process. Into this function, passing the first parameter i.e., lpApplicationName with some spaces like as shown below:
In the below path (pszExePath) there are spaces because of this process is not creating.
pszExePath = L"C:\\Program Files\\Common Files\\Installer\\emrinst.exe";
I tried to trim the space by using the below lines but still i am facing the issue.
pszExePath = L"\"";
pszExePath += L"C:\\Program Files\\Common Files\\Installer\\emrinst.exe";
pszExePath += L"\"";
How to trim the space from the lpApplicationName of the CreateProcessW function?
Below is the updated code:
pszExePath = L"C:\\Program Files\\Common Files\\Installer\\emrinst.exe";
strCommandLine = "C:\\Testfolder\\Program Files\\Common Files\\Apps\\emarmain\\emr-test_folder\\Millinnium Files\\test\\test.inf";
std::wstring strFullPath = L"";
strFullPath += pszExePath;
strFullPath += pszCmdLine;
dwExitCode = ::CreateProcessW(NULL, (LPWSTR)strFullPath.c_str(),
0, 0, FALSE, NORMAL_PRIORITY_CLASS, 0,
pszCurrentDirectory, &si, &pi);
still I am getting the error, I think it is exceeding the size of the second parameter "lpCommandLine" of 32,768 characters. Is there any way to increase the size? And also is my code snippet is correct?

As you have no doubt read in the documentation:
If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin; otherwise, the file name is ambiguous.
So you've got that part right, but you can't concatenate C strings like that. Instead, you can do:
#include <string>
std::wstring cmdline = L"\"";
cmdline += L"C:\\Program Files\\Common Files\\Installer\\emrinst.exe";
cmdline += L"\"";
CreateProcess (NULL, &cmdline [0], ...);
Alternatively, you can pass the application path as yhe first parameter of CreateProcess without quotes.

Following the MSDN documentation for CreateProcess (Unicode) and the code in the question:
1) Add double quotes to the EXE path and the command line.
2) Add whitespace between the pszExePath and strCommandLine.
3) Note: This solution requires a C++11 since CreateProcess requires a non-read only string as lpCommandLine parameter. For prior versions of C++, use _wcsdup to duplicate the string and call for free to release it.
std::wstring pszExePath = L"C:\\Program Files\\Common Files\\Installer\\emrinst.exe";
std::wstring strCommandLine = L"C:\\Testfolder\\Program Files\\Common Files\\Apps\\emarmain\\emr-test_folder\\Millinnium Files\\test\\test.inf";
std::wstring strFullPath = L"\"" + pszExePath + L"\"" + L" " + L"\"" + strCommandLine + L"\"";
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof si;
BOOL result = ::CreateProcessW(NULL, &strFullPath[0], 0, 0, FALSE, NORMAL_PRIORITY_CLASS, 0, pszCurrentDirectory, &si, &pi);
if (result)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Edit:
Alternatively, you could try ShellExecuteW (pay attention to double quotes in both paths).
std::wstring pszExePath = L"\"C:\\Program Files\\Common Files\\Installer\\emrinst.exe\"";
std::wstring strCommandLine = L"\"C:\\Testfolder\\Program Files\\Common Files\\Apps\\emarmain\\emr-test_folder\\Millinnium Files\\test\\test.inf\"";
ShellExecute(0, L"open", pszExePath.c_str(), strCommandLine.c_str(), pszCurrentDirectory, SW_NORMAL);

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

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

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

Running command-line commands in the background without opening a window

I'm running some command line script to encrypt a file using AxCrypt software.
My code compiles and runs fine, but when I check the file afterward, it has not been encrypted.
Here is the code:
LPCWSTR loc = L"C:\\\\Axantum\\\\AxCrypt\\\\AxCrypt";
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcessW(loc, const_cast<LPWSTR>(master), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
The master variable contains the commands, loc contains the location of the AxCrypt program.
LPCWSTR axLocation = L" C:\\\\Axantum\\\\AxCrypt\\\\AxCrypt ";
LPCWSTR flags = L" -b 2 -e -k ";
LPCWSTR passcode = L" \"***************\" ";
LPCWSTR command = L" -z ";
LPCWSTR imagelocation = L"%pathName";
std::wstring mast = std::wstring(axLocation) + flags + passcode + command + imagelocation;
LPCWSTR master = mast.c_str();
LPCWSTR loc = L"C:\\\\Axantum\\\\AxCrypt\\\\AxCrypt";
This is wrong. You have too many slashes in your literal. It needs to be this instead:
LPCWSTR loc = L"C:\\Axantum\\AxCrypt\\AxCrypt";
Same goes with axLocation.
Also, you have unnecessary spaces in your various substrings. Ultimately, you are creating this master command line, from the interpreter's perspective:
C:\\Axantum\\AxCrypt\\AxCrypt -b 2 -e -k "***************" -z %pathName
When it should look more like this:
C:\Axantum\AxCrypt\AxCrypt -b 2 -e -k "***************" -z %pathName
Try something more like this instead:
LPCWSTR axLocation = L"C:\\Axantum\\AxCrypt\\AxCrypt";
LPCWSTR flags = L"-b 2 -e -k";
LPCWSTR passcode = L"***************";
LPCWSTR command = L"-z";
LPCWSTR imagelocation = L"%pathName";
WCHAR master[STRSAFE_MAX_CCH];
StringCchPrintfW(master, STRSAFE_MAX_CCH, L"\"%s\" %s \"%s\" %s %s", axLocation, flags, passcode, command, imagelocation);
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcessW(axLocation, master, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
Lastly, %pathname looks suspicious. Is that supposed to be specified as-is like that? Or is it supposed to be an environment variable instead? If the latter, you are missing a percent sign:
%pathname%
CreateProcess() does not interpret environment variables, so if you need to pass such a variable then you would have to either:
resolve the variable yourself using ExpandEnvironmentStrings() before calling CreateProcess().
have CreateProcess() launch cmd.exe /C and let it interpret environment variables for you, eg:
StringCchPrintfW(master, STRSAFE_MAX_CCH, L"cmd /C \"%s\" %s \"%s\" %s %s", axLocation, flags, passcode, command, imagelocation);

Using wide strings with ifstream::open or multibyte strings with CreateProcess

I have a piece of code in which I need to use a string with both ifstream::open and CreateProcess, something like
//in another file
const char* FILENAME = "C:\\...blah blah\\filename.bat";
// in main app
std::ifstream is;
is.open(FILENAME);
// ...do some writing
is.close();
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
std::string cmdLine = "/c " + FILENAME;
if( !CreateProcess( "c:\\Windows\\system32\\cmd.exe",
cmdLine.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
{
return GetLastError();
}
CreateProcess requires a LPCWSTR, so to use the string with CreateProcess I would need to declare the filename and 'cmdLine' as std::wstring, but ifstream::open doesn't take wide strings...I can't figure a way to get around this. I always seem to run into problems with unicode vs multibyte strings.
Any ideas?
Thanks.
I'm assuming you defined UNICODE. You can change STARTUPINFO to STARTUPINFOA and CreateProcess to CreateProcessA and it should work fine (it did for me).
I don't think it'll like the + operation though. Explicitly convert one char array to a string.
std::string cmdLine = (std::string)"/c " + FILENAME;
Finally, you're going to need quotes around the beginning and end of FILENAME if it has a space.
const char FILENAME = "\"C:\\Program Files\\Company\\Program\\program.exe\"";
^ ^ ^
Edit:
Try putting this below your string declaration:
char charCmdLine [MAX_PATH + 3]; //"/c " is 3 extra chars
strncpy (charCmdLine, cmdLine.c_str(), MAX_PATH + 3);
Then, use charCmdLine in CreateProcess instead of cmdLine.c_str().