首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Windows窗口对象的附加数据

Windows窗口对象的附加数据

作者头像
欧阳大哥2013
发布2018-10-25 14:59:10
1.3K0
发布2018-10-25 14:59:10
举报
窗口对象附加数据的种类

Windows编程中,每一个窗口对象(HWND)都是一个窗口类(WNDCLASSEX)的实例。每个窗口类实例出来的窗口对象都按同样的方式进行处理,共享相同的回调函数,我们也可以使用窗口子类化技术对某个窗口对象做特定的处理。有时候在一些MDI程序中希望每个窗口对象能保留一些不同于其他窗口的特定数据,这样就可以实现窗口对象有相同的行为但有不同的特性。要使窗口对象有不同的附加特性,只要将那些附加特性与窗口句柄关联起来就可以了,也就是将每个窗口不同的附加数据与窗口句柄建立一种映射关系,这样通过这种映射关系就可以从句柄中获取相关联的附加的数据,然后对其进行操作。Windows系统中提供了如下的四种方法:

1. 通过一个数据结构将窗口句柄与附加数据关联起来 2. 通过在窗口类中为每个窗口指定一定的附加空间 3. 使用窗口的用户数据特性 4. 使用窗口属性技术

1. 通过一个数据结构将窗口句柄与附加数据关联起来

这种技术的实现可以通过结构体和C++类来实现,这也是一种很普遍的方法,在一个结构体或C++类中窗口句柄作为一个数据成员,其他附加数据也作为结构体或C++类的数据成员,然后再通过相应的成员函数来操作这种关联。

☞优点:这种方法可以使一个窗口对象能够很简单的带上很多不同类型的数据,而且操作起来最方便,不需要进行其他附加的存取附加数据的操作。

☞缺点:因为类的定义是静态完成的,而且数据的类型也是静态定义好的,因此导致在运行时使用这种方法的窗口所关联的数据虽然值可以不同但数据的类型和大小都是固定好了的,不能改变的,而且当不同的窗口需要不同的类型和大小的附加数据时需要为这种窗口定义不同的结构体或者类。

☞适用: 适用于特定的一类窗口(可以是同一个窗口类所建立的所有窗口,也可以是同一窗口类所建立的部分窗口,也可以是不同窗口类所建立的窗口),这一类窗口具有相同的附加数据类型和相同的大小。不同类的窗口定义不同的类。一个典型的实现就是MFC中CWnd类的定义。

2.通过在窗口类中为每个窗口指定一定的附加空间

在窗口类进行注册时要填充一个WNDCLASSEX结构,这个结构的数据成员cbWndExtra用来指定为每个窗口实例对象分配的附加内存的大小,这样每个从这个窗口类中建立窗口对象都会为这个窗口对象分配cbWndExtra大小的附加内存数据,因此用户可以使用这部分附加内存来存放相对应的窗口的自定义扩展附加数据。当cbWndExtra被指定为0时则不会为每个窗口分配附加的内存空间。一旦这种类型的窗口对象创建后就可以通过SetWindowLongPtr/GetWindowLongPtr两个函数来存放数据到附加空间中或者从附加空间中获取数据了。这两个函数的定义是:

LONG_PTR SetWindowLongPtr(
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);

LONG_PTR GetWindowLongPtr(
HWND hWnd,
int nIndex
);

在SetWindowLongPtr/GetWindowLongPtr中是通过索引来访问附加数据的,每一次访问只能获取和设置sizeof(LONG_PTR)大小的数据。窗口实例对象被创建后,系统为每个窗口对象建立一个cbWndExtra个字节的数据,上述函数中的索引值就是用来访问和设置cbWndExtra中的数据。例如要想获取第0个索引的数据可以通过调用:

LONG_PTR data = GetWindowLongPtr(hWnd, 0);

☞优点:窗口不需要建立一个额外的结构来扩充附加数据。会在窗口对象建立时自动为窗口分配空间,附加空间中的内容和数据类型可以在运行时动态改变

☞缺点:也是分配空间的大小在窗口创建前已经固定好了,不能改变了。存取起来相对麻烦复杂,即要使用附加的操作来获取和设置附加数据的内容而且对一次存取的数量也有限制,而且还需要进行数据类型的转换。

☞适用: 所分配的空间对一个窗口类的所有窗口实例都是大小相等。但内容和类型可以自定,而且最好是每种数据类型都是以操作系统字长长度对齐的。访问通过索引来完成。一般用于用户自定义的窗口类所建立的窗口

☞例如:假如一个窗口类的cbWndExtra指定大小为128,而为其中建立的一个窗口在附加空间中存放一个长度为37的字符串。则其存放和获取的过程如下:

//设置
void foo1()
{
  char szText[37] = "xxxxxxxxxxxxxxxxx";     //全长37   确保长度要小于  128

  int len = (37+7)/8;
  LONG_PTR p = (LONG_PTR)szText;
  for(int i = 0; i<len; i++)
      SetWindowLongPtr(hWnd, i*sizeof(LONG_PTR) p[i]);   //因为一次设置了8个字节,所以下一个索引应该都是乘8
}

//获取
void foo2()
{
  char szText[37];

  int len = (37+7)/8;

 LONG_PTR l;
 for(int i = 0; i<len-1; i++)
 {
     l = GetWindowLongPtr(hWnd, i*sizeof(LONG_PTR));
    memcpy(szText, &l, sizeof(LONG_PTR));  //不能全部拷贝,
    szText+=sizeof(LONG_PTR);
 }

  l = GetWindowLongPtr(hWnd, i*sizeof(LONG_PTR));
  memcpy(szText, &l, 37-len*sizeof(LONG_PTR));   //注意这一步
}
3. 使用窗口的用户数据特性

