0

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

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

编辑:详细说明

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

所以我有3个应用程序。让我们将它们命名为ContainerPlacerToPlace。我正在用 C# 开发ContanerPlacerPlacer是一个 dll,而Container是一个 WinForm。我无权访问ToPlace的源代码。在Container中,我有一个自定义控件,我在ToPlace的主窗口中放置了从Placer调用的 SetParent 。目标是在关闭Contaner应用程序之前恢复ToPlace的父窗口,或者过滤掉发送到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;
        }
    }
}
4

3 回答 3

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 以查看是否找到了所需的进程。

于 2012-12-11T15:45:03.473 回答
0

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

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

于 2012-12-11T15:28:30.663 回答
0

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

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

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

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

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

请提供更多详细信息,以便有可能给出具体答案。

于 2012-12-11T15:29:27.800 回答