WndProc 視窗處理函數【一】

前言

在C# 中,WndProc 是一個用於處理 Windows 消息的函數,通常在創建自定義控件或重寫窗口行為時使用。

它是 Windows 消息循環的核心部分之一。

  • WndProc 是 Window Procedure(視窗處理函數)的縮寫。
  • 它是 Windows 系統用來 傳遞和處理視窗訊息(Messages)的主要機制。
  • 每個視窗(或控制項)都可以有一個視窗過程,用來接收並處理如滑鼠點擊、鍵盤按鍵、繪圖等事件

下面會有幾個繼承自 Form 或 Control 的Class中重寫 WndProc 的方法



1. 攔截關閉訊息 (WM_CLOSE)

protected override void WndProc(ref Message m)
{
const int WM_CLOSE = 0x0010;
if (m.Msg == WM_CLOSE)
{
DialogResult result = MessageBox.Show("確定要關閉應用程式嗎?", "提示", MessageBoxButtons.YesNo);
if (result == DialogResult.No)
return; // 阻止關閉
}
base.WndProc(ref m);
}



2. 禁用最大化按鈕 (WM_SYSCOMMAND)

protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MAXIMIZE = 0xF030;
if (m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_MAXIMIZE)
{
// 阻止最大化
return;
}
base.WndProc(ref m);
}



3. 處理熱鍵 (WM_HOTKEY)

[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
RegisterHotKey(this.Handle, 1, 0x0002, (int)Keys.F12); // Ctrl + F12
}
protected override void WndProc(ref Message m)
{
const int WM_HOTKEY = 0x0312;
if (m.Msg == WM_HOTKEY)
{
int id = m.WParam.ToInt32();
if (id == 1)
{
MessageBox.Show("熱鍵 Ctrl+F12 被觸發!");
}
}
base.WndProc(ref m);
}



4. 攔截鼠標右鍵 (WM_RBUTTONDOWN)

protected override void WndProc(ref Message m)
{
const int WM_RBUTTONDOWN = 0x0204;
if (m.Msg == WM_RBUTTONDOWN)
{
MessageBox.Show("右鍵點擊被攔截!");
return;
}
base.WndProc(ref m);
}



5. 攔截鍵盤輸入 (WM_KEYDOWN)

protected override void WndProc(ref Message m)
{
const int WM_KEYDOWN = 0x0100;
if (m.Msg == WM_KEYDOWN)
{
Keys key = (Keys)m.WParam.ToInt32();
if (key == Keys.Escape)
{
MessageBox.Show("你按下了 ESC 鍵!");
return;
}
}
base.WndProc(ref m);
}



6. 顯示滑鼠移動位置 (WM_MOUSEMOVE)

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MouseMoveForm : Form
{
private const int WM_MOUSEMOVE = 0x0200;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
// 從 LParam 取得 X 和 Y 座標(低位為 X,高位為 Y)
int x = m.LParam.ToInt32() & 0xFFFF;
int y = (m.LParam.ToInt32() >> 16) & 0xFFFF;
this.Text = $"Mouse Position: ({x}, {y})"; // 顯示在標題列
}
base.WndProc(ref m);
}
}
  • WM_MOUSEMOVE:當滑鼠在視窗上移動時會觸發。
  • LParam:包含滑鼠座標,低 16 位是 X,高 16 位是 Y。
  • this.Text:即視窗的標題,我們用它即時顯示滑鼠座標。


  • 建議總是呼叫 base.WndProc(ref m); 以保證其他消息能正常處理。
  • 大部分WM_* 常量可以在 WinUser.h 裡查到

WinUser.h(Windows SDK)

這個檔案是 Windows SDK 中的一部分,通常會隨著 Visual Studio 安裝或 Windows 開發環境提供。

  • 檔案位置
  • C:\Program Files (x86)\Windows Kits\10\Include\<SDK 版本號>\um\WinUser.h
    #define WM_PAINT 0x000F
    #define WM_KEYDOWN 0x0100
    #define WM_MOUSEMOVE 0x0200

在 C# 裡你不能直接使用這些 C 巨集(宏),但可以自己定義這些常數:

private const int WM_PAINT = 0x000F;
private const int WM_KEYDOWN = 0x0100;
private const int WM_MOUSEMOVE = 0x0200;

留言

2025

05-12C# 多執行緒程(Multithreading)【一】
05-05WMI (Windows管理規範)與WQL(WMI 查詢語言)
04-30WndProc 視窗處理函數【三】USB裝置插入/移除偵測
04-29WndProc 視窗處理函數【二】Windows Messages (WM_*) 分類清單
04-29WndProc 視窗處理函數【一】

2024

