简体   繁体   中英

How do I run a process then get notified when it has exited?

I'm trying to reimplement .NET's Process.Exited Event but I've been unsuccessfully so far. No error is returned by the application doesn't open. What am I missing?

#include <stdio.h>
#include <windows.h>

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut);

int main() 
{
    char *cmd = "C:\\Windows\\System32\\notepad.exe";
    STARTUPINFOA si = { sizeof(STARTUPINFOA) };
    //si.cb = STARTUPINFOA.sizeof;
    PROCESS_INFORMATION pi;
    if(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, NULL, NULL, &si, &pi))
    {
        HANDLE hWait;
        if(!RegisterWaitForSingleObject(&hWait, pi.hProcess, OnExited, NULL, INFINITE, WT_EXECUTEONLYONCE))
        {
            printf("register failed! = %d", GetLastError());
        }
    }
    else
    {
        printf("error = %d\n", GetLastError());
    }
}

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut)
{
    printf("OnExited got called!\n");
}

You are creating the new process with the CREATE_SUSPENDED flag, but you are not resuming the process (by passing pi.hThread to ResumeThread() ) after registering the new process HANDLE for notification. Even if you did, you are not waiting for the notification to arrive before exiting from main() .

You are also not closing pi.hThread , pi.hProcess , or hWait after you are done using them.

Try something more like this:

#include <stdio.h>
#include <windows.h>

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut);

int main() 
{
    char *cmd = "C:\\Windows\\System32\\notepad.exe";

    STARTUPINFOA si = {};
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;

    if (!CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, NULL, NULL, &si, &pi))
    {
        printf("create process error = %u\n", GetLastError());
    }
    else
    {
        HANDLE hWait = NULL;

        HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!hEvent)
        {
            printf("create event error = %u\n", GetLastError());
        }
        else if (!RegisterWaitForSingleObject(&hWait, pi.hProcess, OnExited, hEvent, INFINITE, WT_EXECUTEONLYONCE))
        {
            printf("register wait error = %u\n", GetLastError());
        }

        ResumeThread(pi.hThread);
        CloseHandle(pi.hThread);

        if (hEvent)
        {
            if (hWait)
            {
                WaitForSingleObject(hEvent, INFINITE);
                UnregisterWait(hWait);
            }

            CloseHandle(hEvent);
        }

        CloseHandle(pi.hProcess);
    }

    return 0;
}

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut)
{
    printf("OnExited got called!\n");
    SetEvent((HANDLE)context);
}

Or, if you omit the CREATE_SUSPENDED flag, you can then omit the ResumeThread() . The wait notification will still work even if the process ends before the wait thread begins monitoring it, as long as you don't close the process handle before the wait is satisfied:

#include <stdio.h>
#include <windows.h>

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut);

int main() 
{
    char *cmd = "C:\\Windows\\System32\\notepad.exe";

    STARTUPINFOA si = {};
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;

    if (!CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
    {
        printf("create process error = %u\n", GetLastError());
    }
    else
    {
        CloseHandle(pi.hThread);

        HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!hEvent)
        {
            printf("create event error = %u\n", GetLastError());
        }
        else
        {
            HANDLE hWait = NULL;
            if (!RegisterWaitForSingleObject(&hWait, pi.hProcess, OnExited, hEvent, INFINITE, WT_EXECUTEONLYONCE))
            {
                printf("register wait error = %u\n", GetLastError());
            }
            else
            {
                WaitForSingleObject(hEvent, INFINITE);
                UnregisterWait(hWait);
            }

            CloseHandle(hEvent);
        }

        CloseHandle(pi.hProcess);
    }

    return 0;
}

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut)
{
    printf("OnExited got called!\n");
    SetEvent((HANDLE)context);
}

However, either way, using RegisterWaitForSingleObject() isn't very beneficial in such a simple console app. It makes more sense in a multithreaded/GUI app instead. For this example, you could just use WaitForSingleObject() instead:

