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

本文介绍如何使用.NET Framework中的NativeWindow类来拦截窗口消息并创建子窗口控件。通过两个示例类MyNativeWindowListener和MyNativeWindow展示了如何监听激活应用消息及创建自定义窗口。

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.


 
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

<think> 我们正在讨论的是DHCP配置中的`vendor option space`语句。根据描述,该语句用于指定一个选项空间(option space),以便在接收到`vendor-encapsulate-options`选项时进行解码。同时,`dhcp-vendor-identifier`可用于向服务器请求特定类别的厂商选项。 ### 解释与用法 1. **vendor option space "name";** 此语句定义了一个名为`name`的选项空间,该空间用于解析从服务器接收到的`vendor-encapsulate-options`(通常对应Option 43)中的厂商特定选项。每个厂商可以有自己的选项空间,以便正确解释其私有选项。 2. **dhcp-vendor-identifier** 这是一个客户端配置,用于指定客户端希望从服务器获取哪个厂商的选项。它通常与`vendor option space`配合使用,以确保客户端能够正确解释服务器返回的厂商特定选项。 ### 配置示例 在`dhclient.conf`中,可以这样配置: ```conf # 定义厂商选项空间(假设厂商名为"MY_VENDOR") vendor option space MY_VENDOR; # 在MY_VENDOR空间中定义私有选项(例如选项1,假设是一个IP地址) option MY_VENDOR.my-option code 1 = ip-address; # 指定客户端请求的厂商标识符(即告诉服务器客户端属于哪个厂商) send dhcp-vendor-identifier "MY_VENDOR"; # 请求服务器返回厂商特定选项(Option 43) request vendor-encapsulated-options; ``` ### 工作流程 1. 客户端在DHCP请求中通过`dhcp-vendor-identifier`(Option 60)发送厂商标识字符串"MY_VENDOR"。 2. 服务器收到后,在响应中通过`vendor-encapsulated-options`(Option 43)返回针对该厂商的配置选项。 3. 客户端使用`vendor option space MY_VENDOR;`定义的选项空间来解析Option 43中的二进制数据。 ### 注意事项 - **选项空间定义**:必须预先定义选项空间内的私有选项格式(如字符串、IP地址、整数等)。 - **多厂商支持**:可以定义多个vendor option space,但每次请求只能使用一个厂商标识符。 - **兼容性**:并非所有DHCP服务器都支持返回任意厂商的Option 43,需确保服务器端已配置相应选项。 ### 调试技巧 使用`dhclient -d`调试时,观察以下关键点: ``` Option: dhcp-vendor-identifier (60) = "MY_VENDOR" Option: vendor-encapsulated-options (43) = hex:0104C0A80102 ``` 客户端日志应显示解析后的选项: ``` MY_VENDOR.my-option = 192.168.1.2 ``` §§相关问题§§ 1. 如何在ISC DHCP服务器端配置针对特定厂商标识符(例如"MY_VENDOR")返回自定义Option 43? 2. 当客户端需要支持多个不同厂商的Option 43解析时,应如何配置dhclient.conf? 3. 在vendor option space中定义的私有选项与标准DHCP选项(如Option 3)冲突时如何处理? 4. 如何通过tcpdump抓包验证vendor-encapsulated-options(Option 43)的二进制数据格式? 5. 在DHCPv6协议中,厂商特定选项的请求和解析与IPv4有何不同? </think> ### DHCP `vendor option space` 深度解析 `vendor option space` 是 DHCP 配置中的高级指令,用于定义**厂商专属选项空间**,其核心作用是为 `vendor-encapsulate-options`(Option 43)提供解码框架。以下是关键技术细节: #### 工作机制图解 ```mermaid graph LR A[客户端] -->|1. 发送 dhcp-vendor-identifier| B(DHCP服务器) B -->|2. 返回 vendor-encapsulate-options| A C[vendor option space] -->|3. 提供解码规则| D[Option 43 内容] ``` ### 完整配置示例 ```conf # 定义厂商专属选项空间 vendor option space VendorX; # 在选项空间中声明私有选项 option VendorX.option1 code 1 = text; option VendorX.option2 code 2 = ip-address; option VendorX.option3 code 3 = unsigned integer 16; # 指定客户端请求的厂商标识 dhcp-vendor-identifier "VendorX-DeviceV2"; # 启用厂商选项封装 option vendor-encapsulate-options VendorX; ``` ### 核心组件解析 1. **`vendor option space`** 创建命名空间,用于定义厂商私有选项的编码规则: ```conf vendor option space Cisco; option Cisco.tftp-server code 1 = ip-address; option Cisco.config-file code 2 = text; ``` 2. **`dhcp-vendor-identifier`** 客户端声明自身设备类型,触发服务器返回对应厂商选项: ```conf # 思科IP电话标识 dhcp-vendor-identifier "Cisco IP Phone 7841"; # 微软超V主机标识 dhcp-vendor-identifier "Hyper-V"; ``` 3. **`vendor-encapsulate-options`** 服务器通过 Option 43 返回二进制封装数据: ``` Option 43: Hex(0104C0A80102) → Cisco.tftp-server=192.168.1.2 ``` ### 企业级应用案例 **场景:自动配置IP电话系统** ```conf # dhclient.conf vendor option space Yealink; option Yealink.provision_url code 1 = text; option Yealink.firmware_ver code 2 = text; dhcp-vendor-identifier "Yealink T54W"; # 服务器响应示例 option vendor-encapsulate-options = 01:0C:68:74:74:70:3A:2F:2F:70:78:65:2F # provision_url=http://pxe/ 02:05:32:2E:30:2E:31; # firmware_ver=2.0.1 ``` ### 调试与验证 1. **客户端请求检查** ```bash tcpdump -i eth0 -vvv 'port 67 and (udp[247:4] = 0x63825363)' ``` 观察字段:`Option 60: Vendor class identifier = "Yealink T54W"` 2. **服务器响应解析** ```bash dhclient -d -1 -sf /sbin/dhclient-script eth0 ``` 输出关键日志: ``` DHCPACK: Vendor-Encapsulated Options = 01:0C:68:74:74:70... Yealink.provision_url = 'http://pxe/' ``` ### 跨平台兼容方案 | 系统 | 配置文件 | 特殊要求 | |------------|--------------------------|-----------------------------| | **ISC DHCP** | `/etc/dhcp/dhcpd.conf` | 需配置 `vendor-option-space` | | **dnsmasq** | `/etc/dnsmasq.conf` | 使用 `dhcp-option-force=43,...` | | **Windows** | 注册表 `HKEY_LOCAL_MACHINE\...` | 需安装厂商插件 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值