深入解析C++的WNDCLASS结构体及其在Windows中的应用

WNDCLASS是一个由系统支持的结构,用来储存某一类窗口的信息,如ClassStyle,消息处理函数,Icon,Cursor,背景Brush等。也就是说,CreateWindow只是将某个WNDCLASS定义的窗体变成实例。要得到某一窗口的WNDCLASS数据,可以用GetClassLong();
  RegisterClass()就是在系统注册某一类型的窗体。也就是将你提供的WNDCLASS数据注册为一个窗口类,在WNDCLASS.lpszClassName中定义该WNDCLASS的标识,无论CreateWindow或CreateWindowEx创建的窗口都必须对应一个WNDCLASS,但一个WNDCLASS可以有多个窗口对象。
  有一些系统预定义的窗口类,如:ClassName=_T("BUTTON" or "COMBOBOX" or "EDIT" or "LISTBOX" or "MDICLIENT" or "SCOLLBAR" or "STATIC"),要用这些窗体,直接用CreateWindow创建相应对象就是了。要得到某一窗口的窗口类,可以用GetClassName();
  WNDCLASS中的回调函数是窗体的消息处理函数:CALLBACK WinProc(MESSAGE msg,LPARAM lparam,WPARAM wParam);
窗口类属性定义
  结构WNDCLASS包含一个窗口类的全部信息,也是Windows编程中使用的基本数据结构之一,应用程序通过定义一个窗口类确定窗口的属性,定义如下:

typedef struct _WNDCLASS {
  UINT style;
  WNDPROC lpfnWndProc;
  int cbClsExtra;
  int cbWndExtra;
  HINSTANCE hInstance;
  HICON hIcon;
  HCURSOR hCursor;
  HBRUSH hbrBackground;
  LPCTSTR lpszMenuName;
  LPCTSTR lpszClassName;
  } WNDCLASS, *PWNDCLASS;

举例说明
例子:

long CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);//声明
//WinMain函数是所有Windows应用程序的入口,类似c语言中的main函数其功能是完成//一系列的定义和初始化,并产生消息循环。函数说明:
int WINAPI WinMain(HINSTANCE hInstance,    // handle to current instance
               HINSTANCE hPrevInstance, // handle to previous instance
               LPSTR lpCmdLine,       // command line
                int nCmdShow           // show state
)
{
    //初始化,初始化包括窗口类的定义、注册、创建窗口实例和显示窗口四部分
  HWND hwnd;
  MSG Msg;
  WNDCLASS wndclass;
  char lpszClassName[]="窗口"; //窗口类名
  char lpszTitle[]="测试窗口"; //窗口标题名
  //窗口类定义,窗口类定义了窗口的形式与功能,窗口类定义通过给窗口类数据结构WNDCLASS赋值完成
  //该数据结构中包含窗口类的各种属性
  wndclass.style =0; // 窗口类型为缺省类型CS_   Class Style
  wndclass.lpfnWndProc=WndProc; //定义窗口处理函数
  wndclass.cbClsExtra=0; //窗口类无扩展
  wndclass.cbWndExtra=0; //窗口实例无扩展
  wndclass.hInstance=hInstance; //当前实例句柄
  wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //窗口的最小化图标为缺省图标
  wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); // 窗口采用箭头光标
  wndclass.hbrBackground=(HBRUSH)(GetStockObject(WHITE_BRUSH)); //窗口背景为白色
  wndclass.lpszMenuName=NULL; //窗口无菜单
  wndclass.lpszClassName=lpszClassName; //窗口类名为“窗口”
  //以下是窗口类的注册-----------Windows系统本身提供部分预定义的窗口类,程序员也可以自定义窗口类,窗口类必须先注册后使用。
  if(!RegisterClass(&wndclass)) //如果注册失败 发出警告
     {MessageBeep(0); return FALSE;}
  //创建窗口创建一个窗口的实例由函数CreateWindow()实现
  hwnd=CreateWindow( lpszClassName, //窗口类名,创建窗口时一定要基于我们已经注册过的窗口类名,即"窗口"。
                    lpszTitle, //窗口标题名
                    WS_OVERLAPPEDWINDOW, //窗口的风格 WS_ Windows Style
                    CW_USEDEFAULT, //窗口左上角坐标值为缺省值 CW_ Create Wndow
                    CW_USEDEFAULT,
                    CW_USEDEFAULT, //窗口的高和宽为缺省值
                     CW_USEDEFAULT,
                    NULL, //此窗口无父窗口
                    NULL, //此窗口无子菜单
                    hInstance, //创建此窗口的应用程序的当前句柄
                    NULL //不使用该值
  );
  //显示窗口
  ShowWindow(hwnd,nCmdShow);
  //绘制用户区
  UpdateWindow(hwnd);
  //消息循环
  while(GetMessage(&Msg,NULL,0,0)) //GetMessage()函数是从调用线程的消息队列中取出一条消息;对于每一个应用程序窗口线程,操作系统都会为其建立一个消息队列,当我们的窗口有消息时(即所有与这个窗口线程相关的消息),操纵系统会把这个消息放到该线程的消息队列当中,我们的窗口程序就通过这个GetMessage()函数从自己的消息队列中取出一条一条具体的消息并进行响应操作。
  {
  TranslateMessage(&Msg);//对"消息对"的转化,如对键盘的WM_KEYDOWN和WM_KEYUP消息对转化为WM_CHAR消息,并且将转换后的新消息投递到我们的消息队列中去,这个转化操作不会影响原来的消息,只会产生一个新的消息。
  DispatchMessage(&Msg);//DispatchMessage()函数是将我们取出的消息传到窗口的回调函数去处理;可以理解为该函数将取出的消息路由给操作系统,然后操作系统去调用我们的窗口回调函数对这个消息进行处理。
    }
  return Msg.wParam; //消息循环结束 即程序结束时 将信息返回系统
 }

