简体   繁体   English

C#订阅FormClosing

[英]C# subscribe to FormClosing

I want to receive an event in my C# application when a different C# application (Form) is about to close. 当另一个C#应用程序(窗体)即将关闭时,我想在C#应用程序中收到一个事件。

How can I accomplish this? 我该怎么做? Can this be done with Reflections? 可以使用反射来完成吗?

Edit: detailed description 编辑:详细说明

I realize that my original question is not very specific. 我意识到我最初的问题不是很具体。 I will try to describe my goal in more detail. 我将尝试更详细地描述我的目标。

So I have 3 applications. 因此,我有3个应用程序。 Let's name them Container , Placer and ToPlace . 让我们将它们命名为ContainerPlacerToPlace I am developing Contaner and Placer in C#, Placer is a dll while Container is a WinForm. 我正在用C#开发ContanerPlacerPlacer是dll,而Container是WinForm。 I have no access to the source code of ToPlace . 我无权访问ToPlace的源代码。 In Container I have a custom control where I put in the main window of ToPlace with SetParent called from Placer . 集装箱我在那里,我把ToPlace的主窗口的setparent从普莱瑟称为自定义控制。 The goal would be to restore the parent window for the ToPlace before the Contaner app is closed, or to filter out the WM_DESTROY message (or other message) sent to the ToPlace main window. 所述目标将是恢复父窗口的ToPlaceContaner应用程序被关闭之前,或过滤掉发送到ToPlace主窗口中的WM_DESTROY消息(或其它消息)。 In the end the goal is not to destroy the main window of ToPlace when Container exits. 最后,目标是在Container退出时不破坏ToPlace的主窗口。

I tried to override the WndProc of the custom control in Container , but the message to the child window is not sent via the parent window. 我试图在Container中重写自定义控件的WndProc,但是到子窗口的消息不是通过父窗口发送的。

I also tried to install a message filter on the Container app, but this was also not successfull. 我还尝试在Container应用上安装消息过滤器,但这也没有成功。

My last try before writing this question was the SetWindowsHookEx, but after installing the hook successfully the hook procedure is never called. 在编写此问题之前,我的最后一次尝试是SetWindowsHookEx,但在成功安装挂钩之后,永远不会调用该挂钩过程。 Maybe because, I read somewhere, that the hook function must be in a win32 dll, not in a managed one. 可能是因为,我在某处读到,钩子函数必须在win32 dll中,而不是在托管的dll中。 The next try would be to use the SetWinEventHook, I read about this that it is easier to get it working from C#. 下一个尝试是使用SetWinEventHook,我读到有关它的信息,它很容易从C#中运行。

The code I tried with SetWindowsHookEx is below, maybe somebody who is more experienced in C# interop sees a bug and can get it working: 我在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;
        }
    }
}

If you can somehow find the process that you want to monitor (by window title or, as in the example below, by "friendly" process name), it is easy to monitor for it closing. 如果您能以某种方式找到要监视的进程(通过窗口标题或以下示例中的“友好的”进程名称),则很容易监视其关闭。

The example below shows how you can monitor the "Notepad" process to see when it closes: 下面的示例显示如何监视“记事本”进程以查看其关闭时间:

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.");
        }
   }
}

If you need to, you can use Process.GetProcesses() to enumerate all processes on the computer, and for each one check Process.MainWindowTitle to see if you've found the one you want. 如果需要,可以使用Process.GetProcesses()枚举计算机上的所有进程,对于每个进程,请检查Process.MainWindowTitle以查看是否找到了所需的进程。

How much IPC are we talking here? 我们在这里谈多少IPC? If it's just a simple notification, perhaps using WM_COPYDATA will work. 如果只是简单的通知,也许使用WM_COPYDATA Here's an example of this approach: http://www.codeproject.com/Articles/17606/NET-Interprocess-Communication 这是这种方法的示例: http : //www.codeproject.com/Articles/17606/NET-Interprocess-Communication

The usage of named pipes and/or memory mapped files may also be appropriate if there are more, or will be more than a simple "I'm closing" message. 如果有更多的消息,或者比简单的“我要关闭”消息更多,则使用命名管道和/或内存映射文件也可能是合适的。 Example of the named pipe approach is here: http://msdn.microsoft.com/en-us/library/bb546085.aspx 命名管道方法的示例在这里: http : //msdn.microsoft.com/zh-cn/library/bb546085.aspx

No, this can't be done with reflection. 不,这不能通过反射来完成。

You provide very little information to give a specific answer... for example: 您提供的信息很少,无法给出具体答案...例如:

  • IF you have both process "under development" (ie can change source code for both) then the solution might be easy 如果您都处理“开发中”(即可以更改两者的源代码),则解决方案可能很简单
    Otherwise it gets complicated and potentially unreliable 否则会变得复杂并且可能不可靠

  • IF both processes are on the same machine THEN it might be possible by hooking 如果两个进程都在同一台计算机上,则可以通过挂钩来实现
    Otherwise you will need a much more complex implementation. 否则,您将需要更复杂的实现。

  • IF you just want to be informed that the form closes THEN that is possible 如果您只是想通知表单关闭,则可以
    IF you want to execute something in the context of the other process or even handle that event yourself that is very complicated and depending on what exactly you want it might be impossible... 如果您想在其他进程的上下文中执行某些操作,或者甚至自己处理该事件,那么这非常复杂,并且取决于您确切想要什么,则可能是不可能的...

Please provide more details so that a specific answer is possible. 请提供更多详细信息,以便提供具体答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM