CreateProcess can start a process, but QProcess cannot... why? - c++

I am writing a windows QT app that needs to launch other apps. If I use the following windows calls everything works fine:
QString qsExePath = "C:\\Program Files (x86)\\Some Company\\SomeApp.exe";
QString qsCommandLine = "";
DWORD dwLastError = 0;
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = (WORD)1;
PROCESS_INFORMATION processInfo;
ZeroMemory(&processInfo, sizeof(processInfo));
if (CreateProcess((TCHAR*)(qsExePath.utf16()), (TCHAR*)(qsCommandLine.utf16()),
NULL, NULL, FALSE, 0, NULL, NULL,
&startupInfo, &processInfo))
{
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else
{
dwLastError = GetLastError();
}
However, if I use the following QT calls it does not work and fails with QProcess::Unknown Error.
QProcess process;
bool bStarted = process.startDetached(qsExePath);
qDebug() << process.error();
I can get QProcess to work if copy SomeApp.exe to my %TMP% directory and change the qsExePath accordingly, so it is obviously some kind of permissions error. I don't understand why though... if it were really permissions, shouldn't my CreateProcess windows call fail?

Your path has spaces in it. You are calling the overloaded version of QProcess.startDetached() that takes a single parameter, so it interprets that as the complete command line to execute. As such, try wrapping the path in quotes, otherwise it will think that "C:\Program" is the program to execute and everything else are arguments, which would be wrong:
QString qsExePath = "\"C:\\Program Files (x86)\\Some Company\\SomeApp.exe\"";
bool bStarted = process.startDetached(qsExePath);
Alternatively, call one of the other overloaded versions of startDetached() and let it work out the necessary quoting internally for you:
QString qsExePath = "C:\\Program Files (x86)\\Some Company\\SomeApp.exe";
bool bStarted = process.startDetached(qsExePath, QStringList());

Related

32bit Application run 64bit registry (with WOW6432Node)

I have a 32bit application that must call C:\Windows\System32\regedit.exe, but instead it runs C:\Windows\SysWOW64\regedit.exe. How can I call the regedit in System32?
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe;
szExe = "regedit.exe";
if (CreateProcess("C:\\Windows\\Sysnative\\cmd.exe", szExe.GetBuffer(100), 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
WaitForSingleObject(pi.hProcess, IGNORE);// optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
The Condition returns True but regedit does not run.
Instead of SysNative, I put System32, but it does not work. In szExe, I put the string "C:\Windows\regedit", but it does not work. And so on ...
You are trying to run regedit.exe via cmd.exe, why? Any result you get will be for cmd.exe, regardless of what commands it executes.
Just run the Registry Editor instead of cmd.exe. However, there is a wrinkle.
The 64bit C:\Windows\System32 folder does not have a regedit.exe executable. The 64bit regedit.exe is located in C:\Windows instead. If a 32bit process tries to run that executable directly, the 32bit C:\Windows\SysWOW64\regedit.exe will be run, which you don't want.
In order for a 32bit process to run the 64bit regedit.exe, it needs to run C:\Windows\Sysnative\Regedt32.exe instead, which is a stub that will run the 64bit C:\Windows\regedit.exe:
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe = "C:\\Windows\\Sysnative\\Regedt32.exe";
if (CreateProcess(NULL, szExe.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE); // optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
If CreateProcess() fails with ERROR_ELEVATION_REQUIRED, and you don't want to run your 32bit process elevated, then try using ShellExecute/Ex() with the runas" verb to launch Regedt32.exe elevated.
That being said, you should not hard-code the path to the Windows installation folder. Ask Windows where it is actually installed, such as via GetWindowsDirectory(), SHGetFolderPath(CSIDL_WINDOWS), SHGetKnownFolderPath(FOLDERID_Windows), etc. And then you can append Sysnative\\Regedt32.exe to the end of that path.
To solve this problem I use ShellExecuteEx() and SHELLEXECUTEINFO.
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO Sei;
ZeroMemory(&Sei,sizeof(SHELLEXECUTEINFO));
Sei.cbSize = sizeof(SHELLEXECUTEINFO);
Sei.lpFile = "C:\\windows\\regedit.exe";
Sei.nShow = SW_SHOW;
Sei.fMask = SEE_MASK_INVOKEIDLIST;
Sei.lpVerb = "open";
ShellExecuteEx(&Sei);
if (result == S_OK || result == S_FALSE)
CoUninitialize();
For reference: Launching Applications

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

CreateProcess calling cmd.exe incl. arguments with no showing (flashing) window?

The saga continues...
I've searched the web, i've searched on StackOverflow, i found many hope giving answers/solutions, but somehow they have all failed (up)on me (including the ones related to ShellExecute(Ex) ).
How to hide a (flashing) CMD window (incl. arguments) using CreateProcess??
I basically want to call/execute a set of conditional/native cmd.exe commands (i.e. FOR /F, and ||), but also an external command FIND(STR).exe. And this, without showing a (flashing) CMD window.
But even hiding something as simple as "cmd.exe /C ECHO ...flashing window is bad..." seems impossible to do.
The code i've tried (including many variations related to the dwFlags and wShowWindow flags
#include <windows.h>
int main()
{
char cmdline[] = "cmd.exe /c ECHO ...flashing window is bad...";
PROCESS_INFORMATION pi;
STARTUPINFO si;
// memset(&si,0,sizeof(STARTUPINFO));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// si.dwFlags = STARTF_USESTDHANDLES;
// si.dwFlags = CREATE_NO_WINDOW;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
// si.wShowWindow = CREATE_NO_WINDOW;
CreateProcess(NULL, (LPSTR) cmdline, NULL, NULL, 0, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// ExitProcess;
return 0;
}
I don't want to rely on external programs i.e. .vbs (Windows Scripting Host) or shortcut tricks, but simply a standalone compiled .exe.
Is this (really) too much to ask, or am i doing it (completely) wrong?
Thanks...
Update: You also seem to confuse CreateProcess flags (its dwCreationFlags argument) with the member of STARTUPINFO structure. These are different flags, CREATE_NO_WINDOW should not be in STARTUPINFO.
You have to pass the CREATE_NO_WINDOW flag, then the console window won't show. Originally I've answered that you have to redirect the standard handles which is not correct (but still highly recommanded).
Set STARTF_USESTDHANDLES and fill in appropriate handles. If you are interested in the output of the process, create pipes, otherwise you can just open nul an pass that.
Try Using ProcessBuilder. Here is an example of some code that I have that seems to work just fine. In my code below, the shellScript is a StringBuilder that I am dynamically creating that contains the command and it's parameters that I want to execute.
String[] scriptArray = shellScript.toString().split(" ");
ProcessBuilder builder = new ProcessBuilder(scriptArray);
File outputFile = new File("/logs/AgentOutputLog.txt");
File errorFile = new File("/logs/AgentErrorLog.txt");
builder.redirectOutput(outputFile);
builder.redirectError(errorFile);
Process process = builder.start();
int errCode = process.waitFor();
//errCode = 0 means online
if(errCode == 0){
success = true;
break;
//errCode = 1 means offline
} else if (errCode == 1){
success = false;
break;
}

trying to run commend on cmd throw c++ using createprocces (API)?

bool execute()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
bool flag = true;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
string f = "dir desktop"
if (CmdLine.parameter != "")
{
LPSTR l1 = const_cast<char *>(f.c_str());
CreateProcess(NULL, l1, NULL, NULL, false, 0, NULL, NULL, &si, &pi);
flag = true;
// WaitForSingleObject(pi.hProcess, INFINITE);
// // Close process and thread handles.
// CloseHandle(pi.hProcess);
// CloseHandle(pi.hThread);
//}
}
return flag;
}
I'm trying to run cmd command by visual studio.
I'm using createprocces (API) in order to run this thing
but I can't understand why it doesn't run anything.
dir is a command understood by cmd.exe, it's not a program you can execute.
You can try the command cmd /k "dir desktop", properly expressed as a C++ string.
E.g.,
auto execute()
-> bool
{
STARTUPINFO si = { sizeof( si ) };
PROCESS_INFORMATION pi = {};
string f = "cmd /k \"dir desktop\"\0";
bool const ok = !!CreateProcess( 0, &f[0], 0, 0, false, 0, 0, 0, &si, &pi );
if( !ok ) { return false; }
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
Note how the calls to ZeroMemory have been replaced with C++ initialization.
Just by letting the compiler do its job you get shorter, more clear code that is more likely correct, and just as efficient (possibly more). Win win win.
Disclaimer: code not reviewed by compiler.
If the intent is to list the contents of the user's desktop folder, then note that dir desktop doesn't do that. As an interactive command in the command interpreter you could use dir %userprofile%\desktop, and that also works via the Windows Run-dialog. Depending on the command interpreter's behavior for command line arguments it may work directly via CreateProcess, or not.
Generally, when using Windows API level functions it's preferable to use the wchar_t-based text based functions, i.e. define UNICODE before including <windows.h> (or use the ...W functions explicitly).
If you call CreateProcess() with the first parameter set to NULL, then you have to make sure that l1 starts with the module name to call.
As dir is an internal command of the command processor and not an executable, you have to use cmd as module name and give the rest of the parameter as cmd expects them.
So try the following:
string f = "cmd /c=dir desktop";

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