c# - A study on the NativeWindow - encapsulate window handle and procedure

NativeWindow gives you a way to way to intercept the window messages (are you familiar with the Windows Message Pump?) 

Aslo, this class have provided with some default impl on manipulating handles and create child handle/control.s

 

NativeWindow for Message Intercepting and Handle Manipulation

 

We will first start from the basic example of the NativeWindow. This is an example that is taken from the MSDN NativeWindow Class page. 

 

The code is as below.

    // NativeWindow class
    // please check 
    //   http://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.aspx
    // Summary description for Form1.
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class Form1 : System.Windows.Forms.Form
    {
        private MyNativeWindowListener nwl;
        private MyNativeWindow nw;

        internal void ApplicationActivated(bool ApplicationActivated)
        {
            // The application has been activated or deactivated
            System.Diagnostics.Debug.WriteLine("Application Active = " + ApplicationActivated.ToString());
        }

        private Form1()
        {
            this.Size = new System.Drawing.Size(300, 300);
            this.Text = "Form1";

            nwl = new MyNativeWindowListener(this);
            nw = new MyNativeWindow(this);

        }

        // The main entry point for the application.
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

    }


    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    internal class MyNativeWindowListener : NativeWindow
    {

        // Constant value was found in the "windows.h" header file.
        private const int WM_ACTIVATEAPP = 0x001C;

        private Form1 parent;

        public MyNativeWindowListener(Form1 parent)
        {

            parent.HandleCreated += new EventHandler(this.OnHandleCreated);
            parent.HandleDestroyed += new EventHandler(this.OnHandleDestroyed);
            this.parent = parent;
        }

        internal void OnHandleCreated(object sender, EventArgs e)
        {
            // Window is now created, assign handle to NativeWindow.
            AssignHandle(((Form1)sender).Handle);
        }

        internal void OnHandleDestroyed(object sender, EventArgs e)
        {
            // Window was destroyed, release hook.
            ReleaseHandle();
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            // Listen for operating system messages 
            switch (m.Msg)
            {
                case WM_ACTIVATEAPP:
                // Notify the form that this message was received. 
                // Application is activated or deactivated,  
                // based upon the WParam parameter.
                parent.ApplicationActivated(((int)m.WParam != 0));
                break;
            }
            base.WndProc(ref m);
        }

    }

     // MyNativeWindow class to create a window given a class name.
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    internal class MyNativeWindow : NativeWindow
    {
        // Constant values were found in the "windows.h" header file.
        private const int WS_CHILD = 0x40000000,
                          WS_VISIBLE = 0x10000000,
                          WM_ACTIVATEAPP = 0x001C;

        private int windowHandle;

        public MyNativeWindow(Form parent)
        {
            CreateParams cp = new CreateParams();

            // Fill in the CreateParams details.
            cp.Caption = "Click here";
            cp.ClassName = "Button"; // the classname has some predefined enumerations so that it knows what type of Control/handle to create.

            // Set the position on the form
            cp.X = 100;
            cp.Y = 100;
            cp.Height = 100;
            cp.Width = 100;

            // Specify the form as the parent.
            cp.Parent = parent.Handle;

            // Create as a child of the specified parent 
            cp.Style = WS_CHILD | WS_VISIBLE;

            this.CreateHandle(cp);
        }
        
        // Listen to when the handle changes to keep the variable in sync
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void OnHandleChange()
        {
            windowHandle = (int)this.Handle;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_ACTIVATEAPP:
                    // DO someting here in response to messages
                    break;
            }
            base.WndProc(ref m);
        }

    }

 the first derived class to MyNativeWindowListener class intercept the calls to Form's. It assign/destroy the handle (the handle of the main form is assigned) so that the interceptin is done. it will listen to the WM_ACTIVATE call and active the main form.)

the second class MyNativeWindow, what it does is to cretae a child handle based on the main handled passed in, the main handle is the handle of the form's. and this shows how you can leverage the NativeWindow to manipulate the handles/Controls.
If you run this code, you will see the following as a result.

c# - A study on the NativeWindow - encapsulate window handle and procedure
 
NativeWindow to provide message pump


There is a post - Message Pump in .Net windows Service where a method of using the NativeWindow to simulate the Messgae Pump is discussed. and here is the detailed code.

    public class MessgePumpManager
    {
        private readonly Thread messagePump;

        private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);

        public MessgePumpManager()
        {
            // start message pump in its own thread
            messagePump = new Thread(RunMessagePump) { Name = "ManualMessagePump" };
            messagePump.Start();
            messagePumpRunning.WaitOne();
        }

        // the message pump thread
        private void RunMessagePump()
        {
            // Create control to handle windows messages 
            MessageHandler messageHandler = new MessageHandler();

            // Do something
            // like initialize a thirdparty library
            //DLL.Init(messageHandler.Handle);

            Console.WriteLine("Message Pump thrad Started");
            messagePumpRunning.WaitOne();
            Application.Run();
        }
    }

    internal class MessageHandler : NativeWindow
    {
        public event EventHandler<MessageData> MessageReceived;

        public MessageHandler()
        {
            CreateHandle(new CreateParams());
        }

        protected override void WndProc(ref Message msg)
        {            
            // filter message here for your purposes
            EventHandler<MessageData> handler = MessageReceived;
            MessageData msgData = new MessageData(msg);
            if (handler != null) handler(this, msgData);

            base.WndProc(ref msg);
        }
    }

    // Struct cannot be inherited.
    class MessageData : EventArgs
    {
        private Message _message;
        public MessageData(Message message)
        {
            _message = message;
        }
    }

 as you can see some third party or some component may rquires a windows message pump to work, what has shown above is that the message pump handle (denoted by the MessageHandler class - inherits from the NativeWindow class ) is passed to the Third party componet, which could be in native code and be able to consume the handle and reconstruct his own message pump out of it.
The current thread (the thread which enters MessagePumpManager will block until the third party component has done with the handle - a typical way of implementing the message pump). 


 

References

NativeWindow Class

Message Pump in .Net windows Service

你可能感兴趣的:(C#)