11-27SPI Flash 操作 (Read/Write/Erase)
11-19Rotary Encoder Switch 旋轉編碼開關
11-14Command Line Interface - CLI via UART
11-14【STM32】USB HID - Volume Control
11-13【STM32】USB Custom HID
11-12【STM32】USB HID Keyboard + Mouse
11-12【STM32】USB HID Keyboard
11-12【STM32】USB HID Mouse
10-15SSD1306 128x64 OLED 【五】Wokwi Animator
09-2432F429IDISCOVERY - - LTDC [3] + FMC (SDRAM) + FatFS
09-2432F429IDISCOVERY - - LTDC [2] + FMC (SDRAM)
09-20STM32 + FatFs + SD card via SPI【三】FatFS指令操作II
09-19STM32 + FatFs + SD card via SPI【二】FatFS指令操作
09-18STM32 + FatFs + SD card via SPI【一】移植FatFS
09-0232F429IDISCOVERY - - LTDC [1]
04-17SSD1306 128x64 OLED 【四】Adafruit / GFX Library
04-17Arduino - Serial Plotter繪圖儀
04-16SSD1306 128x64 OLED 【三】
04-15SSD1306 128x64 OLED 【二】 Datasheet
04-12SSD1306 128x64 OLED 【一】I2C版本
03-20【freeRTOS】vTaskDelay 與 vTaskDelayUntil 的差異
03-19【freeRTOS】API功能列表
03-18【freeRTOS】Day1
03-08MBR和Blank project的差別
03-05刪除註冊檔registry的資料
02-27DFU over Bluetooth Low Energy
02-27nRF Util - 使用手冊
02-26nRF Command Line Tools
02-20建立BootLoader settings
02-19Secure DFU packet (ZIP) build 建立含袐鑰的Zip檔
02-19Secure DFU via BLE
02-19Secure DFU via UART
02-16nRF Util 安裝
01-16nRF52840 ic升級成nRF52840 Dongle的程式

2023

11-21[ SEGGER Embedded Studio ] 新增header files
11-21[ SEGGER Embedded Studio ] 編譯nRF52840時遇到的問題
11-07Arduino Nano ESP32 - Debugging除錯模式
11-03Git快速入手 - 使用Git GUI
10-30Git快速入手 - 使用Git Bash
10-12程式碼高亮顯示 -- google-code-prettify

2022

11-30[EZ-PD] CCG6DF CCG6SF的Host SDK遇到編譯錯誤(一)

2019

05-27[ Eagle PCB ] 合板成品
05-23#CASE_001_USB_TOOL_RL78_G12
05-22[ Eagle PCB ] 初次洗板
05-21[ Eagle PCB ] Panelize 併板
05-20[ Eagle PCB ] 建立自己的Library及元件
05-20[ Eagle PCB ] 添加library及元件
05-20[ Eagle PCB ] Introduce

2018

04-25[ TCP test Tool ] 好用的TCP Server/Client工具
01-16RZ/A1H -[0]- Renesas RZ/A1H YR0K77210S009BE BSP環境架設

2017

12-11EZ USB Suit使用JLink online debug FX3
10-20RL78 -[12]- CS+_CACX_Lab5_LowPower mode
10-16RL78 -[11]- CS+_CACX_Lab4_ADC_溫度感測
10-13RL78 -[10]- CS+_CACX_Lab4_ADC_內部參考電壓
10-13RL78 -[9]- CS+_Lab3_I2C + MPU6050
10-13RL78 -[8]- CS+_Lab2_Uart transmit
10-12RL78 -[7]- Renesas Flash Programmer 獨立燒錄軟體
10-12RL78 -[6]- CS+_雜記
10-12RL78 -[5]- CS+_tracking variables on debug mode
10-12RL78 -[4]- CS+_顯示ROM與RAM的使用size
10-12RL78 -[3]- CS+_Lab1_Led blinking
10-12RL78 -[2]- CS+專案建立
10-12RL78 -[1]- 開發環境介紹
10-06ESP-01 -[0]- 硬體設置
10-06LinkIt 7688 program Renesas RL78/G12 by 1-wire
10-06LinkIt Smart 7688 -[3]- Build the firmware from source codes
10-06LinkIt Smart 7688 -[2]- 使用UART進入bootloader / kernel console
10-06LinkIt Smart 7688 -[1]- 使用SSH連接kernel console
10-06LinkIt Smart 7688 -[0]- 初次使用
07-14LinkIt Smart 7688 -[9]- Using MRAA SPI in Python
07-13LinkIt Smart 7688 -[8]- Using MRAA UART in Python
07-12LinkIt Smart 7688 -[7]- Using MRAA I2C in Python
07-12LinkIt Smart 7688 -[6]- Using MRAA PWM in Python
07-12LinkIt Smart 7688 -[5]- Using MRAA GPIO in Python
07-10LinkIt Smart 7688 -[4]- 雜記
06-29輕乳酪蛋糕 Cotton Cheesecake
06-26VirtualBox 的 Ubuntu與Windows 共用資料夾

2015

04-29偵測USB PnP