kozolika
kozolika

Reputation: 11

C# subscribe to FormClosing

I want to receive an event in my C# application when a different C# application (Form) is about to close.

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. Let's name them Container, Placer and ToPlace. I am developing Contaner and Placer in C#, Placer is a dll while Container is a WinForm. I have no access to the source code of ToPlace. In Container I have a custom control where I put in the main window of ToPlace with SetParent called from Placer. 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. In the end the goal is not to destroy the main window of ToPlace when Container exits.

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.

I also tried to install a message filter on the Container app, but this was also not successfull.

My last try before writing this question was the SetWindowsHookEx, but after installing the hook successfully the hook procedure is never called. Maybe because, I read somewhere, that the hook function must be in a win32 dll, not in a managed one. The next try would be to use the SetWinEventHook, I read about this that it is easier to get it working from 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:

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;
        }
    }
}

Upvotes: 0

Views: 671

Answers (3)

Matthew Watson
Matthew Watson

Reputation: 109597

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.

Upvotes: 1

Yahia
Yahia

Reputation: 70369

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" (i.e. 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.

Upvotes: 0

jglouie
jglouie

Reputation: 12880

How much IPC are we talking here? If it's just a simple notification, perhaps using WM_COPYDATA will work. Here's an example of this approach: 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

Upvotes: 0

Related Questions