系统为每个窗口都保留了一个32/64位(根据操作系统字长)的空间,这个空间称为用户数据空间。用户数据空间的初始化赋值由CreateWindow函数的最后一个参数指定,当然也可以任何时候设置用户数据空间中的值,比如可以通过调用函数GetWindowLongPtr/SetWindowLongPtr并将索引值设置为GWL_USERDATA来进行设置和获取。因为这个值和系统字长有关,所以一般用来存放句柄或者是指针,而且一般不要去改变系统注册的窗口类所建立的窗口的这个值,因为这个值可以被用在某种窗口类内部使用。这个空间跟窗口的附加空间不同的是,每个窗口都具有,而附加空间的大小则要在注册前指定,另外附加空间可以为任意大小,而这个则只能为32或64位。下面是使用的例子:

  LONG_PTR l = GetWindowLongPtr(hWnd, GWL_USERDATA);
4. 使用窗口属性(Prop)技术

上面的几种方法都有一个限制就是在运行时不能动态改变附加数据的大小,而采用窗口属性技术可以实现。对不同的任意一个窗口使用不同的大小,不同的类型,而不必要针对某一类窗口,因此这种技术是为特定窗口服务的。而且窗口属性不存在预先分配空间的要求,他是运行时可以由程序动态调整的。一个窗口可以具有很多属性,每个属性通常表现为一种句柄,但是也可以是任何一种32/64位的数据类型(32/64位整数,指针,句柄等)。每种属性都由字符串来标识。这跟窗口类的指定的附加空间有相似也有区别,相同是每次访问的值都是一个操作系统字长值,而这个值可以是句柄也可以是整数,也可以是指针。不同之处是窗口类的附加空间是预先分配好了的固定空间,一旦注册后为每个窗口分配的空间就固定了,而窗口属性则没有固定,他的数量可以是任意的,动态的。窗口附加空间由索引来获取,而窗口属性则由字符串来获取,这两种方式的区别就好象用数组和字典一样。

☞优点: 可以不必预先知道窗口需要的附加空间的大小,可以任意改变大小,可以有不同的数据类型,而且每个窗口的这些附加空间的大小可以不相同,一个窗口一个附加空间,而不是一类窗口一个附加空间。

☞缺点: 存取数据需要使用额外的存取函数。窗口属性只是用来存放数据的标识如句柄,而不是数据本身。而当窗口被销毁时窗口所关联的属性所指向的内容不会销毁,需要进行手动的销毁,而且窗口在销毁前必须要手动消除跟窗口关联的属性。

☞适用: 某个子类化了的窗口,或者是MDI中的子窗口。

下面列出了操作窗口属性的函数

  1. 为窗口增加一个新的属性或者修改属性
BOOL SetProp(
  HWND hWnd,         // handle to window
  LPCTSTR lpString,  // 属性的标识符,可以是字符串,也可以是一个全局原子标识符
  HANDLE hData       // 属性的句柄数据
);

lpString :用户可以多次调用来增加不同的属性,若是lpString跟窗口已关联的某属性的标识符相等时则是用hData来代替原来的属性数据。要注意当是替换属性数据时,要保证原先的属性的内容被正确的释放,这可通过调用GetProp函数来获取。

hData: 虽是句柄参数,但可以是任何一种32/64位的值,可以是指针,句柄,32/64位整数。

函数返回TRUE成功

  1. 获取一个属性
HANDLE GetProp(
  HWND hWnd,         // handle to window
  LPCTSTR lpString   //[IN] atom or string
);

函数返回lpString标识的道具句柄。若没有则返回NULL

  1. 删除于某种属性的关联
HANDLE RemoveProp(
  HWND hWnd,         // handle to window
  LPCTSTR lpString   // atom or string
);

注意这个函数只是删除某种属性的关联,并不删除属性所指的数据,函数返回被删除关联的属性句柄,因此用户可以通过这个句柄来销毁属性所指的数据,当窗口被销毁时并不会删除用户添加的属性,因此需要在窗口销毁前调用这个函数来销毁关联的属性,然后再销毁属性所指的数据。 不能删除系统定义的关联属性和其他程序已经关联好了的属性。

  1. 列举窗口所关联的属性
int EnumProps(  
  HWND hWnd,               // handle to window
  PROPENUMPROC lpEnumFunc  // 回调函数指针
);   //返回-1表示窗口没有关联任何道具,否则返回回调函数的最后一个返回值

回调函数的格式如下:

BOOL CALLBACK PropEnumProc(
  HWND hwnd,           // handle to window 
  LPCTSTR lpszString,  // string component
  HANDLE hData         // data handle component
);

回调函数中不能进行GetProp和SetProp的操作只能调用RemoveProp进行删除。这个函数会列举所有的关联的属性,函数若是返回FALSE,则不继续列举,若是返回TRUE则继续列举。

  1. 列举窗口所关联的属性2
int EnumPropsEx( 
  HWND hWnd,                  // handle to window
  PROPENUMPROCEX lpEnumFunc,  // callback function
  LPARAM lParam               // 定义传递给回调函数的自定义数据
);

相对应的回调函数为:

BOOL CALLBACK PropEnumProcEx(
  HWND hwnd,          // handle to window
  LPTSTR lpszString,  // string component
  HANDLE hData,       // data handle component
  ULONG_PTR dwData    // application-defined data
);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.10.17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 窗口对象附加数据的种类
    • 1. 通过一个数据结构将窗口句柄与附加数据关联起来
      • 2.通过在窗口类中为每个窗口指定一定的附加空间
        • 3. 使用窗口的用户数据特性
          • 4. 使用窗口属性(Prop)技术
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档