AD

Creating Processes on Windows

.Introduction
    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.

  1. CreatePipe
  2. setup STARTUPINFO structure
  3. CreateProcess
  4. read pipe
  5. check alive/terminate process
1.CreatePipe

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
  1. http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx

沒有留言:

張貼留言