如何监听来自第 3 方 DLL 的表单的“Form.Shown”和“Window.Closing”?

Posted

技术标签:

【中文标题】如何监听来自第 3 方 DLL 的表单的“Form.Shown”和“Window.Closing”?【英文标题】:How to listen for "Form.Shown" and "Window.Closing" for a form from a 3rd party DLL? 【发布时间】:2019-10-25 02:22:57 【问题描述】:

我的 C# 应用调用第 3 方 DLL。此 DLL 可能显示也可能不显示窗口(窗体)。我希望在显示此窗口时注册一个回调/通知/事件,并在窗口关闭时注册另一个(通过 X 或“关闭”按钮)。如果我能说出导致操作的按钮的名称(即:如果他们按“关闭”或“X”,而不是按“购买”,我会做一些不同的事情)

我无权访问此 DLL 的源代码,并且标头未指定表单。

我所需要的可能吗?

如果您想知道,请联系PaddleSDK

【问题讨论】:

您可以使用 SetWinEventHook, for example setting a callback for EVENT_OBJECT_INVOKED, so when a Button is pressed (invoking its default action) WinEventProc` 将通知 hWndID 调用的子控件。您可以使用 UI 自动化来完成所有这些操作,以及拦截 Window 创建/销毁。 WindowPatter.WindowOpenedWindowPatter.WindowClosed 事件通知(任何)窗口何时打开/关闭。可以为任何 Window 子/后代控件引发特定事件(对于 Windows 窗体尤其简单,对于 WPF 窗口则更少)。 SeWinEventHook 实现参见此处Move window when external application's window moves(您只需更改通知的事件)。 This is a base implementation 使用 UI 自动化的 Window Watcher 过程;它展示了如何检测 Window Opened 事件并与该 Window 交互。 对我来说,EVENT_OBJECT_INVOKED 永远不会触发 :(。您似乎建议了两种不同的方法:1:SetWinEventHook() 和 2:UI 自动化。它们不相关,对吧?那么哪一个?我可以'找不到关于“WindowPatter.WindowOpened”的任何信息你的意思是模式吗?我需要做的是,当窗口第一次显示时,隐藏一个按钮(名为“输入许可证”),所以它消失了(用户不能“选项卡”)。然后,当窗口通过名为“关闭”或通过“X”的按钮关闭时,我需要知道。(如果我能区分该窗口和刚刚关闭的窗口,因为用户否则完成了他们的业务) 如果我确实使用 UI 自动化来捕捉显示的窗口,并找到我想要隐藏的按钮,那么我该如何隐藏它? 是的,抱歉,拼错了(或者平板电脑拼错了:)WindowPattern.WindowOpenedEvent,WindowPattern.WindowClosedEvent。您可以通过 UIAutomation 获得 WinForms 中的任何控件。但是本机 UIA 的 .Net 实现是,比如说,部分。一旦你找到了你想要的元素(通常是通过名字和ControlType),你可以调用ShowWindow([hWnd], SW_HIDE)来隐藏元素。 【参考方案1】:

使用 SetWinEventHook

互联网充满了愚蠢的方法,但这是正确的方法。轮询很糟糕(可以吗?)。

如果您在 *** 上搜索 SetWinEventHook 并查找 c# 匹配项,您会发现很多示例可供使用。

【讨论】:

looking here 我找不到“开窗”和“关窗”或“按下按钮”的事件。我看到“EVENT_OBJECT_INVOKED”,但不会触发出现的窗口中的按钮,也不会触发“X”。我可以获取“前窗已更改”(EVENT_SYSTEM_FOREGROUND)的事件,但这并没有说明打开或关闭。请注意“EVENT_SYSTEM_DIALOGSTART / END”事件似乎没有被触发,大概是因为窗口不是对话框。我听说过使用 UI 自动化的建议,这是更好的选择吗?【参考方案2】:

好的哇,这似乎工作:(感谢大家的提示!)

    private int[]           i_checkoutWindID;

    private void    RegisterEventListener()
    
        Automation.AddAutomationEventHandler(
            WindowPattern.WindowOpenedEvent,
            AutomationElement.RootElement,
            TreeScope.Children,
            (sender, e) => 
        
            AutomationElement       element = sender as AutomationElement;
            string                  automationID = element.Current.AutomationId;

            if (automationID != kLicenseWindowAutomationID) return;

            i_checkoutWindID = element.GetRuntimeId();

            AutomationElement licenseButton = element.FindFirst(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.AutomationIdProperty, kLicenseButtonAutomationID));

            if (licenseButton != null) 
                IntPtr      hwnd = new IntPtr(licenseButton.Current.NativeWindowHandle);
                Control     buttonRef = Control.FromHandle(hwnd);

                HideButton_Safe(buttonRef);
            
        );

        Automation.AddAutomationEventHandler(
            WindowPattern.WindowClosedEvent,
            AutomationElement.RootElement,
            TreeScope.Subtree,
            (sender, e) => 
        
            WindowClosedEventArgs       args = e as WindowClosedEventArgs;

            if (Automation.Compare(args.GetRuntimeId(), i_checkoutWindID)) 
                Array.Clear(i_checkoutWindID, 0, i_checkoutWindID.Length);
                <do your "window closed" callback here>;
            
        );
    

    private void HideButton_Safe(Control buttonRef)
    
        if (buttonRef.InvokeRequired) 
            var d = new SafeCallDelegate_ButtonHide(HideButton_Safe);
            buttonRef.Invoke(d, new object[]  buttonRef );
         else 
            buttonRef.Hide();
        
    

【讨论】:

以上是关于如何监听来自第 3 方 DLL 的表单的“Form.Shown”和“Window.Closing”?的主要内容,如果未能解决你的问题,请参考以下文章

如何重新加载经常崩溃的第 3 方 DLL

Visual C++:插件 DLL 使用的第 3 方 DLL 的位置?

Form表单和模板引擎

在运行时加载第 3 方 DLL 失败并出现未处理异常(Log4CXX、ActiveMQ)

前后端交互:form表单与模板引擎

Django第3章: Form表单