//窗口函数,窗口函数定义了应用程序对接收到的不同消息的响应,其中包含了应用程序对各种可能接受到的消息的处理过程,时消息处理分支控制语句的集合
long CALLBACK WndProc(HWND hwnd,
                   UINT message,
                    WPARAM wParam,
                    LPARAM lParam)
{
  switch(message)
  {
    case WM_DESTROY:
      PostQuitMessage(0);
    default: //缺省时采用系统消息缺省处理函数
      return DefWindowProc(hwnd,message,wParam,lParam);
  }
  return (0);
}

注:窗口回调函数的函数指针定义typedef LRESULT CALLBACK  (* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

WNDPROC OldWndProc;
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
    switch (Msg)
    {
      ......
    }
    return CallWindowProc(OldWndProc,g_Wnd,Msg,wParam,lParam);
}
OldWndProc = (WNDPROC)GetWindowLong(g_Wnd,GWL_WNDPROC);
SetWindowLong(hwnd, GWL_WNDPROC,(LPARAM)(WNDPROC)NewWndProc);

通过调用SetWindowLong函数可以修改该窗体类的回调函数。
 
CallBack 函数中的wParam和lParam有什么区别:
  WPARAM   wParam,     定义成WORD型
  LPARAM   lParam         定义成LONG型
  在Win 3.x中,WPARAM是16位的,而LPARAM是32位的,两者有明显的区别。
  在Win32 API中,WPARAM和LPARAM都是32位,所以没有什么本质的区别。
  但是习惯上,我们愿意使用LPARAM传递地址,而WPARAM传递其他参数。

 function MouseHookProc(nCode: Integer; wPar: WPARAM; lPar: LPARAM): lResult; stdcall;

如果我要判断鼠标左键是否按下,用wParam==WM_LBUTTONDOWN判断.
  lParam 是 (tagMOUSEHOOKSTRUCT的指针)PMouseHookStruct类型,主要是获得发送窗口句柄,鼠标坐标 ,以及其他一些信息 。
  lParam 用的时候需要强制转换,转换成PMouseHookStruct类型.

PMouseHookStruct = ^TMouseHookStruct;
tagMOUSEHOOKSTRUCT = packed record
 pt: TPoint;
 hwnd: HWND;
 wHitTestCode: UINT;
 dwExtraInfo: DWORD;
end;
TMouseHookStruct = tagMOUSEHOOKSTRUCT;

例如:

function GetMsgProc(nCode: Integer; wPara: WPARAM; lPara: LPARAM)
 : lResult; stdcall;
var
 hGetMsgHook:HHOOK;
 Msg: TMsg;