#include <stdio.h>
#include <windows.h>

int main() 
{
    char *cmd = "C:\\Windows\\System32\\notepad.exe";

    STARTUPINFOA si = {};
    si.cb = sizeof(STARTUPINFOA);

    PROCESS_INFORMATION pi;

    if (!CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
    {
        printf("create process error = %d\n", GetLastError());
    }
    else
    {
        CloseHandle(pi.hThread);
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        printf("Exited!\n");
    }

    return 0;
}

I think the basic problem here is your main process is terminating, ie if you do something like:

#include <thread>

std::this_thread::sleep_for(std::chrono::seconds(10000));

on your main thread, the handler will be called. In your example/demo your program has nothing else to do.

I mean apart from CREATE_SUSPENDED, which obviously does what it says so you'll never see your created process's window.

This works for me:

#include <thread>
#include <iostream>
#include <string>
#include <windows.h>

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut);

int main()
{   
    char *cmd = "C:\\Windows\\System32\\notepad.exe";

    STARTUPINFOA si = { sizeof(STARTUPINFOA) }; 
    PROCESS_INFORMATION pi;

    if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
    {
        HANDLE hWait;

        if (!RegisterWaitForSingleObject(&hWait, pi.hProcess, OnExited, NULL, INFINITE, WT_EXECUTEONLYONCE))
        {
            std::cout << "register failed: " << GetLastError() << std::endl;
        }
    }
    else
    {
        std::cout << "error creating process: " << GetLastError() << std::endl;
    }

    // Engage in shenanigans otherwise we'll just exit main.

    std::this_thread::sleep_for(std::chrono::seconds(10000));
}

static void CALLBACK OnExited(void* context, BOOLEAN isTimeOut)
{
    std::cout << "OnExited got called!" << std::endl;
}

you can use for example next code:

class __declspec(novtable) _WAIT_CTX 
{
    HANDLE hObject, WaitHandle;

    static VOID CALLBACK _WaitOrTimerCallback(PVOID lpParameter, BOOLEAN /*TimerOrWaitFired*/)
    {
        reinterpret_cast<_WAIT_CTX*>(lpParameter)->_OnWaitCallback();
    }

    void _OnWaitCallback()
    {
        OnWaitCallback(hObject);
        delete this;
    }
protected:
    virtual ~_WAIT_CTX()
    {
        if (WaitHandle) UnregisterWait(WaitHandle);
        if (hObject) CloseHandle(hObject);
    }
    virtual void OnWaitCallback(HANDLE hObject) = 0;
public:

    ULONG Register(HANDLE h)
    {
        hObject = h;

        if (RegisterWaitForSingleObject(&WaitHandle, h, 
            _WaitOrTimerCallback, this, INFINITE, WT_EXECUTEONLYONCE))
        {
            return NOERROR;
        }

        ULONG dwError = GetLastError();

        delete this;

        return dwError;
    }

    _WAIT_CTX() : hObject(0), WaitHandle(0)
    {
    }
};

class WAIT_CTX : public _WAIT_CTX
{
    virtual void OnWaitCallback(HANDLE hProcess)
    {
        ULONG exitCode;
        GetExitCodeProcess(hProcess, &exitCode);
        DbgPrint("exitcode=%x", exitCode);
    }
};

ULONG bbb()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    ULONG dwError;
    if (CreateProcessW(L"c:\\windows\\notepad.exe", 0, 0, 0, 0, 0, 0, 0, &si, &pi))
    {
        CloseHandle(pi.hThread);

        if (WAIT_CTX* p = new WAIT_CTX)
        {
            dwError = p->Register(pi.hProcess);
        }
        else
        {
            dwError = ERROR_OUTOFMEMORY;
            CloseHandle(pi.hProcess);
        }
    }
    else 
    {
        dwError = GetLastError();
    }

    return dwError;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM