There are several ways to create processes on windows.In this article I will show how to implement with ShellExecute/CreateProcess functions, and discuss what problems might occur if using these functions on windows 7.
.test program
A simple console program.
int main(int argc, char* argv[]) { printf("run test~~~~~\n"); return 0; }
.ShellExecute
MSND :
To create a process without any redirected input/output, ShellExecute() is the simplest way to make it.(If you don't want the process window shows, use SW_HIDE instead of SW_SHOWNORMAL.)
void runTest() { /* With parameters, uses ShellExecute(NULL, L"open", L"./test.exe", L"--h", NULL, SW_SHOWNORMAL); */ ShellExecute(NULL, L"open", L"test.exe", NULL, NULL, SW_SHOWNORMAL); }
.CreateProcess
To create a process with the redirected input and output, it needs to use CreateProcess function instead of ShellExecute function.
Here is Microsoft MSDN online tutorial .
Here I create a process with redirected read pipe. There are five steps to do.
- CreatePipe
- setup STARTUPINFO structure
- CreateProcess
- read pipe
- check alive/terminate process
MSND :
Use CreatePipe function to create pipes for process input or output redirecting.Notice that "one read/write pipe set" corresponds to "one of input and ouput". If we want to redirect both of input and output, it needs two read/write sets of pipes.All Pipes we create must be closed by CloseHandle function.
SECURITY_ATTRIBUTES secattr; ZeroMemory(&secattr,sizeof(secattr)); secattr.nLength = sizeof(secattr); secattr.bInheritHandle = TRUE; //Create pipes to write and read data HANDLE rPipe, wPipe; CreatePipe(&rPipe,&wPipe,&secattr,0);
2.setup STARTUPINFO structure
MSDN:
CreateProcess function uses STARTUPINFO structure to handle necessary information for process creating.For controlling process input/output, we should set the "hStdInput" and "hStdOutput" in STARTUPINFO to our own pipe, and pass it to CreateProcess function.
STARTUPINFO sInfo; ZeroMemory(&sInfo,sizeof(sInfo)); sInfo.cb=sizeof(sInfo); sInfo.dwFlags=STARTF_USESTDHANDLES; /* if you need the input to process, create another new pipe and let sInfo.hStdInput = newReadPipe; write command to newWritePipe for process inputing */ sInfo.hStdInput=NULL; //for getting process output sInfo.hStdOutput=wPipe; sInfo.hStdError=wPipe;
3.CreateProcess
MSND :
create process
PROCESS_INFORMATION pInfo; ZeroMemory(&pInfo,sizeof(pInfo)); /* No widows, use NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW instead. */ CreateProcess(0, L"test.exe",0,0,TRUE, NORMAL_PRIORITY_CLASS,0,0,&sInfo,&pInfo);
4.read pipe
Reading pipe to receive process output messages.Read readPipe will block waiting for process output until the writePipe is closed(I mean the ReadFile is in synchronous mode). With read pipe, handles CreateProcess in another thread is better.
char buf[512]; DWORD reDword; CString csOutput=L"",csTemp = L""; BOOL res; do { res=::ReadFile(rPipe,buf,512,&reDword,0); csTemp=buf; int ss = csTemp.GetLength(); csOutput=csTemp.Left(reDword); PassMessageToParnts(csOutput); }while(res);
5.check alive/terminate process
To control the process we create needs the "process handle" in PROCESS_INFORMATION structure passed to CreateProcess.
Here is the implement for checking if process is alive.
GetExitCodeProcess()
MSDN:
checkAlive
bool checkAlive(PROCESS_INFORMATION pInfo) { if(pInfo.hProcess == NULL) return false; DWORD dwExitCode; BOOL success = GetExitCodeProcess(pInfo.hProcess, &dwExitCode); if(!success) return false; if(dwExitCode == STILL_ACTIVE) return true; return false; }
Use TerminateProcess function to terminate process we create.Before we terminate the process,it's better to check if the process is alive because process may have completed.And don't forget to close the pipes we create.
TerminateProcess()
MSDN:
Here is the implement to terminate process.
terminate process
//process may have completed if(CheckAlive()) { //pipe should be close by CloseHandle() if(wPipe != NULL) CloseHandle(wPipe); if(rPipe != NULL) CloseHandle(rPipe); TerminateProcess(pInfo.hProcess, 0); }
CreateProcess Result
.Problems when creating processes on Windows 7
.What problems may occur on Windows 7 when create processes?
Sometimes we create processes on windows XP successfully, but fails on windows 7. The solution is to run process as system administrator.User Account Control(UAC) technology on windows vista/7 make system more safely , but the problem exists.
"We can not successfully create process on those programs only can be run as system administrator."
"How can we create processes as system administrator for more access to systems."
.Processes may have different results if we run as system administrator or not.
For example
dd for windows
run "dd --list"
Normal
System Administrator
Another problem is the policy of main program.Only the program with administrator policy can create process which set to "run as administrator" if I use ShellExecute/CreateProcess normally(above this article).
First, I set dd.exe to "run as administrator"...
and then I create process to run "dd.exe --list" in my program "RunProcess.exe" with normal policy.
It failed when I create a process which need to run as administrator.
If I run "RunProcess.exe" as administrator.
It works!!. But we shouldn't restrict all our program to run as administrator.
Is there a solution let us create process as administrator in our programs?
.ShellExecute/ShellExecuteEx function with "runas"
Operation
To create a process as administrator, we can use ShellExecute/ShellExecuteEx function with "runas" operation.
The differentence of ShellExecute and ShellExecuteEx is that we can get the process handle with ShellExecuteEx. That means when we need the control to the process we create, uses ShellExecuteEx function.
The implements is almost the same with above, I will show a brief.
ShellExecuteEx
MSDN:
ShellExecute
ShellExecute(NULL, L"runas", L"dd.exe", L"--list", NULL, SW_SHOWNORMAL);
If you need to control the process created by yourself, uses the process handle in SHELLEXECUTEINFO structure.
SHELLEXECUTEINFO
MSDN:
If you want to wait for process complete,uses WaitForSingleObject.
WaitForSingleObject
MSDN:
ShellExecuteEx
int OpenAsAdmin() { std::wstring csExeName = L"C:/dd/dd.exe"; std::wstring csArguments = L"--list"; SHELLEXECUTEINFO sei; DWORD exitcode; memset(&sei,0,sizeof(SHELLEXECUTEINFO)); sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.fMask=SEE_MASK_NOCLOSEPROCESS; sei.hwnd = NULL; sei.lpVerb = L"runas"; sei.lpFile = csExeName.c_str(); sei.lpParameters = csArguments.c_str(); sei.nShow = SW_HIDE; //if user accept UAC prompt, ShellExecuteEx return TRUE BOOL re = ShellExecuteEx(&sei); if(re == FALSE) return 1; //process handle!!! /* HANDLE hProcess = sei.hProcess; example 1, use this to wait for process complete if(sei.hProcess) { // User accepted UAC prompt (gave permission). // The unprivileged parent should wait for // the privileged child to finish. WaitForSingleObject(sei.hProcess, INFINITE); } example 2 GetExitCodeProcess(sei.hProcess,&exitcode); if(exitcode==STILL_ACTIVE) { TerminateProcess(sei.hProcess,0); } */ return 0; }
.Reference
沒有留言:
張貼留言