目标:我想监视 notepad.exe 并在其终止时重新启动它。
我的程序应该,
ShellExecuteEx
RegisterWaitForSingleObject
ProcessWaitCallback
每当记事本进程终止时调用。但是 我的程序确实,
ShellExecuteEx
RegisterWaitForSingleObject
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
相反,
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();
}
}