RegisterWaitForSingleObject 中监听其他进程时调用的回调函数

问题描述 投票:0回答:1

目标:我想监视 notepad.exe 并在其终止时重新启动它。

我的程序应该

  1. 使用
    ShellExecuteEx
  2. 启动记事本进程
  3. 使用
    RegisterWaitForSingleObject
  4. 注册已启动的进程
  5. 回调函数
    ProcessWaitCallback
    每当记事本进程终止时调用。

但是 我的程序确实

  1. 使用
    ShellExecuteEx
  2. 启动记事本进程
  3. 使用
    RegisterWaitForSingleObject
  4. 注册已启动的进程
  5. 回调函数
    ProcessWaitCallback
    在我运行程序时立即调用,导致重复的记事本进程产生。

问题是,一旦我运行程序,回调就会被执行(我希望在关闭记事本进程时执行),从而导致记事本的 2 个实例运行。

#include <windows.h>
#include <iostream>
#include <atlbase.h>

#include "conio.h"

using namespace ::std;

HANDLE hWaitHandle;
LPCSTR notepadExePath = "C:\\Windows\\notepad.exe";
SHELLEXECUTEINFO shellExecInfo = { 0 };

bool IsValidHandle(HANDLE hHandle, string logString) 
{
    bool returnVal = hHandle != NULL && hHandle != INVALID_HANDLE_VALUE;
    cout << "IsValidHandle(): " << logString << " " << returnVal << endl;
    return returnVal;
}

SHELLEXECUTEINFO ShellExecEx(
    HWND hwnd,
    LPCSTR lpOperation,
    LPCSTR lpFile,
    LPCSTR lpParameters,
    LPCSTR lpDirectory,
    INT nShowCmd,
    ULONG fmask)
{
    SHELLEXECUTEINFO shellExecInfoLocal = { 0 };
    shellExecInfoLocal.cbSize = sizeof(SHELLEXECUTEINFO);
    shellExecInfoLocal.fMask = fmask;
    shellExecInfoLocal.hwnd = hwnd;
    shellExecInfoLocal.lpVerb = lpOperation;
    shellExecInfoLocal.lpFile = lpFile;
    shellExecInfoLocal.lpParameters = lpParameters;
    shellExecInfoLocal.lpDirectory = lpDirectory;
    shellExecInfoLocal.nShow = nShowCmd;
    shellExecInfoLocal.hInstApp = NULL;

    cout << "ShellExecEx(): Invoking LPCWSTR version of ShellExecEx" << endl;
    ShellExecuteEx(&shellExecInfoLocal);
    return shellExecInfoLocal;
}

// Callback function to be called when the process terminates
void CALLBACK ProcessWaitCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    cout << "ProcessWaitCallback(): Process terminated. Restarting..." << std::endl;
    if (IsValidHandle(shellExecInfo.hProcess, "ProcessWaitCallback(): shellExecInfo.hProcess"))
    {
        CloseHandle(shellExecInfo.hProcess);
    }

    shellExecInfo = ShellExecEx(nullptr, "open", notepadExePath, "", "", SW_SHOWNORMAL, SEE_MASK_NOCLOSEPROCESS);

    auto code = reinterpret_cast<INT_PTR>(shellExecInfo.hInstApp);
    if (code <= 32)
    {
        cout << "ProcessWaitCallback(): Error re-starting Windows 365:" << GetLastError() << endl;
        return;
    }
    else
    {
        cout << "ProcessWaitCallback(): Restarted notepad with pid:" << GetProcessId(shellExecInfo.hProcess) << endl;
    }
    if (!RegisterWaitForSingleObject(
        &hWaitHandle,
        shellExecInfo.hProcess,
        ProcessWaitCallback,
        NULL,
        INFINITE,
        WT_EXECUTEONLYONCE))
    {
        cout << "ProcessWaitCallback(): Error registering wait operation: " << GetLastError() << std::endl;
    }
}

int main()
{
    shellExecInfo = ShellExecEx(nullptr, "open", notepadExePath, "", "", SW_SHOWNORMAL, SEE_MASK_NOCLOSEPROCESS);
    
    auto code = reinterpret_cast<INT_PTR>(shellExecInfo.hInstApp);
    if (code <= 32)
    {
        cout << "main(): Error Starting Notepad:" << GetLastError() << endl;
        return 1;
    }

    DWORD w365ProcessId = GetProcessId(shellExecInfo.hProcess);
    cout << "main(): Process started successfully pid:" << w365ProcessId << endl;

    if (!RegisterWaitForSingleObject(
        &hWaitHandle,
        shellExecInfo.hProcess,
        ProcessWaitCallback,
        NULL,
        INFINITE,
        WT_EXECUTEONLYONCE
))
    {
        cout << "main(): Error registering wait operation: " << GetLastError() << std::endl;
    }

    cout << "main(): Press any key to exit 1..." << std::endl;
    _getch();
    cout << "main(): Press any key to exit 2..." << std::endl;
    _getch();

    if (IsValidHandle(hWaitHandle, "main(): hWaitHandle"))
    {
        if (UnregisterWait(hWaitHandle) == 0)
        {
            cout << "main(): Error while unregistering wait handle. error:" << GetLastError() << std::endl;
        }
        else
        {
            cout << "main(): Wait handle successfully unregistered." << std::endl;
        }
    }

    if (IsValidHandle(shellExecInfo.hProcess, "main(): shellExecInfo.hProcess"))
    {
        if (CloseHandle(shellExecInfo.hProcess) == 0)
        {
            cout << "main(): Error while closing process handle. error:" << GetLastError() << std::endl;
        }
        else
        {
            cout << "main(): Process handle successfully closed." << std::endl;
        }
    }

    return 0;
}

