Hi @水 知
The System.ExecutionEngineException (0x80131506) you are experiencing is a classic symptom of an unmanaged callback attempting to invoke a Garbage Collected (GC'ed) delegate.
Issue 1: Delegate Garbage Collection
In your StartMonitoring method, you pass WinEventProc directly to SetWinEventHook. This essentially creates a new delegate instance on the fly as an unmanaged function pointer. Because you don't store this delegate instance in a class-level variable, the .NET Garbage Collector doesn't see any active managed references to it and promptly collects it. When a foreground window changes shortly after, the operating system attempts to invoke the freed memory, bringing down the application with an ExecutionEngineException.
Fix: Store the delegate as a private field in your class to keep it alive for the lifetime of the hook.
Issue 2: Event Hooks Require a Message Loop
WINEVENT_OUTOFCONTEXT hooks require a standard Windows message loop (GetMessage / DispatchMessage) to receive the events asynchronously. If you execute StartMonitoring() on the WinUI main UI thread, its built-in Dispatcher handles this for you. However, if you execute it from a background thread (as you commented out with Task.Run), it will fail unless you provide it with an active message loop.
Here is my implementation that you can try to follow instead to resolve the crash:
public class ForegroundWindow
{
// ...
private readonly object _hwndLock = new();
private bool _ismonitoring = false;
// 1. Keep a strong reference to the delegate to prevent GC.
private WINEVENTPROC _winEventProcDelegate;
// 2. Keep the handle in case you want to UnhookWinEvent later.
private HWINEVENTHOOK _hook;
public ForegroundWindow()
{
// Initialize and save the delegate locally inside the class instance.
_winEventProcDelegate = new WINEVENTPROC(WinEventProc);
}
// ...
public void StartMonitoring()
{
if (_ismonitoring)
return;
_ismonitoring = true;
// Pass the saved delegate instance, preventing it from being GC'ed.
_hook = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND,
HMODULE.Null,
_winEventProcDelegate, // <-- Use the instance variable here
0,
0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS
);
// Note: As long as StartMonitoring() is called on the application's Main UI thread,
// it will inherit WinUI's message loop automatically and WINEVENT_OUTOFCONTEXT works fine.
}
}
If you found my response helpful or informative, I would greatly appreciate it if you could follow this guidance or provide feedback.
Thank you.