如何运行流程,然后在退出时收到通知?

时间:2018-03-07 21:08:56

标签: c++ winapi process

我试图重新实现.NET的Process.Exited事件,但到目前为止我还没有成功。应用程序未打开时不会返回任何错误。我错过了什么?

#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");
}

3 个答案:

答案 0 :(得分:3)

您正在使用CREATE_SUSPENDED标记创建新流程,但在注册新流程pi.hThread后,您没有恢复流程(将ResumeThread()传递给HANDLE)通知。即使您这样做,也不会在退出main()之前等待通知到达。

使用完毕后,您也无法关闭pi.hThreadpi.hProcesshWait

尝试更像这样的事情:

#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);
}

或者,如果省略CREATE_SUSPENDED标记,则可以省略ResumeThread()。只要在等待线程满足之前没有关闭进程句柄,即使进程在等待线程开始监视之前结束,等待通知仍然有效:

#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);
}

然而,无论哪种方式,在这样一个简单的控制台应用程序中使用RegisterWaitForSingleObject()并不是非常有用。相反,它在多线程/ GUI应用程序中更有意义。对于此示例,您只需使用WaitForSingleObject()

#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;
}

答案 1 :(得分:-1)

我认为这里的基本问题是你的主要过程是终止,即如果你做了类似的事情:

#include <thread>

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

在主线程上,将调用该处理程序。在您的示例/演示中,您的程序没有其他任何操作。

我的意思是除了CREATE_SUSPENDED之外,它显然做了它所说的,所以你永远不会看到你创建的进程的窗口。

这对我有用:

#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;
}

答案 2 :(得分:-3)

你可以使用例如下一个代码:

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