输出如下(没有我杀掉打开的记事本)

.\MonitorNotepad_WT_EXECUTEONLYONCE.exe
ShellExecEx(): Invoking LPCWSTR version of ShellExecEx
main(): Process started successfully pid:46116
main(): Press any key to exit 1...
ProcessWaitCallback(): Process terminated. Restarting...
IsValidHandle(): ProcessWaitCallback(): shellExecInfo.hProcess 1
ShellExecEx(): Invoking LPCWSTR version of ShellExecEx
ProcessWaitCallback(): Restarted notepad with pid:39648

看看即使我没有关闭记事本进程,

ProcessWaitCallback(): Process terminated. Restarting...
行仍然被打印。

更新1: 我也看到了与 CreateProcess 完全相同的行为

#include <windows.h>
#include <iostream>
#include "conio.h"

using namespace::std;

void CALLBACK ProcessExitCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    std::cout << "Process has exited asynchronously." << std::endl;
}

int main()
{
    LPSTR processPath = "C:\\Windows\\notepad.exe"; // Replace with your actual process path

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

    if (CreateProcess(
        nullptr,
        processPath,
        nullptr,
        nullptr,
        FALSE,
        0,
        nullptr,
        nullptr,
        &si,
        &pi))
    {
        CloseHandle(pi.hThread);

        HANDLE waitHandle = NULL;

        if (RegisterWaitForSingleObject(&waitHandle, pi.hProcess, &ProcessExitCallback, nullptr, INFINITE, WT_EXECUTEONLYONCE))
        {
            std::cout << "Asynchronous wait registered. Waiting for process exit..." << std::endl;
            _getch();
            _getch();
            if (CloseHandle(pi.hProcess) == 0) 
            { cout << "CloseHandle error" << endl; }
            else { cout << "CloseHandle success" << endl; }
            if (UnregisterWait(waitHandle) == 0) 
            { cout << "UnregisterWait error" << endl; } 
            else 
            { cout << "UnregisterWait success" << endl; }
        }
        else
        {
            std::cout << "Failed to register asynchronous wait. Error: " << GetLastError() << std::endl;
            CloseHandle(pi.hProcess);
        }
    }
    else
    {
        std::cout << "Failed to create the process. Error: " << GetLastError() << std::endl;
    }

    return 0;
}

这是我没有关闭记事本进程的输出

Asynchronous wait registered. Waiting for process exit...
Process has exited asynchronously.
CloseHandle success
UnregisterWait success
c++ windows winapi win32-process
1个回答
0
投票

相反,

RegisterWaitForSingleObject
最好使用一次
CreateThreadpoolWait
SetThreadpoolWait
(每次开始新流程时)。此 api 由
RegisterWaitForSingleObject
内部调用。代码可以是下一个:

class RundownProtection
{
    LONG _Value;

public:

    enum {
        v_complete = 0, v_init = 0x80000000
    };

    BOOL IsRundownCompleted()
    {
        return v_complete == _Value;
    }

    _NODISCARD BOOL IsRundownBegin()
    {
        return 0 <= _Value;
    }

    _NODISCARD BOOL Acquire()
    {
        LONG Value, NewValue;

        if (0 > (Value = _Value))
        {
            do 
            {
                NewValue = InterlockedCompareExchangeNoFence(&_Value, Value + 1, Value);

                if (NewValue == Value) return TRUE;

            } while (0 > (Value = NewValue));
        }

        return FALSE;
    }

    _NODISCARD BOOL Release()
    {
        return InterlockedDecrement(&_Value) == v_complete;
    }

    // if (Acquire()) { Rundown_l(); Release(); }
    void Rundown_l()
    {
        InterlockedBitTestAndReset(&_Value, 31);
    }

    RundownProtection(LONG Value = v_complete) : _Value(Value)
    {
    }

    BOOL Init()
    {
        return InterlockedCompareExchange(&_Value, v_init, v_complete) == v_complete;
    }
};

struct AR 
{
    PTP_WAIT _M_Wait = 0;
    RundownProtection _M_lock = RundownProtection::v_init;

    static VOID CALLBACK _S_WaitCallback(
        _In_ PTP_CALLBACK_INSTANCE /*Instance*/,
        _In_ PVOID Context,
        _In_ PTP_WAIT Wait,
        _In_ TP_WAIT_RESULT WaitResult
        )
    {
        reinterpret_cast<AR*>(Context)->WaitCallback(Wait, WaitResult);
    }

