开源GUI-Microwindows之程序入口分析
***************************************************************************************************************************
作者:EasyWave 时间:2014.10.05
类别:开源GUI系统-Microwindows之程序入口分析 声明:转载,请保留链接
注意:如有错误,欢迎指正。这些是我学习的日志文章......
***************************************************************************************************************************
一:MicroWindows程序入口
这里仅仅分析基于WIN32 Message方式部分,对于Nano不是这里的分析重点,相信熟悉Linux内核的,应该都知道在Linux下,无论是设备,驱动,还是进程,都是採用链表的方式将各个宿主数据结构链接起来,而在Microwindows中也採用内似的方式,我们先来复习下Linux的双向链表吧,在Linux内核中,有大量的数据结构须要用到双循环链表,比如进程、文件、模块、页面等。若採用双循环链表的传统实现方式,须要为这些数据结构维护各自的链表,而且为每一个链表都要设计插入、删除等操作函数。由于用来维持链表的next和prev指针指向相应类型的对象,因此一种数据结构的链表操作函数不能用于操作其他数据结构的链表。
在Linux源码树的include/linux/list.h文件里,採用了一种类型无关的双循环链表实现方式。其思想是将指针prev和next从详细的数据结构中提取出来构成一种通用的"双链表"数据结构list_head。假设须要构造某类对象的特定链表,则在其结构(被称为宿主数据结构)中定义一个类型为list_head类型的成员,通过这个成员将这类对象连接起来,形成所需链表,并通过通用链表函数对其进行操作。其长处是仅仅需编写通用链表函数,就可以构造和操作不同对象的链表,而无需为每类对象的每种列表编写专用函数,实现了代码的重用。例如以下所看到的的定义:
struct list_head {
struct list_head *next, *prev;
};
在Linux内核中的双循环链表实现方式例如以下:
- list_head类型的变量作为一个成员嵌入到宿主数据结构内;
- 能够将链表结构放在宿主结构内的不论什么地方;
- 能够为链表结构取不论什么名字;
- 宿主结构能够有多个链表结构;
- 用list_head中的成员和相相应的处理函数来对链表进行遍历;
- 假设想得到宿主结构的指针,使用list_entry能够算出来。
typedef struct dtest {
…
struct list_head list;
…
};
可是,怎样通过list_head成员訪问到宿主结构项呢?毕竟list_head只是是个连接件,而我们须要的是一个"特定"的数据结构链表。
先介绍几个基本宏:offsetof、typeof、containerof
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
而__builtin_offsetof()宏就是在编译器中已经设计好了的函数,直接调用就可以。
#undef offsetof //取消先前的不论什么定义,能够保证以下的定义生效
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
上面的算法一共分以下几步:
- ( (TYPE *)0 ) 0地址强制 "转换" 为 TYPE结构的指针;
- ((TYPE *)0)->MEMBER 訪问结构中的数据成员;
- &( ( (TYPE *)0 )->MEMBER)取出数据成员的地址
- (size_t)(&(((TYPE*)0)->MEMBER))结果转换类型.
巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址; 这里使用的是一个利用编译器技术的小技巧(编译器自己主动算出成员的偏移量),即先求得结构成员变量在结构体中的相对于结构体的首地址的偏移地址,然后依据结构体的首地址为0,从而得出该偏移地址就是该结构体变量在该结构体中的偏移,即:该结构体成员变量距离结构体首的距离。在offsetof()中,这个member成员的地址实际上就是type数据结构中member成员相对于结构变量的偏移量。对于给定一个结构,offsetof(type,member)是一个常量,list_entry()正是利用这个不变的偏移量来求得链表数据项的变量地址。
ontainer_of - cast a member of a structure out to the containing structure。
ptr: the pointer to the member.
type: the type of the container struct this is embedded in.
member: the name of the member within the struct.
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
list_entry()宏,获取当前list_head链表节点所在的宿主结构项。第一个參数为当前list_head节点的指针,即指向宿主结构项的list_head成员。第二个參数是宿主数据结构的定义类型。第三个參数为宿主结构类型定义中list_head成员名。
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
扩展替换即为:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
比如,我们要訪问dtest链表(链表头为head)中首个元素,则如此调用:
list_entry(head->next, struct dtest, list);
经过C预处理的文字替换,这一行的内容就成为:
((struct dtest *)((char *)(head->next) - (unsigned long)(&((struct dtest *)0)->list)))
考虑list_head类型成员member相对于宿主结构(类型为type)起始地址的偏移量。对于全部该类型的宿主对象,这个偏移量是固定的。而且能够在如果宿主对象地址值为0,通过返回member成员的地址获得,即等于(unsigned long)(&((type *)0)->member)。这样,将当前宿主对象的"连接件"地址(ptr)减去这个偏移量,得到宿主对象地址,再将它转换为宿主数据结构类型的指针。 须要重申的是,链表头必须被嵌入到宿主对象中。
wp->pClass = (PWNDCLASS)mwClassHead.head;,而mwClassHead的定义例如以下所看到的:
*lpWndClass)函数,例如以下所看到的:
lpClassName)函数中会去调用GdItemAddr,是否有似曾相识的感觉呢,只是,我们能够通过解析例如以下:
开源GUI-Microwindows之程序入口分析的更多相关文章
- cocos2d-x打飞机实例总结(一):程序入口分析和AppDelegate,Application,ApplicationProtocol三个类的分析
首先,是个敲代码的,基本上都知道程序的入口是main函数,显然,就算在cocos2d-x框架中也一样 我们看看main函数做了什么 #include "main.h" #inclu ...
- MongoDB源码分析——mongod程序源码入口分析
Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...
- 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 百篇博客分析OpenHarmony源码 | v51.04
百篇博客系列篇.本篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | ...
- Linux安装程序Anaconda分析(续)
本来想写篇关于Anaconda的文章,但看到这里写的这么详细,转,原文在这里:Linux安装程序Anaconda分析(续) (1) disptach.py: 下面我们看一下Dispatcher类的主要 ...
- VS2010/MFC编程入门之四(MFC应用程序框架分析)
VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html 上一讲鸡啄米讲的是VS2010应用 ...
- Quartz(GUI)图形界面程序----Quartz Web
下载.设置和运行Quartz(GUI)图形界面程序----Quartz Web 一.获取Quartz Web程序(Quartz GUI).早期的 Quartz 框架开发者意识到一个 GUI 对于某类用 ...
- 小程序入口构造工具&二维码测试工具
小程序入口构造工具&二维码测试工具 本文将介绍我们小程序中隐藏的两个工具页面.原理虽不复杂,收益却实实在在,或许也能给诸君带来启发. 入口构造工具 痛点 PM&运营 投放链接 PM&a ...
- Ethzasl MSF源码阅读(1):程序入口和主题订阅
关于IMU融合知乎上的一篇问答:有哪些开源项目是关于单目+imu做slam的? Ethz的Stephen Weiss的工作,是一个IMU松耦合的方法. 1.程序入口:ethzasl_msf\msf_u ...
- 程序入口函数和glibc及C++全局构造和析构
分类: CRT Machnasim 2011-06-15 17:45 144人阅读 评论(0) 收藏 举报 c++汇编linuxlist语言编译器 1,程序入口函数和初始化 操作系统在装载可执行文件后 ...
随机推荐
- 促销R语言应用性能
1. 绩效评估 时间的确定 R测量时间是在最简单的方式提供是system.time性能. system.time(expr, gcFirst=TRUE) 这个函数会在不减少程序执行性能的情 ...
- Microsoft Build 2016 Day 2
Microsoft Build 2016 Day 2 Microsoft Build 2016 Day 1 记录 Microsoft Build 2016 进行到了第二天,我觉得这一天的内容非常精彩, ...
- 获取编译学习笔记 (六)—— si、di,双环
疯狂暑期学习 汇编入门学习笔记 (六)-- si.di,双重循环 參考: <汇编语言> 王爽 第7章 1. and和or指令,与[bx+idata] and和or.就不多说了. [bx+ ...
- Windows Phone开发(30):图形
原文:Windows Phone开发(30):图形 图形如矩形.椭圆.路径等都从Shape类派生,它们一般表示规则或不规则图形,这些图形都是简单的二维图形,我相信大家都能理解的. 例一:矩形. 请看下 ...
- live555 for Android
因为Live555 包中未提供Android 的config 所以编译器来比較麻烦,须要自己编写Android.mk ,下面是我通过 改动 现有的config文件,在cygwin实现 编译的过程,co ...
- cocos2d-x3.0 相对布局(一)
2dx相对布局和Android非常类似.假设前完成Android它应该是easy入门. Size widgetSize = Director::getInstance()->getWinSize ...
- WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计
在我工作的项目中含有多种操作系统.多种设备.多种开发语言,因此需要使用跨平台的通信技术和自定义的消息编码.经过技术调研,ZeroMQ+ProtocolBuffer最终成为通信技术和编码方式.但是如 ...
- 【Java基础】异常的简单分类与处理
Java中所有的异常都继承自Throwable类,Throwable类的已知子类有Error和Exception. Error是指系统出现的错误,这种错误出现的时候,我们的程序无能为力,所以不需要进行 ...
- [C++]四种方式求解最大子序列求和问题
问题 给定整数: A1,A2,-,An,求∑jk=iAk 的最大值(为方便起见,假设全部的整数均为负数,则最大子序列和为0) 比如 对于输入:-2,11,-4,13,-5,-2,答案为20,即从A2到 ...
- WPF中两条路径渐变的探讨
原文:WPF中两条路径渐变的探讨 我们在WPF中,偶尔也会涉及到两条路径作一些“路径渐变 ”.先看看比较简单的情形:如下图(关键点用红色圆点加以标识):(图1) 上面图1中的第1幅图可以说是最简单的路 ...