什么是事件管理器

发布时间:2023年12月29日

什么是事件管理器 – WhiteNight's Site

标签:Windows, 日志

Windows调试必备工具

你知道吗,windows系统左下角的”win徽标“,除了可以左键打开“开始”菜单,还可以右键点击。

其中有个叫事件管理器的东西。今天碰到个问题,就是通过事件管理器+GitHub Copilot解决的。接下来详细说说是怎么解决的。

最近在写一个计时器程序,基础要求CPU占用要小于10%。试了下python随便写了个带gui的程序,cpu占用最少也要15%,那只能skip了。

接下来想起来之前写winui3的时候,还听说过个叫winform的东西,用的是C#,随便试了一下,占有率只有0.3%(还得是C/C++/C#),那就决定是你啦!

写者注

至于为什么不用winui3?winui3和winform一对比实在是太笨重了,虽然ui库+xaml确实很方便也很好用,但是对于计时器这种简单的应用还是杀鸡用牛刀了。

接下来这个计时器还有个要求,要无论在什么情况下,它都能始终“置顶”。意思就是其他应用程序的窗口不管怎么动,都不能遮挡住这个计时器,系统本身的窗口除外。

实现这个有两种方法,一种是直接把TopMost设为true,但这个方法不知道为什么在其他主机上面跑会失效,所以换了种方法。导入user32.dll来调用windows本身的API。


        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [DllImport("user32.dll")]
        static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);

        const uint SWP_NOMOVE = 0x0002;
        const uint SWP_NOSIZE = 0x0001;

        const uint EVENT_SYSTEM_FOREGROUND = 0x0003;

        const uint WINEVENT_OUTOFCONTEXT = 0x0000;

        void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            if (!this.TopMost)
            {
                SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
            }
        }

当然这么写还不够,应用窗口创建的时候这个函数就得跟着。而且我为了以防万一,写了个钩子函数监听窗口变化,只要不是置顶就重新置顶它。

然而这里就出现了一个问题:这个程序在执行一段时间后会自己闪退。后面发现是在运行了一段时间后再切换窗口就会闪退。

我反正是毫无头绪。内存泄漏?跑了一下并不是,稳定37mb。CPU占用过高?0%-0.5%的占用。本机内存不够?昨天刚申请的16g内存,才跑到10g。更要命的是vs本身的报错不明确,只说窗口的创建有异常,其他啥都看不到。

接下来想起来有个叫事件管理器的玩意,抱着试试的心态看了看,诶,还别说,真的有更详细的报错信息。

垃圾回收?把这段扔给copilot解释看看。

我是没学过.net,委托是啥,看不懂。但我大致、直觉、或许、也许感觉是钩子函数出了问题。我想象了一下,可能是这个委托啥的被.net自动回收了,但windows api不知道。变成了委托用钩子钩住了Windows api,windows api也知道有个东西钩着自己,但把东西传过去的时候发现原来只剩个钩子了,委托早就被.net回收了。(怎么有点恐怖游戏的意思)

那就照着改呗,不让垃圾收集器回收这个委托不就好了。

namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        private WinEventDelegate _winEventDelegate;
        public Form1()
        {
            InitializeComponent();
            SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

            _winEventDelegate = new WinEventDelegate(WinEventProc);
            SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _winEventDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
        }
    }
}

改完之后再跑,不会闪退了,问题解决。

具体的原理我是还没搞懂,只是有个大概的思路。主要是.net暂时没有深入理解的打算,还是先把python搞熟练再说。

文章来源:https://blog.csdn.net/white_night_SZTU/article/details/135295932
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。