【问题标题】:C# subscribe to FormClosingC# 订阅 FormClosing
【发布时间】:2012-12-11 15:19:25
【问题描述】:

当另一个 C# 应用程序(窗体)即将关闭时,我想在我的 C# 应用程序中接收一个事件。

我怎样才能做到这一点?这可以用反射来完成吗?

编辑:详细说明

我意识到我最初的问题不是很具体。 我将尝试更详细地描述我的目标。

所以我有 3 个应用程序。我们将它们命名为 ContainerPlacerToPlace。 我正在用 C# 开发 ContanerPlacerPlacer 是一个 dll,而 Container 是一个 WinForm。我无法访问 ToPlace 的源代码。在 Container 中,我有一个自定义控件,我在 ToPlace 的主窗口中放置了从 Placer 调用的 SetParent。目标是在 Contaner 应用程序关闭之前恢复 ToPlace 的父窗口,或者过滤掉发送到 的 WM_DESTROY 消息(或其他消息) >ToPlace 主窗口。最终目标是在 Container 退出时不破坏 ToPlace 的主窗口。

我尝试在Container中重写自定义控件的WndProc,但是没有通过父窗口发送给子窗口的消息。

我也尝试在 Container 应用上安装消息过滤器,但这也不成功。

我在写这个问题之前的最后一次尝试是 SetWindowsHookEx,但是在成功安装钩子后,钩子程序永远不会被调用。也许是因为,我在某处读到,钩子函数必须在 win32 dll 中,而不是在托管的。下一个尝试是使用 SetWinEventHook,我读到了这个,它更容易从 C# 中工作。

我尝试使用 SetWindowsHookEx 的代码如下,也许在 C# 互操作方面更有经验的人看到了一个错误并可以让它工作:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace AppMonitor
{
    public class AppMonitor
    {
        private const int WH_GETMESSAGE = 3;
        private const int HC_ACTION = 0;
        private const int PM_NOREMOVE = 0x0000;
        private const int PM_REMOVE = 0x0001;
        private const int WM_QUIT = 0x0012;

        [DllImport("user32.dll")]
        private static extern int SetWindowsHookEx(int idHook, GetMsgProcDelegate lpfn, int hMod, int dwThreadId);

        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(int hhk);

        [DllImport("user32.dll")]
        private unsafe static extern int CallNextHookEx(int hhk, int nCode, int wParam, void* lParam);

        [DllImport("user32.dll")]
        private static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

        [DllImport("kernel32.dll")]
        private static extern int GetLastError();

        private struct Msg {
            public int        hwnd;
            public int        message;
            public int      wParam;
            public int      lParam;
            public int       time;
            public int       pt;
        };

        [DllImport("kernel32.dll")]
        public static extern int LoadLibrary(string dllToLoad);

        public AppMonitor()
        { 
        }

        private int hHook;
        private int hMod;

        public event EventHandler AppClosing;

        private unsafe delegate int GetMsgProcDelegate(int code, int wParam, void* lParam);
        private unsafe GetMsgProcDelegate m_dlgt;

        private unsafe int GetMsgProc(int code, int wParam, void* lParam)
        {
            if (code != HC_ACTION || wParam != PM_REMOVE)
               return CallNextHookEx(this.hHook, code, wParam, lParam);

            Msg* msg = (Msg*)lParam;

            //if (msg.message == WM_QUIT)
            //    OnAppClosing(new EventArgs());

            return CallNextHookEx(this.hHook, code, wParam, lParam);
        }

        protected virtual void OnAppClosing(EventArgs e)
        {
            EventHandler h = AppClosing;
            if (h != null)
            {
                h(this, e);
            }
        }

        public unsafe bool setHook(int hWnd)
        {
            hMod = LoadLibrary("AppMonitor.dll"); //this dll
            int procId = 0;

            int threadId = GetWindowThreadProcessId(hWnd, out procId);
            if (threadId == 0)
                throw new System.Exception("Invalid thread Id");

            m_dlgt = GetMsgProc;
            this.hHook = SetWindowsHookEx(WH_GETMESSAGE, m_dlgt, hMod, threadId);

            if (this.hHook == 0)
                throw new System.Exception("Hook not successfull! Error code: " + GetLastError());

            return this.hHook != 0;
        }

        public bool unSetHook()
        {
            bool result = false;
            if (hHook != 0)
                result = UnhookWindowsHookEx(hHook);
            return result;
        }
    }
}

【问题讨论】:

  • 进程是否在同一台机器上?
  • 使用message hook 并查找 WM_CLOSE 消息,或者设置一个计时器来轮询其他窗口句柄是否消失引发事件。
  • 是的,进程在同一台机器上。

标签: c# winapi reflection window ipc


【解决方案1】:

如果您能以某种方式找到您想要监控的进程(通过窗口标题或如下例中的“友好”进程名称),则很容易监控它是否关闭。

以下示例显示了如何监视“记事本”进程以查看它何时关闭:

using System;
using System.Diagnostics;

// To test this program:
// (1) Start Notepad.
// (2) Run this program.
// (3) Close Notepad.
// (4) This program should print "Notepad exited".

namespace Demo
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            foreach (var process in Process.GetProcessesByName("notepad"))
            {
                Console.WriteLine("Found a Notepad process to attach to.");
                process.EnableRaisingEvents = true;
                process.Exited += process_Exited;
            }

            Console.WriteLine("Press ENTER to quit.");
            Console.ReadLine();
        }

        private static void process_Exited(object sender, EventArgs e)
        {
            Console.WriteLine("Notepad exited.");
        }
   }
}

如果需要,可以使用 Process.GetProcesses() 枚举计算机上的所有进程,并为每个进程检查 Process.MainWindowTitle 以查看是否找到了所需的进程。

【讨论】:

  • 是否有可能以某种方式获得流程的主要形式(或任何形式)?然后订阅 FormClosing 事件?
  • 不,你不能那样做。但是如果关闭主窗体也会关闭应用程序,您将能够检测到 ok。
  • 非常酷,我从不知道您可以使用 Process 类查看来自其他进程的事件。
【解决方案2】:

不,这不能通过反射来完成。

您提供的信息很少,无法给出具体答案……例如:

  • 如果您有两个“正在开发”的进程(即可以更改两者的源代码),那么解决方案可能很简单
    否则它会变得复杂并且可能不可靠

  • 如果两个进程都在同一台机器上,那么可能通过挂钩
    否则,您将需要更复杂的实现。

  • 如果您只是想被告知表单已关闭,那么这是可能的
    如果您想在其他进程的上下文中执行某些事情,甚至自己处理该事件,这非常复杂,并且取决于您到底想要什么,这可能是不可能的......

请提供更多详细信息,以便获得具体答案。

【讨论】:

  • 你还需要提供更多细节......你的目标到底是什么?
  • 问题描述中添加了更多数据
  • 在阅读了您的详细描述后,您的目标仍然不清楚......如果您正在开发容器,为什么不直接处理它的退出事件并在那里恢复 ToPlace 的父级?
  • 是的,你是对的。我把一个简单的问题复杂化了。感谢您帮助解决这个问题。
【解决方案3】:

我们在这里谈论多少 IPC?如果它只是一个简单的通知,也许使用WM_COPYDATA 会起作用。这是这种方法的一个示例:http://www.codeproject.com/Articles/17606/NET-Interprocess-Communication

命名管道和/或内存映射文件的使用也可能是合适的,如果有更多,或者不仅仅是一个简单的“我正在关闭”消息。命名管道方法的示例在这里:http://msdn.microsoft.com/en-us/library/bb546085.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-15
    • 2020-10-17
    相关资源
    最近更新 更多