begin
 if (nCode >= 0) then
 begin
  FillChar(pMsgData^, Sizeof(TMessageRecord), #0);
  Msg := TMsg(Pointer(lPara)^);
 end;
 Result := CallNextHookEx(hGetMsgHook, nCode, wPara, lPara);
end;

数据结构原型

typedef struct _WNDCLASS{
          UINT style;
           WNDPROC lpfnWndProc;
           int cbClsExtra;
           int cbWndExtra;
           HANDLE hInstance;
           HICON hIcon;
           HCURSOR hCursor;
           HBRUSH hbrBackground;
           LPCTSTR lpszMenuName;
           LPCTSTR lpszClassName;
}WNDCLASS;

结构说明
  WNDCLASS 结构包含了RegisterClass函数注册的类属性
分量简介
style: 指定类风格。这些风格可通过按位或操作组合起来。风格如下:

  •   CS_BYTEALIGNCLIENT: 在字节边界上(在x方向上)定位窗口的用户区域的位置
  •   CS_BYTEALIGNWINDOW: 在字节边界上(在x方向上)定位窗口的位置
  •   CS_CLASSDC: 该窗口类的所有窗口实例都共享一个窗口类DC
  •   CS_DBLCLKS: 允许向窗口发送双击鼠标键的消息
  •   CS_GLOBALCLASS: 当调用CreateWindow 或 CreateWindowEx 函数来创建窗口时允许它的hInstance参数和注册窗口类时传递给RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。
  •   CS_HREDRAW: 当水平长度改变或移动窗口时,重画整个窗口
  •   CS_NOCLOSE: 禁止系统菜单的关闭选项
  •   CS_OWNDC: 给予每个窗口实例它本身的DC。注意,尽管这样是很方便,但它必须慎重使用,因为每个DC大约要占800个字节的内存。
  •   CS_PARENTDC: 将子窗口的裁剪区域设置到父窗口的DC中去,这样子窗口便可以在父窗口上绘制自身。注意,这是子窗口还是从系统缓存中获取DC,而不是使用父窗口的DC。使用该风格可以提高系统性能。
  •   CS_SAVEBITS: 以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息。该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失。设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存。
  •   CS_VREDRAW: 当垂直长度改变或移动窗口时,重画整个窗口
  • lpfnWndProc: 指向窗口过程

cbClsExtra: 指定紧随在 WNDCLASS 数据结构后分配的字节数。系统将其初始化为零。
cbWndExtra: 指定紧随在窗口实例之后分配的字节数,系统将其初始化为零。如果应用程序正在用WNDCLASS结构注册一个在RC资源描述文件中用CLASS指令创建的对话框时,它必须设置这个字段为 DLGWINDOWEXTRA。
hInstance: 标识了该窗口类的窗口过程所在的模块实例的句柄,不能为NULL。
hIcon: 标识了该窗口类的图标。hIcon字段必须是一个图标的句柄;若hIcon字段为NULL,则无论何时用户把应用程序缩至最小时,应用程序必须画一个图标。
hCursor: 标识该窗口类的光标,hCursor必须是一个光标资源的句柄。若hCursor字段为NULL,则无论何时鼠标移到应用程序窗口时,应用程序必须显式设置光标形状。
hbrBackground: 标识了该窗口类的背景画笔。hbrBackground字段必须是用于绘制背景的物理刷子的句柄,或者是一个颜色的值。如果给出一个颜色的值,它必须是下面列出的标准系统颜色之一(系统将对所选颜色加1)。如果给出了颜色值,它必须是转换成下列的HBRUSH类型之一的颜色:

  •   COLOR_ACTIVEBORDER
  •   COLOR_ACTIVECAPTION
  •   COLOR_APPWORKSPACE
  •   COLOR_BACKGROUND
  •   COLOR_BTNFACE
  •   COLOR_BTHSHADOW
  •   COLOR_BTNTEXT
  •   COLOR_CAPTIONTEXT
  •   COLOR_GRAYTEXT
  •   COLOR_HIGHLIGHT
  •   COLOR_HIGHLIGHTTEXT
  •   COLOR_INACTIVEBORDER
  •   COLOR_INACTIVECAPTION
  •   COLOR_MENU
  •   COLOR_MENUTEXT
  •   COLOR_SCROLLBAR
  •   COLOR_WINDOW
  •   COLOR_WINDOWFRAME
  •   COLOR_WINDOWTEXT
  •   当hbrBackground字段为NULL时,每当需要绘制其用户区域时,应用程序必须自己来绘制其背景。应用程序可以通过处理WM_ERASEBKGND 消息或检查由 BeginPaint 函数填写的 PAINTSTRUCT 结构的fErase 字段来确定背景什么时候需要着色。

lpszMenuName:指向NULL结束的字符串,该字符串描述菜单的资源名,如同在资源文件里显示的名字一样。若使用一个整数标识菜单,可以使用MAKEINTRESOURCE宏。如果lpszMenuName为NULL,
  那么该窗口类的窗口将没有默认菜单。
lpszClassName:指向NULL结束的字符串,或者是一个原型(atom)。若该参数是一个原型,它必须是一个有先前调用RegisterClass或者 RegisterClassEx函数产生的类原型。类原型必须作为lpszClassName的低字,高字必须为0.若lpszClassName是一个字符串,它描述了窗口类名。这个类名可以是由RegisterClass或者RegisterClassEx注册的名字,或者是任何预定义的控件类名。
  结构信息Header 在winuser.h声明,包含windows.h

(0)

相关推荐

  • C++ 学习之旅 Windows程序内部运行原理

    学习C++与.net不同的是,一定要搞清楚Windows程序内部运行原理,因为他所涉及大多数是操作系统的调用,而.net毕竟是在.netFrameWork上唱戏. 那Windows应用程序,操作系统,计算机硬件之间的相互关系究竟什么了,下面的图就给予很好的解释. 向下箭头①是 应用程序运行判断处理的结果,输出到输出的设备. 向上箭头②是输入设备,输入到操作系统中. 向下箭头③代表API,我们要解释以下API是什么.API是应用程序接口, 表示应用程序可以通知操作系统执行某个具体的动作,如操作系统

  • Visual C++程序设计中Windows GDI贴图闪烁的解决方法

    本文实例讲述了Visual C++程序设计中Windows GDI贴图闪烁的解决方法.分享给大家供大家参考.具体如下: 一般的windows 复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小的时候出现闪烁. 先来谈谈闪烁产生的原因 原因一: 如果熟悉显卡原理的话,调用GDI函数向屏幕输出的时候并不是立刻就显示在屏幕 上只是写到了显存里,而显卡每隔一段时间把显存的内容输出到屏幕上,这就是刷新周期. 一般显卡的刷新周期是 1/80秒左右,具体数字可以自己设置的. 这样

  • c++利用windows函数实现计时示例

    复制代码 代码如下: //Windows系统下可以用 time(),clock(),timeGetTime(),GetTickCount(),QueryPerformanceCounter()来对一段程序代码进行计时 #include <stdio.h>#include <windows.h>#include <time.h>                   //time_t time()  clock_t clock()    #include <Mmsys

  • 基于Windows C++ 应用程序通用日志组件的使用详解

    引言 在如何记录程序日志方面,通常有三种选择: 1.采用Log4CXX等公共开源日志组件:这类日志组件的特点是跨平台且功能比较强大,例如可以把日志发往另一台服务器或记录到数据库中等:另外,可配置性较高,可以通过配置文件或程序代码对日志进行很多个性化设置.但从另外一个角度看,由于这些优点往往也导致了在使用方面的缺点.首先,对于一般应用程序来说,它们并不需要太多的功能,通常只需要把日志记录到文件或反馈到应用程序,功能太多反正让用户使用起来觉得繁琐还得背负很多从来都用不到的代码.其次,这类日志组件通常

  • 在C++程序中开启和禁用Windows设备的无线网卡的方法

    1.列出当前网卡:SetupDiEnumDeviceInfo 2.找出当前无线网卡的名字(用natvie wifi api) 3.卸载\安装此驱动 问题: log为:SetupDiSetClassInstallParams failed. -536870347   完整代码如下: // ControlWirelessCard.cpp : Defines the entry point for the console application. // #include "stdafx.h"

  • C++程序中使用Windows系统Native Wifi API的基本教程

    Windows应用想要实现连接wifi,监听wifi信号,断开连接等功能,用NativeWifi API是个不错的选择. 打开MSDN,搜索NativeWifi Api,找到Native Wifi页.在这里. 信息量很大,如果像我着急实现上述功能,看海量的文档有些来不及.如果直接给我例子,在运行中调试,阅读代码,效率会更高. 但是,我并没有成功.首先,Sample在SDK中,参见这里.我下载几次都失败了,最后放弃这条路.后来同事给了我一份Sample,我不敢确定是否就是这个,但是代码写的也是很晦

  • 深入解析C++的WNDCLASS结构体及其在Windows中的应用

    WNDCLASS是一个由系统支持的结构,用来储存某一类窗口的信息,如ClassStyle,消息处理函数,Icon,Cursor,背景Brush等.也就是说,CreateWindow只是将某个WNDCLASS定义的窗体变成实例.要得到某一窗口的WNDCLASS数据,可以用GetClassLong(); RegisterClass()就是在系统注册某一类型的窗体.也就是将你提供的WNDCLASS数据注册为一个窗口类,在WNDCLASS.lpszClassName中定义该WNDCLASS的标识,无论C

  • C#语言基础——结构体和枚举类型全面解析

    一.结构体(struct) 结构类型是用户自己定义的一种类型,它是由其他类型组合而成的,可包含构造函数.常数.字段.方法.属性.索引器.运算符.事件和嵌套类型的值类型.结构在几个重要方面不同于类:结构为值类型而不是引用类型,并且结构不支持继承. 用结构的主要思想是用于创建小型的对象,如Point和FileInfo等等.这可以节省内存,因为没有如类对象所需的那样有额外的引用产生.例如,当声明含有成千上万个对象的数组时,这会引起极大的差异. 结构体是一个变量组,将一组变量放在一起,其实就是一个自定义

  • go语言通过结构体生成json示例解析

    目录 通过结构体生成json 通过map生成json json解析到结构体 json解析到map 通过结构体生成json buf, err := json.MarshalIndent(s, "", " ") //格式化编码 package main import ( "encoding/json" "fmt" ) //成员变量名首字母必须大写 type IT struct { Company string `json:&quo

  • C语言中结构体struct编写的一些要点解析

    一.关于结构体的声明 1.匿名声明.如: struct { int i,j; }point; 说明: 这段代码的含义是,声明一个无名(anonymous)的结构体,并创建了一个结构体变量point.如果这段声明是放在全局域(在任意函数(比如main函数)外)内,那么point内的变量将被初始化为默认值,换句话说,以这种方式声明结构体变量时就已经为它分配了内存空间. 适用于该结构体只需要产生一个变量!本例中,该匿名结构体将有且仅有point这个结构体变量! 不同的匿名结构体变量,类型是不同的!如

  • Go 结构体、数组、字典和 json 字符串的相互转换方法

    Go 语言中 encoding/json 包可以很方便的将结构体.数组.字典转换为 json 字符串. 引用 import "encoding/json" 解析语法 // v 传入结构体.数组等实例变量 // []byte 字节数组 // error 可能会有的错误 func Marshal(v interface{}) ([]byte, error) 反解析 // []byte 字节数组 // v 传入结构体.数组等实例变量的指针地址 // error 可能会有的错误 func Un

  • Go语言学习教程之结构体的示例详解

    目录 前言 可导出的标识符 嵌入字段 提升 标签 结构体与JSON相互转换 结构体转JSON JSON转结构体 练习代码步骤 前言 结构体是一个序列,包含一些被命名的元素,这些被命名的元素称为字段(field),每个字段有一个名字和一个类型. 结构体用得比较多的地方是声明与数据库交互时需要用到的Model类型,以及与JSON数据进行相互转换.(当然,项目中任何需要多种数据结构组合在一起使用的地方,都可以选择用结构体) 代码段1:声明一个待办事项的Model类型: type Todo struct

  • 详解Go语言中结构体与JSON间的转换

    目录 前言 结构体转 JSON JSON 解析结构体 小结 前言 在日常开发中,我们往往会将 JSON 解析成对应的结构体,反之也会将结构体转成 JSON.接下来本文会通过 JSON 包的两个函数,来介绍 JSON 与结构体之间的转换. 结构体转 JSON Marshal(v any) ([]byte, error):将 v 转成 JSON 数据,以 []byte 的形式返回. import ( "encoding/json" "fmt" ) type User s

  • 详解C语言中结构体的自引用和相互引用

    结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针. 结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针. 1. 自引用 结构体 1.1 不使用typedef时 错误的方式: struct tag_1{ struct tag_1 A; /* 结构体 */ int value; }; 这种声明是错误的,因为这种声明实际上是一个无限循环,成员b是一个结构体,b的内部还会有成员是结构体,依次下去,无线循环.

  • C++中结构体的类型定义和初始化以及变量引用

    C++结构体类型的定义和初始化 有时需要将不同类型的数据组合成一个有机的整体,以供用户方便地使用.这些组合在一个整体中的数据是互相联系的.例如,一个学生的学号.姓名.性别.年龄.成绩.家庭地址等项,都是这个学生的属性,见图 可以看到学号(num).姓名(name).性别(sex).年龄(age).成绩(score ).地址(addr)是与姓名为"Li Fun"的学生有关的.如果在程序中将num,name,sex,age,score,addr分别定义为互相独立的变量,就难以反映出它们之间

  • 初步剖析C语言编程中的结构体

    C语言结构体,可谓是C强大功能之一,也是C++语言之所以能衍生的有利条件,事实上,当结构体中成员中有函数指针了后,那么,结构体也即C++中的类了. C语言中,结构体的声明.定义是用到关键字struct,就像联合体用到关键字union.枚举类型用到enum关键字一样,事实上,联合体.枚举类型的用法几乎是参照结构体来的.结构体的声明格式如下: struct tag-name{ { member 1; - member N; }; 因此,定义结构体变量的语句为:struct tag-name vari

随机推荐