如何监视窗口的打开,关闭和激活?

 监视windows窗口状态都可以用以下hook

WindowEventHook
 /// <summary>
   /// Represents a hook that the system calls in response to events generated by an accessible object.
   /// A message loop is required for the hook to function propertly.
   /// </summary>
   public class WindowEventHook : IDisposable
   {
       private const uint AllThreads = 0;
       private const uint AllProcesses = 0;

       /// <summary>
       /// The lower bound of the observed event range.
       /// </summary>
       public WindowEvent EventMin { get; }
       /// <summary>
       /// The upper bound of the observed event range.
       /// </summary>
       public WindowEvent EventMax { get; }

       /// <summary>
       /// A value indicating whether the hook is currently attached.
       /// </summary>
       public bool Hooked => RawHookHandle != IntPtr.Zero;

       /// <summary>
       /// A value indicating whether the hook should observe events originating from the current thread.
       /// </summary>
       public bool SkipOwnThread
       {
           get => (_hookFlags & WinEventHookFlags.WINEVENT_SKIPOWNTHREAD) == WinEventHookFlags.WINEVENT_SKIPOWNTHREAD;
           set
           {
               if (Hooked)
                   throw new InvalidOperationException("SkipOwnThread cannot be changed while hooked.");
               _hookFlags = value ? _hookFlags | WinEventHookFlags.WINEVENT_SKIPOWNTHREAD : _hookFlags & ~WinEventHookFlags.WINEVENT_SKIPOWNTHREAD;
           }
       }
       /// <summary>
       /// A value indicating whether the hook should observe events originating from the current process.
       /// </summary>
       public bool SkipOwnProcess
       {
           get => (_hookFlags & WinEventHookFlags.WINEVENT_SKIPOWNPROCESS) == WinEventHookFlags.WINEVENT_SKIPOWNPROCESS;
           set
           {
               if (Hooked)
                   throw new InvalidOperationException("SkipOwnProcess cannot be changed while hooked.");
               _hookFlags = value ? _hookFlags | WinEventHookFlags.WINEVENT_SKIPOWNPROCESS : _hookFlags & ~WinEventHookFlags.WINEVENT_SKIPOWNPROCESS;
           }
       }
       private WinEventHookFlags _hookFlags = WinEventHookFlags.WINEVENT_OUTOFCONTEXT | WinEventHookFlags.WINEVENT_SKIPOWNPROCESS | WinEventHookFlags.WINEVENT_SKIPOWNTHREAD;

       /// <summary>
       /// Occurs whenever an window event is raised in an oberserved process or thread.
       /// </summary>
       public event EventHandler<WinEventHookEventArgs>? EventReceived;

       /// <summary>
       /// The handle representing this hook.
       /// </summary>
       public IntPtr RawHookHandle { get; private set; } = IntPtr.Zero;

       private WinEventProc? eventHandler;

       /// <summary>
       /// Creates a new hook that listens to all events.
       /// </summary>
       public WindowEventHook() : this(WindowEvent.EVENT_MIN, WindowEvent.EVENT_MAX) { }
       /// <summary>
       /// Creates a new hook that listens to the specified event.
       /// </summary>
       public WindowEventHook(WindowEvent @event) : this(@event, @event) { }
       /// <summary>
       /// Creates a new hook that listens to the specified event range.
       /// </summary>
       public WindowEventHook(WindowEvent eventMin, WindowEvent eventMax)
       {
           EventMin = eventMin;
           EventMax = eventMax;
       }

       /// <summary>
       /// Attaches the hook to all running processes and threads.
       /// </summary>
       /// <exception cref="InvalidOperationException">If this hook is already hooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public void HookGlobal() =>
           HookInternal(processId: AllProcesses, threadId: AllThreads, throwIfAlreadyHooked: true, throwOnFailure: true);

       /// <summary>
       /// Trys to attach the hook to all running processes and threads.
       /// </summary>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       public bool TryHookGlobal() =>
           HookInternal(processId: AllProcesses, threadId: AllThreads, throwIfAlreadyHooked: false, throwOnFailure: false);

       /// <summary>
       /// Attaches the hook to the specfied process.
       /// </summary>
       /// <param name="process">The process that should be observed.</param>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       /// <exception cref="InvalidOperationException">If this hook is already hooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public void HookToProcess(Process process) =>
           HookToProcess((uint)process.Id);

       /// <summary>
       /// Attaches the hook to the process specfied by the given id.
       /// </summary>
       /// <param name="processId">The id of the process that should be observed.</param>
       /// <exception cref="InvalidOperationException">If this hook is already hooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public void HookToProcess(uint processId) =>
           HookInternal(processId, threadId: AllThreads, throwIfAlreadyHooked: true, throwOnFailure: true);

       /// <summary>
       /// Trys to attach the hook to the specfied process.
       /// </summary>
       /// <param name="process">The process that should be observed.</param>
       public bool TryHookToProcess(Process process) =>
           TryHookToProcess((uint)process.Id);

       /// <summary>
       /// Trys to attach the hook to the process specfied by the given id.
       /// </summary>
       /// <param name="processId">The id of the process that should be observed.</param>
       public bool TryHookToProcess(uint processId) =>
           HookInternal(processId, threadId: AllThreads, throwIfAlreadyHooked: false, throwOnFailure: false);

       /// <summary>
       /// Attaches the hook to the specfied thread.
       /// </summary>
       /// <param name="thread">The thread that should be observed.</param>
       /// <exception cref="InvalidOperationException">If this hook is already hooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public void HookToThread(ProcessThread thread) =>
           HookToThread((uint)thread.Id);

       /// <summary>
       /// Attaches the hook to the thread specfied by the given id.
       /// </summary>
       /// <param name="threadId">The id of the thread that should be observed.</param>
       /// <exception cref="InvalidOperationException">If this hook is already hooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public void HookToThread(uint threadId) =>
           HookInternal(processId: AllProcesses, threadId, throwIfAlreadyHooked: true, throwOnFailure: true);

       /// <summary>
       /// Attaches the hook to the specfied thread.
       /// </summary>
       /// <param name="thread">The thread that should be observed.</param>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       public bool TryHookToThread(ProcessThread thread) =>
           TryHookToThread((uint)thread.Id);

       /// <summary>
       /// Attaches the hook to the thread specfied by the given id.
       /// </summary>
       /// <param name="threadId">The id of the thread that should be observed.</param>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       public bool TryHookToThread(uint threadId) =>
           HookInternal(processId: AllProcesses, threadId, throwIfAlreadyHooked: false, throwOnFailure: false);

       /// <summary>
       /// Attaches the hook globally or to the specified thread or process.
       /// </summary>
       /// <param name="processId">The id of the process to attach to.</param>
       /// <param name="threadId">The id of the thread to attach to.</param>
       /// <param name="throwIfAlreadyHooked">Should an exception be thrown if the hook is already in the hooked state.</param>
       /// <param name="throwOnFailure">Should an exception be thrown if the operation fails.</param>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       /// <exception cref="InvalidOperationException">If this hook is already hooked and <paramref name="throwIfAlreadyHooked"/> is true.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation and <paramref name="throwOnFailure"/> is true.</exception>
       internal bool HookInternal(uint processId = AllProcesses, uint threadId = AllThreads, bool throwIfAlreadyHooked = true, bool throwOnFailure = true)
       {
           if (Hooked)
           {
               if (throwIfAlreadyHooked)
                   throw new InvalidOperationException("Hook is already hooked.");
               return true;
           }

           eventHandler = new WinEventProc(OnWinEventProc);
           RawHookHandle = SetWinEventHook(
               eventMin: EventMin,
               eventMax: EventMax,
               hmodWinEventProc: IntPtr.Zero,
               lpfnWinEventProc: eventHandler,
               idProcess: processId,
               idThread: threadId,
               dwFlags: _hookFlags
           );

           if (RawHookHandle != IntPtr.Zero)
           {
               return true;
           }
           else
           {
               eventHandler = null;
               if (throwOnFailure)
                   throw new Win32Exception();
               return false;
           }
       }

       /// <summary>
       /// Detaches the hook from wherever it is attached to.
       /// </summary>
       /// <exception cref="InvalidOperationException">If this hook is already unhooked.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation.</exception>
       public bool Unhook() =>
           UnhookInternal(throwIfNotHooked: true, throwOnFailure: true);

       /// <summary>
       /// Detaches the hook from wherever it is attached to.
       /// </summary>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       public bool TryUnhook() =>
           UnhookInternal(throwIfNotHooked: false, throwOnFailure: false);

       /// <summary>
       /// Detaches the hook from wherever it is attached to.
       /// </summary>
       /// <param name="throwIfNotHooked">Should an exception be thrown if the hook is not in the hooked state.</param>
       /// <param name="throwOnFailure">Should an exception be thrown if the operation fails.</param>
       /// <returns>A value indication whether the operation completed successfully.</returns>
       /// <exception cref="InvalidOperationException">If this hook is already unhooked and <paramref name="throwIfNotHooked"/> is true.</exception>
       /// <exception cref="Win32Exception">If an error occured during the operation and <paramref name="throwOnFailure"/> is true.</exception>
       internal bool UnhookInternal(bool throwIfNotHooked = true, bool throwOnFailure = true)
       {
           if (!Hooked)
           {
               if (throwIfNotHooked)
                   throw new InvalidOperationException("Hook is not hooked.");
               return true;
           }

           // we need to unhook before freeing our callback in case an event sneaks in at the right time.
           var result = UnhookWinEvent(RawHookHandle);

           eventHandler = null;
           RawHookHandle = IntPtr.Zero;

           if (!result && throwOnFailure)
               throw new Win32Exception();

           return result;
       }

       protected virtual void OnWinEventProc(IntPtr hWinEventHook, WindowEvent eventType, IntPtr hwnd, AccessibleObjectID idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
       {
           if (hWinEventHook != RawHookHandle || RawHookHandle == IntPtr.Zero)
               return;

           EventReceived?.Invoke(this, new WinEventHookEventArgs(hWinEventHook, eventType, hwnd, idObject, idChild, dwEventThread, dwmsEventTime));
       }

       #region IDisposable
       private bool _disposed;
       protected virtual void Dispose(bool disposing)
       {
           if (!_disposed)
           {
               UnhookInternal(throwIfNotHooked: false, throwOnFailure: false);

               _disposed = true;
           }
       }

       ~WindowEventHook()
       {
           Dispose(disposing: false);
       }

       public void Dispose()
       {
           Dispose(disposing: true);
           GC.SuppressFinalize(this);
       }
       #endregion
   }

   /// <summary>
   /// Represents an event generated by an accessible object.
   /// </summary>
   public sealed class WinEventHookEventArgs : EventArgs
   {
       private const int CHILDID_SELF = 0;

       /// <summary>
       /// Handle to an event hook callback.
       /// </summary>
       public IntPtr HookHandle { get; }

       /// <summary>
       /// Specifies the event that occurred.
       /// </summary>
       public WindowEvent EventType { get; }

       /// <summary>
       /// Handle to the window that generates the event, or null if no window is associated with the event. For example, the mouse pointer is not associated with a window.<
       /// </summary>
       public IntPtr WindowHandle { get; }

       /// <summary>
       /// Identifies the object associated with the event. This is one of the object identifiers or a custom object ID.
       /// </summary>
       public AccessibleObjectID ObjectId { get; }

       /// <summary>
       /// Identifies the id of the child that triggered the event if it was not generated by the object itself.
       /// You can determine if the event originated from a child object with <see cref="IsChildEvent"/>.
       /// </summary>
       public int ChildId { get; }

       /// <summary>
       /// The id of the thread that generated this event.
       /// </summary>
       /// <remarks>
       /// This does not refer to managed thread ids.
       /// </remarks>
       public uint EventThreadId { get; }

       /// <summary>
       /// Time in milliseconds when the event was generated since the system started.
       /// </summary>
       public uint EventTime { get; }

       /// <summary>
       /// The date and time the event was generated.
       /// </summary>
       public DateTime EventDate => DateTime.Now.AddMilliseconds(EventTime - Environment.TickCount);

       /// <summary>
       /// A value indicating whether the event was triggered by a child of the given object.
       /// The child can be identified via <see cref="ChildId"/>.
       /// </summary>
       public bool IsChildEvent => !IsOwnEvent;

       /// <summary>
       /// A value indicating whether the event was triggered by given object (not a child).
       /// </summary>
       public bool IsOwnEvent => ChildId == CHILDID_SELF;

       /// <summary>
       /// Constructs a new <see cref="WinEventHookEventArgs"/> instance witht the given data.
       /// </summary>
       /// <param name="hookHandle">The handle to the event hook callback.</param>
       /// <param name="eventType">The type of the event that occured.</param>
       /// <param name="windowHandle">The handle of the window that generated the event.</param>
       /// <param name="objectId">Identifies the object associated with the event</param>
       /// <param name="childId">Identifies the id of the child that triggered the event if it was not generated by the object itself.</param>
       /// <param name="eventThreadId">The id of the thread that generated this event.</param>
       /// <param name="eventTime">Time in milliseconds when the event was generated since the system started.</param>
       public WinEventHookEventArgs(IntPtr hookHandle, WindowEvent eventType, IntPtr windowHandle, AccessibleObjectID objectId, int childId, uint eventThreadId, uint eventTime)
       {
           HookHandle = hookHandle;
           EventType = eventType;
           WindowHandle = windowHandle;
           ObjectId = objectId;
           ChildId = childId;
           EventThreadId = eventThreadId;
           EventTime = eventTime;
       }
   }

订阅

 _hook = new WindowEventHook(WindowEvent.EVENT_SYSTEM_FOREGROUND, WindowEvent.EVENT_OBJECT_HIDE);  //这里只监视3-32771,打开关闭激活都在这个区间内
 _hook.EventReceived += Hook_EventReceived;
 _hook.HookGlobal();

事件

private static void Hook_EventReceived(object? sender, WinEventHookEventArgs e)
  {
      //if (e.ObjectId != AccessibleObjectID.OBJID_WINDOW) //过滤掉自己,只监视非自己的窗口
      //{
      //    return;
      //}
      switch (e.EventType)
      {
          case WindowEvent.EVENT_OBJECT_CREATE:  //窗口打开
          case WindowEvent.EVENT_OBJECT_SHOW:   //窗口显示 
              var title = GetWindowTitle(e.WindowHandle); //获取标题
              StringBuilder className = new StringBuilder();  
              GetClassName(e.WindowHandle, className, 7);  //获取className
              GetWindowRect(e.WindowHandle, out RECT rc);    //获取窗口大小 
              break;
          case WindowEvent.EVENT_SYSTEM_FOREGROUND: // 窗口激活 
              break;
          case WindowEvent.EVENT_OBJECT_DESTROY://窗口关闭 
          case WindowEvent.EVENT_OBJECT_HIDE:   //窗口隐藏  
              break;
      }
  }

 

posted @ 2025-07-23 14:32  众爱卿平身2  阅读(18)  评论(0)    收藏  举报