using the CreateProcess function - c++

I'm trying to create something similar to a cmd with Microsoft Visual Studio Express 2013 for Windows Desktop in c++ and one of my function should start a process like open skype by typing "skype.exe". I searched in the internet and found the CreateProcess function that should do the work for me. when I created a function that receives a class value that I created called Line (the name of the class but it doesn't really metter) and used the CreateProcess function in the way that is shown bellow I have to type in my cmd "start skype.exe" but I want it to work like in the regular cmd by writing only "skype.exe", how can I do it?
(the l.parameter is just a string that contains the word skype)
void execute(Line l){
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
LPSTR s = const_cast<char *>(l.parameter.c_str());
if (!CreateProcess(NULL, s, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
printf("CreateProcess failed (%d).\n", GetLastError());
return;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);}

First thing is that:
LPSTR s = const_cast<char *>(l.parameter.c_str());
is bad idea, CreateFile accepts for lpCommandLine non const buffer for a reason - it might modify it:
The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.
so you should pass an array, for example:
TCHAR szCmd[MAX_PATH] = {0};
then to your question, if "start skype.exe" works for you and you want to enter only skype.exe at the command line - then why not concatenate strings? for example:
_tcscat(szCmd, _T("start "));
_tcscat(szCmd, parameter.c_str());
and pass szCmd to CreateProcess
the question is whether you use UNICODE build, if yes then make sure parameter is std::wstring, otherwise if you use non-UNICODE build (and it looks like thats true) then std::string is fine.

start is not an executable, it is a feature of cmd.exe, so to invoke start skype.exe via CreateProcess(), you would have to specify cmd.exe as the command and /C start skype.exe as its parameter.
Line l;
line.parameter = "cmd.exe /C start skype.exe";
execute(l);
But that is overkill in this situation, as start is not actually needed, despite what you claim. It is perfectly valid and preferable to invoke skype.exe directly as the command.
However, you have to provide the full path to skype.exe (same if you were to invoke start), otherwise CreateProcess() won't be able to find it, as Skype does not register its .exe file path in the App Paths key of the Registry, or the path to its Phone subfolder (where skype.exe resides) on the system's %PATH% environment variable.
For example:
Line l;
line.parameter = "C:\\Program Files (x86)\\Skype\\Phone\\Skype.exe";
execute(l);
Fortunately, Skype does store the full path to skype.exe in the Registry, specifically in the following key:
HKEY_CURRENT_USER\Software\Skype\Phone
It is stored in a REG_SZ value named "SkypePath".
std::string GetSkypePath()
{
std::string sPath;
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Skype\\Phone", 0, KEY_QUERY_VALUE, &hKey) == 0)
{
char szPath[MAX_PATH+1] = {0};
DWORD dwPathLen = MAX_PATH;
if (RegQueryValueExA(hKey, "SkypePath", NULL, NULL, (LPBYTE)szPath, &dwPathLen) == 0)
sPath = szPath;
RegCloseKey(hKey);
}
return sPath;
}

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

Executing rundll32.exe with CreateProcess

I've created a DLL and would like to execute one of the functions using the rundll32.exe command on windows.
Using rundll32.exe, it runs correctly from the command line; however, I'd like to call it (rundll32.exe) from a separate program. I cannot directly call the function from my code due to 32/64 bit compatibility issues in the underlying libraries I'm using (Easyhook).
Below is what I'm using in an attempt to run the dll function:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi));
LPCTSTR application = "C:\\Windows\\system32\\rundll32.exe";
LPTSTR cmd = "C:\\Projects\\Test\\mydll.dll,MyFunc";
BOOL cpRes = CreateProcess(application,
cmd,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi);
if(cpRes == 0) {
cout << "ERROR\n";
cout << GetLastError() << endl;
} else {
cout << "DLL Launched!" << endl;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
The output to my console is always DLL Launched; however, I do not see the effects of my DLL actually being called (currently stubbed out in such a way that the command writes to a file).
If I swap out the application with something such as C:\\Windows\\system32\\notepad.exe, the program successfully runs.
For completion, here's the body of MyFunc:
ofstream file;
file.open("C:\\Projects\\Test\\test.txt");
file << "I wrote to a file!";
file.close();
Is there any reason CreateProcess cannot be used with rundll32? While reading over this I found several warnings about LoadLibrary() and DLLMain but it doesn't seem like they're relevant to this.
More Clarification:
This is currently a 32-bit application (allegedly) launching the 32-bit rundll32.exe (Logic will be added later to call the 32 or 64 bit version).
My dll is as follows:
extern "C" __declspec(dllexport) void CALLBACK MyFunc(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
void CALLBACK MyFunc(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { ... }
Which also has a .def file with:
EXPORTS
MyFunc
Running
C:\Windows\system32\rundll32.exe C:\Projects\Test\mydll.dll,MyFunc
produces the expected results.
Update
Setting application to NULL and including the rundll32.exe in cmd as mentioned in the comments seems to work.
Relevant Docs:
CreateProcess
RunDll32.exe
Per the CreateProcess() documentation:
If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.
You are not repeating rundll32.exe as the first command-line token.
So, if you continue using the lpApplicationName parameter, then change this:
LPCTSTR application = "C:\\Windows\\system32\\rundll32.exe";
LPTSTR cmd = "C:\\Projects\\Test\\mydll.dll,MyFunc";
To this instead:
LPCTSTR application = TEXT("C:\\Windows\\system32\\rundll32.exe");
LPTSTR cmd = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
Note that you are currently compiling for ANSI/MBCS (by virtue of the fact that you are passing narrow strings to CreateProcess()). If you ever update the project to compile for Unicode, use this instead:
TCHAR cmd[] = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
This is because the documentation states:
lpCommandLine [in, out, optional]
...
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
You might consider changing cmd into a TCHAR[] array anyway, even in ANSI/MBCS, so you can do something like this:
LPCTSTR application = TEXT("C:\\Windows\\system32\\rundll32.exe");
TCHAR cmd[(MAX_PATH*2)+10];
wsprintf(cmd, TEXT("%s %s,%s"), application, TEXT("C:\\Projects\\Test\\mydll.dll"), TEXT("MyFunc"));
Either way, by passing the module filename as the first token in the lpCommandLine parameter, you can then set the lpApplicationName parameter to NULL:
The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
Let CreateProcess() setup the correct command-line to pass to rundll32.exe for you:
TCHAR cmd[] = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
BOOL cpRes = CreateProcess(NULL, cmd, ...);

why argv in createProcess() is different from normal c++ program

this is my code, and i found the first output is "thisProgram.exe"
and the second output is "a".
why?
i read the doc in msdn, however i don't quite clear why the argv[0] can be "a", is there something different in windows when using createProcess. Could someone please tell me the difference from lpApplicationName and lpCommandline? thanks
int main( int argc, char *argv[] ) {
cout << argv[0] << endl;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess("thisProgram.exe", // No module name (use command line)
"a b c", // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
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 (%d).\n", GetLastError());
return 1;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
CreateProcess passes the second argument (the command line) to the new process as its command line. CreateProcess will not prepend the module name. If you want the application name to appear as argv[0] you must repeat the application name in the command line argument.
The documentation says it like this:
If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.
It's generally simplest to pass NULL for the application name, and for the command line pass the application name and the arguments concatenated together.

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

Beginner C++ CreateProcess () Error 2

I am trying to create a process that send command to cmd.exe and receive Error 2,
Why? It's posible? How?
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
String pathexe = "C:\Windows\system32\cmd.exe";
String command= "notepad.exe";
if(!CreateProcess(
pathexe.c_str(), // lpApplicationName
command.c_str(), // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
0, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, // lpStartupInfo
&pi // lpProcessInformation
))
{
AnsiString error = GetLastError();
ShowMessage("Error: " + error);
}
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
PD: 1) It is assumed that you can use CreateProcess () for this purpose, I should not do it with ShellExecute () or system().
2) I have read about it in the forum and can not find a solution to this error, there are many answers to similar questions but do not address the error, other functions are proposed, or mix with the route command.
3) I do not think that issue permits because I built while the manifest.
4) I currently use C ++ Builder, in win7, 32bits but not important.
5) I guess the issue will be voted as negative and duplicate (as usual) but the proposed testing examples also receive errors.
Thanks to all
FIRST CONCLUSIONS:
Error 2: The system cannot find the file specified.
Link funtion: https://msdn.microsoft.com/es-es/library/windows/desktop/ms679360(v=vs.85).aspx
Link error: https://msdn.microsoft.com/es-es/library/windows/desktop/ms681382(v=vs.85).aspx
With error 2: check syntax, file path and existence.
works:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
String command = "notepad.exe";
if(!CreateProcess(
NULL, // lpApplicationName
commmand.c_str(), // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
0, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, // lpStartupInfo
&pi // lpProcessInformation
))
{
AnsiString error = GetLastError();
ShowMessage("Error: " + error);
}
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
This example works also for exe
String command = "cd C:\\sample\\calc.exe";
But no with cmd´s general commands, there must be a way to send commands to cmd as:
notepad.exe && cd C:\sample\ && sample1.txt
THANKS TO ALL
You're trying to run this command:
cmd notepad
(You aren't doing that quite right, either; the lpCommandLine argument should include the entire string, not just notepad, and you haven't quoted the backslashes properly.)
But even once you fix those problems, it won't work, because you've got the syntax wrong. You'll find it won't work if typed on the command line either!
Instead, try:
String pathexe = "C:\\Windows\\system32\\cmd.exe";
String command= "cmd /c notepad.exe";
The /c option means "run this command". You can use /k instead if you want the command window to stay open after the command has finished, though it's unusual for a program to do so.
One final note: I'm assuming here than notepad is just a stand-in for a more complicated command. If you actually want to run notepad, or any other executable, you shouldn't be invoking cmd.exe at all:
String command= "notepad";
if(!CreateProcess(
NULL, // lpApplicationName
command.c_str(), // lpCommandLine
...
You only need to call on cmd.exe if you need to run a built-in command, or a composite command line.
(Actually, calling cmd.exe is considered poor practice even in those cases; in Windows, you are generally expected do that sort of thing for yourself via the API rather than farming out the job to the command interpreter. But there are edge cases, and your mileage may vary.)