    VOID WaitCallback(
        _In_ PTP_WAIT Wait,
        _In_ TP_WAIT_RESULT WaitResult
        )
    {
        switch (WaitResult)
        {
        case WAIT_OBJECT_0:
            if (_M_lock.Acquire())
            {                   
                Start(Wait);

                if (_M_lock.Release())
                {
                    CloseThreadpoolWait(Wait);
                    delete this;
                }
            }
            break;
        }
    }

    void Start(_In_ PTP_WAIT Wait)
    {
        static const WCHAR notepad[] = L"\\notepad.exe";
        WCHAR buf[MAX_PATH];
        if (ULONG cch = GetWindowsDirectoryW(buf, _countof(buf)))
        {
            if (cch <= _countof(buf) - _countof(notepad))
            {
                memcpy(buf + cch, notepad, sizeof(notepad));

                STARTUPINFOW si = { sizeof(si) };
                PROCESS_INFORMATION pi;
                if (CreateProcessW(buf, 0, 0, 0, 0, 0, 0, 0, &si, &pi))
                {
                    NtClose(pi.hThread);
                    SetThreadpoolWait(Wait, pi.hProcess, 0);
                    NtClose(pi.hProcess);
                }
            }
        }
    }

    void Start()
    {
        if (PTP_WAIT Wait = CreateThreadpoolWait(_S_WaitCallback, this, 0))
        {
            _M_Wait = Wait;
            Start(Wait);
        }
    }

    void Stop()
    {
        if (_M_lock.Acquire())
        {
            _M_lock.Rundown_l();

            if (_M_lock.Release())
            {
                if (_M_Wait)
                {
                    WaitForThreadpoolWaitCallbacks(_M_Wait, TRUE);
                    CloseThreadpoolWait(_M_Wait);
                }

                delete this;
            }
        }
    }
};

void demo()
{
    if (AR* p = new AR)
    {
        p->Start();

        MessageBoxW(0,0,0,0);

        p->Stop();
    }
}

如果使用

RegisterWaitForSingleObject
那么

struct AR 
{
    HANDLE _M_hWaitObject = 0;
    SRWLOCK _M_lock {};
    LONG _M_dwRef = 1;
    BOOLEAN _M_bStop = FALSE;

    void AddRef()
    {
        InterlockedIncrementNoFence(&_M_dwRef);
    }

    void Release()
    {
        if (!InterlockedDecrement(&_M_dwRef))
        {
            delete this;
        }
    }

    static VOID CALLBACK _S_WaitOrTimerCallback(
        _In_  PVOID lpParameter,
        _In_  BOOLEAN TimerOrWaitFired
        )
    {
        if (TimerOrWaitFired)
        {
            __debugbreak();
        }
        reinterpret_cast<AR*>(lpParameter)->WaitOrTimerCallback();
    }

    VOID WaitOrTimerCallback()
    {
        AcquireSRWLockExclusive(&_M_lock);

        if (HANDLE hWaitObject = _M_hWaitObject)
        {
            _M_hWaitObject = 0;

            if (UnregisterWaitEx(hWaitObject, 0) || ERROR_IO_PENDING != GetLastError())
            {
                __debugbreak();
            };
        }

        if (!_M_bStop)
        {
            Start();
        }

        ReleaseSRWLockExclusive(&_M_lock);

        Release();
    }

    void Start()
    {
        static const WCHAR notepad[] = L"\\notepad.exe";
        WCHAR buf[MAX_PATH];
        if (ULONG cch = GetWindowsDirectoryW(buf, _countof(buf)))
        {
            if (cch <= _countof(buf) - _countof(notepad))
            {
                memcpy(buf + cch, notepad, sizeof(notepad));

                STARTUPINFOW si = { sizeof(si) };
                PROCESS_INFORMATION pi;
                if (CreateProcessW(buf, 0, 0, 0, 0, 0, 0, 0, &si, &pi))
                {
                    NtClose(pi.hThread);

                    AddRef();
                    if (!RegisterWaitForSingleObject(&_M_hWaitObject, pi.hProcess, 
                        _S_WaitOrTimerCallback, this, INFINITE, WT_EXECUTEONLYONCE))
                    {
                        Release();
                    }
                    NtClose(pi.hProcess);
                    __nop();
                }
            }
        }
    }

    void Stop()
    {
        _M_bStop = TRUE;

        AcquireSRWLockExclusive(&_M_lock);

        if (HANDLE hWaitObject = _M_hWaitObject)
        {
            _M_hWaitObject = 0;

            if (UnregisterWaitEx(hWaitObject, 0))
            {
                Release();
            }
            else if (ERROR_IO_PENDING != GetLastError())
            {
                __debugbreak();
            }
        }

        ReleaseSRWLockExclusive(&_M_lock);
    }
};

void demo()
{
    if (AR* p = new AR)
    {
        p->Start();

        MessageBoxW(0,0,0,0);

        p->Stop();

        p->Release();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.