关于作者

用户名:jfw010
笔名:阿峰
地区: 北京
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



老朱

访问统计:
文章个数:18
评论个数:2
留言条数:0




Powered by BlogDriver 2.1

jfw010的博客

 

欢迎访问jfw010的博客

文章

中文RFC3315(2-5)

2. 需求

    文档中出现的关键字MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD,SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL,在文献[1]中解释。

    本文档还使用了内部概念性变量描述协议行为,和在执行时允许系统管理员改变的外部变量。通过变量名值是如何改变,和设置他们的值对协议行为的影响,来演示整个协议的行为。一个执行并不需要对他们作精确描述,只要他的外部变量与本文档描述的保持一致。

3. 背景

    IPv6规范提供了IPv6的基础架构和设计,能作为研究工具的IPv6相关著作包括:the IPv6 Specification [3], the IPv6 Addressing Architecture [5], IPv6 Stateless Address Autoconfiguration [17], IPv6 Neighbor Discovery Processing [13], and Dynamic Updates to DNS [22].这些规范使得DHCP建立在IPv6上,提供健壮的由状态自动配置和DNS主机名自动注册。

    The IPv6 Addressing Architecture specification [5]为网络设计者定义了IPv6执行时可用的地址范围,和各种配置结构知道方针。IPv6 的两个优点在于:支持多播和节点在初始化时可以创建link-local地址。这些特点的实用性意味着,客户端可以利用它的link-local地址和多播地址,发现她所在链路上的DHCP服务器或者中继代理,并与他们通信。

    IPv6[13]邻居发现是一个节点发现协议,他替代了ARP的功能并有所提高。要理解IPv6的和无状态地址自动配置,强烈推荐理解IPv6的邻居发现协议。

    DNS动态更新协议[22]支持IPv4IPv6DNS纪录的动态更新,DHCP利用DNS的动态更新,把地址和名字空间结合起来,支持IPv6的自动配置和自动注册。

4. 术语

    在这部分我们将对本文档中IPv6DHCP中使用术语进行定义。

4.1 IPv6术语

    本文档中相关的IPv6术语来自IPv6 Protocol [3], IPv6 Addressing Architecture [5], and IPv6 Stateless Address Autoconfiguration [17],如下。

    地址    一个接口或一组接口的IP层标识符

    主机    任何不是路由器的节点

    IP      IPv6IPv4IPv6只在有有冲突时使用

    接口    节点与链路的连接

链路    一个通信设备或者媒体,通过它节点可以与IP层下面的链路层通信。典型的例子是以太网(简单的活着网桥的);令牌环;PPP连接,X.25,帧中继,或者ATM网络;以及网络层(或者更高层)的隧道,例如IPv4IPv6自身的隧道。

链路层标识符    接口的链路层标识。例如以太网IEEE802地址,令牌环网络接口,和ISDN链路的E.164地址。

链路-局部地址   一个IPv6地址都有一个单一链路范围,以FE80::/10作为前缀,它可以同一链路上的临近节点。每一个接口都有一个链路-局部地址。

组播地址        一组接口的标识(属于不同的节点)。一个发给组播地址的包就是通过地址发向所有的接口标识符。

邻居        连接在同一链路上的节点。

节点        实现IPv6的设备

分组    加上有效载荷的IPv6报头

前缀    地址的前几位,或者是一批IP地址相同的前几位

前缀长度    前缀的位数

路由器  一个转发没有清晰指向自身IPv6分组的节点

单播地址   

4.2 DHCP术语

    下面是DHCP术语的详细说明。

合乎链接        一个地址合乎链接就是说该地址与DHCP服务器的网络拓扑、前缀分配和地址分配方法一致。

绑定        绑定(或者客户端绑定)就是一组明确的分配给客户端的一组服务器数据记录,它包含服务器关于IA中地址的信息,或者配置信息。通过一定策略返回给客户端的配置信息不要求绑定,例如,在同一链路上返回给所有客户端的信息。一个包含IA的信息绑定格式为 IA-type, IAID>,(IA-typeIA中地址的类型;例如临时地址)。客户端的配置信息绑定是由DUID编入素引的。

配置参数    服务器上配置信息集中的一个元素,并且用DHCP传递给客户。例如,节点用这写信息设定自己的网络子系统,使得在链路或者网络上通信。

5. DHCP常量

    这一部分描述了DHCP使用的各种程序和网络常数。

5.1 组播地址

    DHCP使用的组播地址如下:

    All_DHCP_Relay_Agents_and_Servers (FF02::1:2)

    All_DHCP_Servers (FF05::1:3)

All_DHCP_Relay_Agents_and_Servers (FF02::1:2)是一个link-scoped多播地址,客户机用他来与相邻的(也就是在同一链路)中继代理和服务器通信,在同一链路上的中继代理和服务器都属于多播组的成员。

All_DHCP_Servers (FF05::1:3)是一个site-scoped多播地址,中继代理用他来与服务器通信,可能是因为中继代理想把报文发送给所有服务器,或者是他不知道服务器的单播地址,注意,为了让中继代理使用这个地址,服务器必须有足够范围的地址让中继代理获得。同一站点内的所有服务器都属于多播组的成员。

5.2 UDP端口

    DHCP协议是UDP层上的应用,客户端使用端口546,服务器和中继代理使用端口547

5.3 DHCP 消息类型

    DHCP定义了如下消息类型,括号内是消息类型的代码。这些消息的细节在67部分。

    SOLICIT (1)     客户端发送Solicit消息给要查找的服务器

    ADVERTISE (2)   服务器收到客户端的Solicit消息后,发送Advertise message表示DHCP 服务可用,

    REQUEST (3)     客户端发送Request消息向特殊的服务器请求包括IP地址在内的配置参数。

    CONFIRM (4)     客户端发送Confirm消息给所有可达的服务器,来确定它所分配的地址在他连接的链路是否可用。

    RENEW (5)       客户端向为他提供地址和配置信息的服务器发送Renew消息,来延长地址的生命期和更新其他的配置参数。

    REBIND (6)      客户端在没有收到Renew消息的回应后,向服务器发送Rebind消息延伸租期,和更新其他的配置参数。

    REPLY (7)       服务器发送包含地址和配置参数的Reply消息,以回应收到客户端的SolicitRequestRenewRebind消息。服务器发送包含配置参数的Reply消息以回应Information-request消息。发送Relay消息回应Confirm消息,确认或者否认为客户端分配的地址。或者承认收到了Release或者Decline消息。

    RELEASE (8)     客户端向服务器发送Release消息表明他不再使用为他分配的地址。

    DECLINE (9)     客户端向服务器发送Decline消息,表明服务器分配的地址已经被链路上的其他节点使用。

    RECONFIGURE (10)    服务器发送Reconfigure消息告知客户端他有新的或者最新的配置参数,并让客户端发起Renew/Reply或者Information-request/Reply处理,来接收服务器最新的信息。

    INFORMATION-REQUEST (11)    客户端发送Information-request消息向服务器请求除了IP地址外的配置参数。

    RELAY-FORW (12)     中继代理发送Relay-forward消息给服务器,不论是直接转发还是通过其他中继代理。接收到的客户端信息或者是从其他中继代理的Relay-forward消息都被封装到Relay-forwardoption中。

    RELAY-REPL (13)     服务器发送Relay-reply消息给中继代理,消息中包含中继发送给客户端的消息。Relay-reply消息可能要经过其他中继代理转发给目的中继。

5.4 状态编码

    DHCPv6采用状态编码,来传达从客户端和服务器的消息中,所要求的操作的成功或失败信息。并且提供导致消息失败的特别的额外信息,专门的状态编码定义在24.4

5.5 传播和转播参量

这一部分展示了描述客户端和服务器的消息传输行为的数值表。

        参数          默认          描述

   -------------------------------------

   SOL_MAX_DELAY     1 sec   Max delay of first Solicit

   SOL_TIMEOUT       1 sec   Initial Solicit timeout

   SOL_MAX_RT      120 secs  Max Solicit timeout value

   REQ_TIMEOUT       1 sec   Initial Request timeout

   REQ_MAX_RT       30 secs  Max Request timeout value

   REQ_MAX_RC       10       Max Request retry attempts

   CNF_MAX_DELAY     1 sec   Max delay of first Confirm

   CNF_TIMEOUT       1 sec   Initial Confirm timeout

   CNF_MAX_RT        4 secs  Max Confirm timeout

   CNF_MAX_RD       10 secs  Max Confirm duration

   REN_TIMEOUT      10 secs  Initial Renew timeout

   REN_MAX_RT      600 secs  Max Renew timeout value

   REB_TIMEOUT      10 secs  Initial Rebind timeout

   REB_MAX_RT      600 secs  Max Rebind timeout value

   INF_MAX_DELAY     1 sec   Max delay of first Information-request

   INF_TIMEOUT       1 sec   Initial Information-request timeout

   INF_MAX_RT      120 secs  Max Information-request timeout value

   REL_TIMEOUT       1 sec   Initial Release timeout

   REL_MAX_RC        5       MAX Release attempts

   DEC_TIMEOUT       1 sec   Initial Decline timeout

   DEC_MAX_RC        5       Max Decline attempts

   REC_TIMEOUT       2 secs  Initial Reconfigure timeout

   REC_MAX_RC        8       Max Reconfigure attempts

   HOP_COUNT_LIMIT  32       Max hop count in a Relay-forward message

5.6 时间值的表述和“无限”的含义

所有时间都可以作为生命期,T1T2是无符号整数,当0xffffffff用作生命期或者T1T2时,他的意思是“无限”。

- 作者: jfw010 2006年06月15日, 星期四 11:14  回复(0) |  引用(0) 加入博采

中文RFC3315(目录+1)

备忘录状态

本文档规定了Internet协会的一种标准跟踪协议,尚需讨论、建议以待提高。本协议的标准化声明及状态请参阅“Internet官方协议标准(STD 1)”的当前版本。本文档可以无限制的分发。

版权告示

    版权(C)互联网社区(2003)。版权所有。

摘要

动态主机配置协议(DHCPv6)使DHCP服务器能够向IPv6节点传递IPv6网络地址等配置参数,它具有自动分配网络地址的能力,和额外配置的灵活性。本协议是“IPv6无状态地址配置”(RFC 2462)的相对协议,他可以单独使用,也可以与RFC 2462一起来获得配置信息。

目录

1. 简介。 . . . . . . . . . . . . . . . . . 5

1.1. 协议和寻址。 . . . . . . . . . . . . . 6

1.2. 客户服务器交换介入二则消息。 . . 6

1.3. 客户服务器交换介入四则消息。 . . 7

2. 要求。 . . . . . . . . . . . . . . . . . . . . . . . 7

3. 背景。 . . . . . . . . . . . . . . . . . . . . . . . . 8

4. 术语。 . . . . . . . . . . . . . . . . . . . . . . . 8

4.1. IPv6 术语。 . . . . . . . . . . . . . . . . . 9

4.2. DHCP 术语。 . . . . . . . . . . . . . . . . . . 10

5. DHCP 常量。 . . . . . . . . . . . . . . . . . . . . . . 12

5.1. 多播地址。 . . . . . . . . . . . . . . . . . 13

5.2. UDP 端口。 . . . . . . . . . . . . . . . . . . . . . . 13

5.3. DHCP 消息类型。 . . . . . . . . . . . . . . . . . 13

5.4. 状态编码。 . . . . . . . . . . . . . . . . . . . . 15

5.5. 传输和转播参量。 . . . . . 16

5.6 时间值和"无限的" 表示法。 . . . . . . . . . . . . . . . . . . . . . . . . 16

6. 客户机/服务器消息格式。 . . . . . . . . . . . . . . . 16

7. 中转Agent/Server 消息格式。 . . . . . . . . . . . . . 17

7.1. 传递向前消息。 . . . . . . . . . . . . . . . . 18

7.2. 传递回复消息。 . . . . . . . . . . . . . . . . . 19

8. 表示法和对域名的用途。 . . . . . . . . . . . 19

9. DHCP 唯一标识符(DUID) . . . . . . . . . . . . . . . 19

9.1. DUID 内容。 . . . . . . . . . . . . . . . . . . . . 20

9.2. DUID 基于链接层地址加上时间[ DUID-LLT ] 20

9.3. DUID 由厂商分配基于企业数字[ DUID-EN ]. . . . . . . . . . . . 22

9.4. DUID 根据链接层地址[ DUID-LL ] . . . . . 22

10. 一致性关联。 . . . . . . . . . . . . . . . . . . . 23

11. 选择地址分配给IA . . . . . . . . 24

12. 管理临时地址。 . . . . . . . . . . . . . 25

13. 客户端消息传输。 . . . . . . . . . . . . 25

14. 客户端初始信息交换的可靠性。 . . . . . 26

15. 消息确认. . . . . . . . . . . . . . . . . . . . . . 27

15.1. Transaction IDs的用途。 . . . . . . . . . . . . . . . 28

15.2. 请求(solicit)消息。 . . . . . . . . . . . . . . . . . . . 28

15.3. 广告消息。 . . . . . . . . . . . . . . . . . . 28

15.4. 请求(request)消息。 . . . . . . . . . . . . . . . . . . . 29

15.5. 确认消息。 . . . . . . . . . . . . . . . . . . . 29

15.6. 更新消息。 . . . . . . . . . . . . . . . . . . . . 29

15.7. Rebind 消息。 . . . . . . . . . . . . . . . . . . . 29

15.8. 衰落消息。 . . . . . . . . . . . . . . . . . . 30

15.9. 释放消息。 . . . . . . . . . . . . . . . . . . . 30

15.10. 回复消息。 . . . . . . . . . . . . . . . . . . . . 30

15.11. 重新构造消息。 . . . . . . . . . . . . . . . . . 31

15.12. 信息请求消息。 . . . . . . . . . . . . . 31

15.13. 传递向前消息。 . . . . . . . . . . . . . . . . 31

15.14. 传递回复消息。 . . . . . . . . . . . . . . . . . 31

16. 客户源地址和接口选择。 . . . . . . . 32

17. DHCP 服务器请求。 . . . . . . . . . . . . . . . . . . 32

17.1. 客户行为。 . . . . . . . . . . . . . . . . . . . 32

17.1.1. 创作请求消息。 . . . . . . . . 32

17.1.2. 传输请求消息。 . . . . . . 33

17.1.3. 接收广告消息。 . . . . . . . . 35

17.1.4. 接受回复消息。 . . . . . . . . . . 35

17.2. 服务器行为。 . . . . . . . . . . . . . . . . . . . 36

17.2.1. 接收请求消息。 . . . . . . . . 36

17.2.2. 广告消息创建和传输

17.2.3. 回复消息创建和传输。 . 38

18. DHCP 客户初始配置交换。 . . . . . . . . 38

18.1. 客户行为。 . . . . . . . . . . . . . . . . . . . 39

18.1.1. 请求消息创作和传输。 39

18.1.2. 创作和传输证实消息。 40

18.1.3. 创作和传输更新消息。 . 41

18.1.4. Rebind 消息创作和传输。 43

18.1.5. 信息创作和传输

请求消息。 . .. . . . . . . . . . . . 44

18.1.6. 发行消息创作和传输。 44

18.1.7. 衰落消息创作和传输。 46

18.1.8. 回复消息收据。 . . . . . . . . . . 46

18.2. 服务器行为。 . . . . . . . . . . . . . . . . . . . 48

18.2.1. 请求消息收据。 . . . . . . . . . 49

18.2.2. 收据证实消息。 . . . . . . . . . 50

18.2.3. 收据更新消息。 . . . . . . . . . . 51

18.2.4. Rebind 消息收据。 . . . . . . . . . 51

18.2.5. 收据信息请求消息。 . . . 52

18.2.6. 发行消息收据。 . . . . . . . . . 53

18.2.7. 衰落消息收据。 . . . . . . . . . 53

18.2.8. 回复消息传输。 . . . . . . . 54

19. DHCP 服务器被创始配置交换。 . . . . . . . . 54

19.1. 服务器行为。 . . . . . . . . . . . . . . . . . . . 55

19.1.1. 创作和传输重新构造

消息。 . . . . . . . . . . . . . . . . . . 55

19.1.2. 时间外面和转播重新构造

消息。 . . . . . . . . . . . . . . . . . . 56

19.2. 收据更新消息。 . . . . . . . . . . . . . . 56

19.3. 收据信息请求消息。 . . . . . . . 56

19.4. 客户行为。 . . . . . . . . . . . . . . . . . . . 57

19.4.1. 收据重新构造消息。 . . . . . . . 57

19.4.2. 创作和传输更新消息。 . 58

19.4.3. 信息创作和传输

请求消息。 . . . . . . . . . . . . . . 58

19.4.4. 时间?? 并且转播?? 更新或

信息请求消息。 . . . . . . . . 58

19.4.5. 回复消息收据。 . . . . . . . . . . 58

20. 中转代理行为??????. . . . . . . . . . . . . . . . . . . . . 58

20.1. 传递客户消息或一则传递向前消息。 59

20.1.1. 传递一则消息从客户。 . . . . . . 59

20.1.2. 传递一则消息从中转代理。 . . . . 59

20.2. 传递传递回复消息。 . . . . . . . . . . . 60

20.3. 建筑传递回复消息。 . . . . . . . . 60

21. DHCP 的认证消息。 . . . . . . . . . . . . . . 61

21.1. 信安全被送在服务器和中转之间

代理。 . . . . . . . . . . . . . . . . . . . . . . 61

21.2. DHCP 认证总结。 . . . . . . . . . . . 63

21.3. 重赛侦查。 . . . . . . . . . . . . . . . . . . 63

21.4. 被延迟的认证协议。 . . . . . . . . . . . 63

21.4.1. 对认证选择的用途在被延迟

认证协议。 . . . . . . . . . . . 64

21.4.2. 消息检验。 . . . . . . . . . . . . . 65

21.4.3. 关键运用。 . . . . . . . . . . . . . . 65

21.4.4. 客户考虑为被延迟的认证

协议。 . . . . . . . . . . . . . . . . . . 66

21.4.5. 服务器考虑为被延迟的认证

协议。 . . . . . . . . . . . . . . . . . . 67

21.5. 重新构造关键认证协议。 . . . . . . . 68

21.5.1. 对认证选择的用途在

重新构造关键认证协议。 . . . 69

21.5.2. 服务器考虑为重新构造钥匙

协议。 . . . . . . . . . . . . . . . . . . 69

21.5.3. 客户考虑为重新构造钥匙

协议。 . . . . . . . . . . . . . . . . . . 70

22. DHCP 选择。 . . . . . . . . . . . . . . . . . . . . . . . 70

22.1. DHCP 选择格式。 . . . . . . . . . . . . . . . 71

22.2. 客户标识符选择。 . . . . . . . . . . . . . . 71

22.3. 服务器标识符选择。 . . . . . . . . . . . . . . 72

22.4. 身分协会为Non-temporary 地址选择72

22.5. 身分协会为临时地址选择。 . 75

22.6. IA 地址选择。 . . . . . . . . . . . . . . . . . . 76

22.7. 选择请求选择。 . . . . . . . . . . . . . . . . 78

22.8. 特选选择。 . . . . . . . . . . . . . . . . . . 79

22.9. 占用时间选择。 . . . . . . . . . . . . . . . . . 79

22.10. 中转消息选择。 . . . . . . . . . . . . . . . . 80

22.11. 认证选择。 . . . . . . . . . . . . . . . . 81

22.12. 服务器Unicast 选择。 . . . . . . . . . . . . . . . . 82

22.13. 状态编码选择。 . . . . . . . . . . . . . . . . . 82

22.14. 迅速做选择。 . . . . . . . . . . . . . . . . . 83

22.15. 用户类选择。 . . . . . . . . . . . . . . . . . . 84

22.16. 贩卖者类选择。 . . . . . . . . . . . . . . . . . 85

22.17. Vendor-specific 信息选择。 . . . . . . . . . 86

22.18. 接口I.d 选择。 . . . . . . . . . . . . . . . . . 87

22.19. 重新构造消息选择。 . . . . . . . . . . . . . 88

22.20. 重新构造受理选择。 . . . . . . . . . . . . . . 89

23. 安全考虑. . . . . . . . . . . . . . . . . . . 89

24. IANA 考虑。 . . . . . . . . . . . . . . . . . . . 91

24.1. Multicast 地址。 . . . . . . . . . . . . . . . . . 92

24.2. DHCP 消息类型。 . . . . . . . . . . . . . . . . . 93

24.3. DHCP 选择。 . . . . . . . . . . . . . . . . . . . . 94

24.4. 状态编码。 . . . . . . . . . . . . . . . . . . . . 95

24.5. DUID . . . . . . . . . . . . . . . . . . . . . . . . 95

25. 鸣谢。 . . . . . . . . . . . . . . . . . . . . . 95

26. 参考。 . . . . . . . . . . . . . . . . . . . . . . . . 96

26.1. 基准参考。 . . . . . . . . . . . . . . . . 96

26.2. 情报参考。 . . . . . . . . . . . . . . . 97

A 。 选择出现消息类型。 . . . . . . . . . . . 98

B 。 选择出现 DHCP 选择的选项域。 . 99

Chair的地址。 . . . . . . . . . . . . . . . . . . . . . . . 99

作者的地址。 . . . . . . . . . . . . . . . . . . . . . . 100

充分的版权声明。 . . . . . . . . . . . . . . . . . . . 101

1. 介绍和概要

本文档描述了一个IPv6客户机/服务器协议DHCP,它主要为设备提供配置的管理。DHCP可以为设备分配由server指定的地址,以及其他包含在option中的配置信息,DHCP还可以通过对option的扩展传送本文档没有说明的配置信息。DHCP是有状态地址配置协议,无状态配置请查阅IPv6 无状态地址配置协议。

DHCPv4DHCPv6在操作模式和相关的配置信息上有很大的不同,在这里没有对两种服务的总体差别作相关介绍,如果有兴趣和需要,有一篇文档专门对DHCPv6携带IPv4地址和配置信息作了介绍。

接下来是对DHCP的概述,消息交换机制的解释和消息流实例。在1.2 1.3中只对DHCP操作的消息流作了简要的说明,其详细介绍在17 18 19部分。

1.1 协议和寻址

DHCP采用UDP协议实现客户端和服务器的信息交换,客户端使用链路-局部地址或者已由其他机制分配的地址传送和接收DHCP消息。

DHCP服务器使用一个预留的,链路-全局多播地址来接收由客户端发送的消息。由于client把大部分消息都发送到这个多播地址,因此client就不需要设置DHCP服务器的地址。

    如果客户端和服务器不在同一个链路上,在客户端链路上的DHCP中继代理将负责客户端和服务器端的消息转发,中继对客户端来说是透明的,因此后面部分省略了中继代理对消息转发操作的描述。

    客户端一旦找到了服务器的地址,大部分情形他将把消息采用单播的形式直接发送给服务器。

1.2 客户/服务器2则消息交换

    DHCP客户端不需要服务器为他分配IP地址时,他可以通过一条单独消息和与和服务器的答复,获得诸如可用的DNS服务器、NTP服务器列表等配置信息。要获得配置信息,客户端首先向All_DHCP_Relay_Agents_and_Servers多播地址发送Information-Request消息,服务器以包含有配置信息的Reply消息作为回答。

    这样的消息交换是在客户端不需要服务器为他分配IPv6地址,只需要配置信息作为前提。

当服务器要为客户端分配IPv6地址和其他的配置信息,用两个消息就可以实现交换,下面部分描述的使用四个消息。在这种情况下,客户端向All_DHCP_Relay_Agents_and_Servers发送Solicit消息,请求分配地址和其他的配置信息。在这个消息中包含暗示,客户端希望立刻收到服务器的Reply消息。如果服务器愿意为客户端分配地址,就马上回复Reply消息给客户端,配置信息和地址信息都包含在回复的消息中。

1.3 客户/服务器4则消息交换

    客户端要请求分配一个或多个IPv6地址,首先要找到DHCP服务器,然后才能请求分配地址和其他的配置信息。客户端向All_DHCP_Relay_Agents_and_Servers发送Solicit消息,查找可以利用的DHCP服务器,任何服务器只要收到客户端的请求,都要以Advertise消息答复。然后客户端选择其中的一个服务器发送Request消息,证实分配地址的和其他配置信息。在服务器返回的Reply消息中包含了对地址和配置信息的确认。

    正如前面部分所描述的,客户端要不中断的使用分配的地址,就需要向服务器发送Renew消息来延续地址的租期。

- 作者: jfw010 2006年06月15日, 星期四 11:13  回复(0) |  引用(0) 加入博采

主题:深入了解MFC中的文挡/视结构

主题:深入了解MFC中的文挡/视结构 
内容:

Visual C++ 6.0 以其功能强大、用户界面友好而倍受程序员们的青睐。但是,在当前的Microsoft 基本类库4.2 版本中,大约有将近200 个类,数千个函数,加之Microsoft 公司隐藏了一些技术细节,使得人们深入学习MFC变得十分困难。


  MFC的AppWizard可以生成三种类型的应用程序:基于对话框的应用、单文档应用(SDI)和多文档应用(MDI)。前两者的结构较简单,本文不再赘叙。笔者拟从MFC中的文档/视结构入手,分析一些函数的流程,并解决编制MDI 应用程序过程中的一些常见问题。


(一)、了解文档/视结构


  MFC应用程序模型历经多年以有了相当大的发展。有一个时期,它只是个使用应用程序对象和主窗口对象的简单模型。在这个模型中,应用程序的数据作为成员变量保持在框架窗口类中,在框架窗口的客户区中,该数据被提交显示器。随着MFC2。0的问世,一种应用程序结构的新方式----MFC文档/视结构出现了。在这种结构中,CFrameWnd繁重的任务被委派给几个不同类,实现了数据存储和显示的分离。一般情况下,采用文档/视结构的应用程序至少应由以下对象组成:


。应用程序是一个CwinApp派生对象,它充当全部应用程序的容器。应用程序沿消息映射网络分配消息给它的所有子程序。


。框架窗口是一CfrmeWnd派生对象。


。文档是一个CDocument派生对象,它存储应用程序的数据,并把这些信息提供给应用程序的其余部分。


。视窗是Cview派生对象,它与其父框架窗口用户区对齐。视窗接受用户对应用程序的输入并显示相关联的文档数据。


通常,应用程序数据存在于简单模型中的框架窗口中。在文档/视方式中,该数据移入称为document的独立数据对象。当然,文档不一定是文字,文档是可以表现应用程序使用的数据集的抽象术语。而用户输入处理及图形输出功能从框架窗口转向视图。单独的视窗完全遮蔽框架窗口的客户区,这意味着即使程序员直接绘画至框架窗口的客户区,视图仍遮蔽绘画,在屏幕上不出现任何信息。所以输出必须通过视图。框架窗口仅仅是个视图容器。


CDocument类对文档的建立及归档提供支持并提供应用程序用于控制其数据的接口。MDI应用程序可以处理多个类型的文档,每个类型的文档拥有一个相关联的文档模板对象。文档对象驻留在场景后面,提供由视图对象显示的信息。文档至少有一个相关联的视图。视图只能与一个文档相关联。


在文档/视方式中,对象的建立是由文档模板来管理的,它是CDocTemplate派生对象,建立并维护框架窗口,文档及视。


MFC调用命令处理程序以响应发生在应用程序中的事件。命令发送的优先级是:


活动的视图->框架窗口->文档->应用程序->默认窗口过程(DefWindowsProc)


总之,在文档/视方式中,文档和视是分离的,即:文档用于保存数据,而视是用来显示这些数据。文档模板维护它们之间的关西。这种文档/视结构在开发大型软件项目时特别有用。


(二)、了解与文档/视结构有关的各种类之间的关系。


在文档/视应用程序中,CWinApp对象拥有并控制文档模板,后者产生文档、框架窗口及视窗。这种相互关系如图(1)所示:


从用户的角度来看,“视”实际上是一个普通的窗口。象其他基于Widnows应用的窗口一样,人们可以改变它的尺寸,对它进行移动,也可以随时关闭它。若从程序员的角度来看,视实际上是一个从MFC类库中的Cview类所派生出的类的对象。文档对象是用来保存数据的,而视对象是用来显示数据的,并且允许对数据进行编辑。SDI或MDI的文档类是由Cdocument类派生出来的,它可以有一个或多个视类,而这些视类最终都是由Cview类派生出来的。视对象只有一个与之相联系的文档对象,它所包含的CView::GetDocument函数允许应用在视中得到与之相联系的文档,据此,应用程序可以对文档类成员函数及公共数据成员进行访问。如果视对象接受到了一条消息,表示用户在编辑控制中输入了新的数据,此时,视就必须通知文档对象对其内部数据进行相应的更新。


如果文档数据发生了变化,则所有的视都必须被通知到,以便它们能够对所显示的数据进行相应的更新。Cdocument::UpdateAllViews函数即可完成此功能。当该函数被调用时,派生视类的CView::OnUpdate函数被触发。通常OnUpdate函数要对文档进行访问,读取文档数据,然后再对视的数据成员或控制进行更新,以便反映出文档的变化。另外,还可以利用OnUpdate函数使视的部分客户区无效,以便触发Cview::OnDraw函数,利用文档数据来重新对窗口进行绘制。


在MDI应用程序中,可以处理多个文档类型,即多个文档模板,每个模板又可以有多个文档,每个文档又可以多视显示。为管理方便,上一级往往保留了下一级的指针列表。如图(2)所示:


解释如下:


(1)、每个应用程序类(CwinApp的派生类)都保留并维护了一份所有文档模板的指针列表,这是一个链表结构。应用程序为所要支持的每个文档类型动态分配一个CMultiDocTemplate 对象,


CmultiDocTemplate(UINT nIDResource,


CruntimeClass * pDocClass,


CruntimeClass * pFrameClass,


CruntimeClass * pViewClass );


并在应用程序类的CWinApp::InitInstance成员函数中将每个CMultiDocTemplate对象传递给CWinApp::AddDocTemplate。 该函数将一个文档模板加入到应用程序可用文档模板的列表中。函数原形为:


void AddDocTemplate(CdocTemplate * pTemplate);


应用程序可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置,利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。函数原形如下:


POSITION GetFirstDocTemplate( ) const;


CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;


第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。


(2)、一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。应用程序可以用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与模板相关的文档列表。函数原形为:


viaual POSITION GetFirstDocPosition( ) const = 0;


visual Cdocument *GetNextDoc(POSITION & rPos) const = 0;


如果列表为空,则rPos被置为NULL.


(3)、在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。函数原形如下:


CDocTemplate * GetDocTemplate ( ) const;


如果该文档不属于文档模板管理,则返回值为NULL。


(4)、一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自己的需要调用该函数。


Virtual POSITION GetFirstViewPosition( ) const;


Virtual CViw * GetNextView( POSITION &rPosition) cosnt;


应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL.


当在文档上新增一个视或删除一个视时,MFC会调用OnChangeViewList函数。如果被删除的视是该文档的最后一个视,则删除该文档。


(5)、一个视只能有一个文档。在视中,调用CView::GetDocument可以获得一个指向视的文档的指针。函数原形如下:


CDocument *GetDocument ( ) const;


如果该视不与任何文档相,则返回NULL.


(6)、MDI框架窗口通过调用CFrameWnd::GetActiveDocument 可以获得与当前活动的视相连的CDocument 指针。函数原形如下:


virtual CDocument * GetActiveDocument( );


(7)、通过调用CFrameWnd::GetActiveView 可以获得指向与CFrameWnd框架窗口连接的活动视的指针,如果是被CMDIFrameWnd框架窗口调用,则返回NULL。MDI框架窗口可以首先调用MDIGetActive找到活动的MDI子窗口,然后找到该子窗口的活动视。函数原形如下:


virtual Cdocument * GetActiveDocument( );


(8)、MDI框架窗口通过调用CFrameWnd::GetActiveFrame, 可以获得一个指向MDI框架窗口的活动多文档界面子窗口的指针。


(9)、CMDIChildWnd调用GetMDIFrame获得MDI框架窗口(CMDIFrameWnd)。


(10)、CWinApp 调用AfxGetMainWnd得到指向应用程序的活动主窗口的指针。


下面一段代码,就是利用CDocTemplate、CDocument和CView之间的存取关系,遍历整个文档模板、文档以及视。


CMyApp * pMyApp = (CMyApp *)AfxGetApp();


POSITION p = pMyApp->GetFirstDocTemplatePosition();


while(p!= NULL) {


CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p);


POSITION p1 = pDocTemplate->GetFirstDocPosition();


while(p1 != NULL) {


CDocument * pDocument = pDocTemplate->GetNextDoc(p1);


POSITION p2 = pDocument->GetFirstViewPosition();


while(p2 != NULL) {


CView * pView = pDocument->GetNextView(p2);


}


}


}


(图4)、遍历整个文档模板、文档和视


在应用程序的任何地方,程序员都可以调用AfxGetApp( )获得应用程序的对象指针。由于本文着重介绍文档/视的关系,至于框架窗口之间的关系没能列全,读者可以查相应的文档。


(三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程序流程。


(1)、CwinApp::OnFileNew和CwinApp::OnFileOpen函数的简单流程。


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


--------------------------------------------------------------------------------


在CWinApp::OnFile/new 或CwinApp::OnFileOpen函数中,核心操作是CDocTemplate::OpenDocument函数。其函数原型为:


virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE ) = 0;


图(4)中星号标注之后即是该函数的流程,简要介绍如下:


(1)、CDocTemplate::CreateNewDocument函数创建一个新文档,其类型与文档模板相关,并通过函数CDocTemplate::AddDocument加入该文档模板的文档指针列表中。此时,文档类的构造函数被执行,程序可以在此进行文档的初始化。


(2)、函数CDocTemplate::CreateNewFrame调用MDI子窗口类(CMDIChildWnd)的构造函数,生成MDI子窗口对象。接着调用CMDIChildWnd::PreCreateWindow。然后,生成一个CCreateContext对象,(CcreateContext是MFC框架所使用的一种结构,它将构成文档和视的组件联系起来。后文将详细介绍之。)并将该对象值传给CMDIChildWnd::OnCreateClient函数。MFC调用此函数,用CCreateContext对象提供的信息创建一个或多个CView对象。此时,各视的构造函数被依次调用。


(3)、接着,判断lpszPathName是否为空。分为两种情况:


(a)、若为空,则表明要创建一个新文档:调用SetDefaultTitle函数装载文档的缺省标题,并显示在文档的标题栏中;然后执行CDocument::OnNewDocument。该函数调用DeleteContents以保证文档为空,然后置新文档为清洁。可以重载该函数。


(b)、否则,表明要打开一个已存在的文档:调用CDocument::OnOpenDocument打开指定的文件;执行DeleteContext,保证文档为空;调用CObject::Serialize读入该文件的内容。(程序员可在此进行文件的读入操作。当然,也可以在CDocument::OnOpenDocument中读入文件)。然后置文档为清洁;最后,调用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。


(4)、调用CDocTemplate::InitialUpdateFrame函数,使框架窗口中的各个视收到OnInitialUpdate调用。框架窗口的主视(子窗ID等于AFX_IDW_PANE_FIRST的视)被激活。程序员可以在此对视对象进行初始化。


(2)、Window/New命令的程序流程


当主框架窗口上有子窗口时,选择Window/New命令可以生成该活动子窗口的影象。它们有相同的文档模板、相同的文档。其流程如下:


 


 


 


 


执行Window/New的过程与File/New的过程差不多。所不同的是,File/New须要创建一个新文档,而Window/New则是获得已存在的MDI子窗口的文档。因此以前存在的视和New以后生成的视均为该文档的视,都是该文档的内容的显示。当调用CDocument::UpdateAllViews函数时,它们(视)的OnUpdate函数都将被激活。此时,在该文档的视指针列表中,将有多于一个的视(具体数目视Window/New执行的次数而定)。读者可以利用(图3)中的代码跟踪程序结果。


 


(四)、几种情况的讨论


上面,笔者就MFC中文档/视的关系进行了分析,下面,笔者将结合具体情况进行讨论:


(1)、如何根据自己的要求来选择文档模板,及相应的视和文档。


在通常的MDI应用程序中,只有一个文档模板,程序员只能打开一种类型的文档。因此,程序员只要调用File/New或者File/Open创建或者打开文档即可,至于文档、视和框架窗口之间的关系,由文档模板在幕后控制,不须要对文档模板进行操作。但是,如果应用程序需要处理多种类型的文档,并且何时打开何种文档均需程序员手工控制,此时,程序员必须对文档模板进行编程。


例如,笔者需要处理AVI和BMP两种文件类型。AVI和BMP的数据存放格式不同,不能用同一的数据结构来描述,因此,把它们的数据都存入一个文档是不合适的。同时,由于AVI是图象序列,BMP仅是一幅图象,它们的显示是肯定不一样的,即它门的视不同。基于此,笔者决定分别建立两套文档模板,两套框架窗口,两套文档和两套视,分别用于AVI和BMP的数据存放和显示。程序可以根据用户选择的文件名来分别处理AVI和BMP。具体步骤如下:


(Step 1)、在应用程序类(CWinApp)的派生类中增加文档模板成员变量,以便对文档模板进行操作。


class C3dlcsApp : public CWinApp


{ 。。。 。。。


public:


CMultiDocTemplate * m_pAVIDocTemplate;


CMultiDocTemplate * m_pBMPDocTemplate;


}


(Step 2)、在主框架中增加菜单响应:


void CMainFrame::OnFileOpen() {


CFileDialog my(true);


if(my.DoModal()==IDOK) {


CString FileName = my.GetPathName();


CString FileExt = my.GetFileExt();


if((FileExt == "AVI") || (FileExt == "avi")) {


CMyApp * pMyApp = (CMyApp *)AfxGetApp();


CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate;


pAVIDocTemplate->OpenDocumentFile(FileName);


}


else if((FileExt == "BMP") || (FileExt == "bmp")) {


CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp();


CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate;


pDATDocTemplate->OpenDocumentFile(FileName);


}


else {


AfxMessageBox("Yor select a file not supported!");


return;


}


}


}


笔者把用户输入文件名的后缀作为分支条件,如果是AVI文件,则先获得关于AVI文件的文档模板,然后调用CDocTemplate::OpenUpdateFrame (lpszFileName)函数打开此文档。正如前面所分析,此函数将依次生成新文档,新框架,在CMDIChildWnd::OnCreateClient中创建视,最后向框架中所有的视发送初始化消息,使其显示在屏幕上。如果是BMP文件,操作类似。


当然,程序员也可以在程序的任何位置实现此操作:通过全局函数AfxGetApp 获得应用程序对象指针,从而获得相应的文档模板指针。


由于由AppWizard生成的应用程序会缺省调用CWinApp::OnFileNew,所以当程序开始执行时,会在主框架上显示一个新的空窗口。如果想去掉这个空窗口,只须重载CWinApp::OnFileNew函数,不许要任何代码,即可。


 


(2)、切分窗口与文档/视结构


一个文档可以有多个视,切分窗口即是表示多视的一种方法。 切分窗口是通过类CSplitterWnd来表示的,对Window来说,CSplitterWnd对象是一个真正的窗口,它完全占据了框架窗口的客户区域,而视窗口则占据了切分窗口的窗片区域。切分窗口并不参与命令传递机制,(窗片中)活动的视窗从逻辑上来看直接被连到了它的框架窗口中。


切分窗口可以分为动态和静态两种。前者较简单,本文仅讨论后者。创建切分窗口的步骤如下:


(Step 1)、在自己的框架窗口中声明成员变量,用以对切分窗口进行操作。


class CMyFrame : public CMDIChildWnd


{ 。。。 。。。


CSplitterWnd m_Splitter;


CSplitterWnd m_Splitter2;


}


(Step 2)、重载CMDIChildWnd::OnCreateClient函数,创建切分窗口。


BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)


{


BOOL btn = m_Splitter.CreateStatic(this,1,2);


btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext);


m_Splitter2.CreateStatic(&m_Splitter,


2, 1,


WS_CHILD | WS_VISIBLE | WS_BORDER,


m_Splitter.IdFromRowCol(0, 1));


btn |= m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView),


CSize(100,100), pContext);


btn |= m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView),


CSize(100,100), pContext);


return btn;


//return CMDIChildWnd::OnCreateClient(lpcs, pContext);


}


CFrameWnd::OnCreateClient函数原形为:


virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);


缺省的CMDIChildWnd::OnCreateClient函数根据pContext参数提供的信息,调用CFrameWnd::CreateView函数创建一个视。可以重载该函数,加载CCreateContext对象中传递的值,或改变框架窗口主客户区中控制的创建方式。在上面的程序中,笔者 创建了3个切分窗口。比如打开了一个名为“a.avi”的文档,此时该文档将有3个视,一个框架窗口。如果执行了Window/New操作,则此时有一个文档,6个视和2个框架窗口。若该文档调用CDocument::UpdateAllViews函数,则这6个视的CView::OnUpdate函数都会被激发。


(3)、关于CCreateContext的讨论。


CCreateContext是MFC框架所使用的一种结构,它将构成文档/视的组件联系起来。这个结构包括指向文档的指针,框架窗口,视以及文档模板,它还包含一个指向CRuntimeClass的指针,以指明所创建的视的类型。其数据成员如下:


m_pNewViewClass:指向创建上下文的视的CRuntimeClass的指针。


m_pCurrentDoc:指向文档对象的指针,以和新视联系起来。


m_pNewDocTemplate:指向与框架窗口的创建相联系文档模板的指针。


m_pLastView:指向已存在的视,它是新产生的视的模型。


m_pCurrentFrame:指向已存在的框架窗口,它是新产生的框架窗口的模型。


程序员可以通过改变CCreateContext对象的值,来创建更加灵活的视。由于过程较复杂,笔者不再赘许叙,读者可参阅相关的Visual C++ Help文档。


(五)、结束语


Visual C++ 6.0的文档/视结构代表了一种新的程序设计方式,其核心是文档与视的分离,即数据存放与显示(操作)的分离。在MFC类库中,各个对象之间的关系很复杂,但,只要深入了解后,会发现它们之间是相互联系的,可以相互存取的。如果大家想设计出灵活、健壮的应用程序,就必须深入了解MFC。跟踪原代码就是一个较好的方法。文档/视的关系的确非常复杂,如果能知道每个函数是在哪调用的,执行了何种操作,就能游人刃有余,写出优美的应用程序。

 

- 作者: jfw010 2005年08月20日, 星期六 11:20  回复(1) |  引用(0) 加入博采

[转载] 使用VC6.0实现窗口的任意分割

一、关于CSplitterWnd类
我们在使用CuteFtp或者NetAnt等工具的时候,一般都会被其复杂的界面所吸引,在这些界面中窗口被分割为若干的区域,真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面,也实现窗口的任意的分割呢 ?在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口,每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条总是不能让我们满意,因此我们还是通过手工增加代码来熟悉这个类。
CSplitterWnd的构造函数主要包括下面三个。

BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,
                   CCreateContext* pContext,DWORD dwStyle,UINT nID);
功能描述:该函数用来创建动态切分窗口。
参数含义:pParentWnd 切分窗口的父框架窗口。 nMaxRows,nMaxCols是创建的最大的列数和行数。 sizeMin是窗格的现实大小。 pContext 大多数情况下传给父窗口。 nID是字窗口的ID号.

BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,
                           DWORD dwStyle,UINT nID)
功能描述:用来创建切分窗口。
参数含义同上。

BOOL CreateView (int row,int col,CruntimeClass* pViewClass,
                         SIZE sizeinit,CcreateContext* pContext);
功能描述:为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必须先将切分窗口创建好。
参数含义:同上。

从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic,在函数中都调用了一个保护函数CreateCommon,从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗口。
DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
if (afxData.bWin4)
       dwCreateStyle &= ~WS_BORDER; //create with the same wnd-class as MDI-Frame (no erase bkgnd)
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle,
          0, 0, 0, 0,pParentWnd->m_hWnd, (HMENU)nID, NULL))
       return FALSE; // create invisible
          

二、创建嵌套分割窗口
2.1创建动态分割窗口
动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。
m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);

但是动态创建的分割窗口的窗格数目不能超过2x2,而且对于所有的窗格,都必须共享同一个视图,所受的限制也比较多,因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。
2.2创建静态分割窗口
与动态创建相比,静态创建的代码要简单许多,而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。
在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下:
-----------------------
| CCuteFTPView   |
-----------------------
|CView2 |CView3 |
-----------------------
|        CView4       |
-----------------------

创建步骤:
▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP,生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2, CView3, CView4.
▲ 增加成员:
在Cmainfrm.h中我们将增加下面的代码:


CSplitterWnd wndSplitter1;
CSplitterWnd wndSplitter2;
▲ 重载CMainFrame::OnCreateClient()函数:
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT  /*lpcs*/,
                                                  CCreateContext* pContext)
{ //创建一个静态分栏窗口,分为三行一列
     if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
              return FALSE;
  //将CCuteFTPView连接到0行0列窗格上
     m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
     m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
  //将CView4连接到2行0列
     if(m_wndSplitter2.CreateStatic(&m_wndSplitter1,1,2,WS_CHILD|WS_VISIBLE,
          m_wndSplitter1.IdFromRowCol(1, 0))==NULL)
               return FALSE; //将第1行0列再分开1行2列
  //将CView2类连接到第二个分栏对象的0行0列
          m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
  //将CView3类连接到第二个分栏对象的0行1列
          m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
               return TRUE;
}
2.3实现各个分割区域的通信
■有文档相连的视图之间的通信
由AppWizard生成的CCuteFTPView是与文档相连的,同时我们也让CView2与文档相连,因此我们需要修改CCuteFTPApp的InitInstance()函数,我们将增加下面的部分。
AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE,
          RUNTIME_CLASS(CMainDoc),
          RUNTIME_CLASS(CMDIChildWnd),
          RUNTIME_CLASS(CView2)));
我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类是不能安全的与除文档类之外的其余的视图类通信的。因此我们只能让他们都与文档类通信。在文档中我们设置相应的指针以用来获的各个视图。我们重载CCuteFTPView::OnOpenDocument()函数;
CCuteFTPView* pCuteFTPView;
CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
      pView=GetNextView(pos);
      if(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
          pCuteFTPView=(CCuteFTPView*)pView;
      else(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
          pView2=(CView2*)pView;
}
这样我们在文档类中就获的了跟它相连的所有的视图的指针。
如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下:
CCuteFTPDoc* pDoc=GetDocument();
CView2* pView2=pDoc->pView3;
pView3.DoIt();

■无文档视图与文档关联视图之间的通信
CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说,CView2只能安全的与CCuteFTPDoc通信,因此,CView3如果需要跟CView2通信,也必须借助于文档类。因此程序的关键是如何在CView3中获得文档的指针。视图类中没有这样的类成员可以用来直接访问文档类。但是我们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获得程序主窗口了的指针,就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。

CView3中的代码如下:
          CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
          CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame->GetActiveDocument();
          if(Doc!=NULL) Doc->DoIt();
          
          CCuteFTPDoc中的相应的处理函数DoIt()代码如下:
          CView2* pView2;
          POSITION pos;
          CView* pView;
          while(pos!=NULL)
          {
                  pView=GetNextView(pos);
                  if(pView->IsKindOf(RUNTIME_CLASS(CView2))==NULL)
                  pView2=(CView2*)pView;
          }
          pView2->DoIt();
■无文档关联视图之间的通信
CView3和CView4都是不跟文档相连的,如何实现他们之间的通信呢。 正如我们在上面所说的那样,由于在主框架中我们可以访问任意的视图,因此我们的主要任 务还是在程序中获得主框架的指针。在CView3中访问CView4中的方法DoIt()。
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
          
          CView4* View4=(CView4*)MainFrame->m_wndSplitter1.GetPane(2,0);
          View4->DoIt();

到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以实现其他的一些流行界面例如NetAnts,Foxmail的分割。

三、关于对话框的分割
到目前为止,只有基于文档/视图的程序才能使用CSplitterWnd,而基于对话框的应用程序却不支持CSplitterWnd,但是如果我们在继承类中重载一些虚拟方法,也能使CSplitterWnd 在对话框程序中使用。从MFC的源程序WinSplit.cpp中可以看出,为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame(),因此如果在对话框中使用,我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。
virtual void StartTracking(int ht);
virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol  = NULL);
virtual void SetActivePane( int row, int col, CWnd* pWnd  = NULL );
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );
virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );
具体实现如下,实现中我将给出原有代码的主要部分以及修改后的代码以作对比。
在cpp文件中加入下面的枚举类型。
enum HitTestvalue
{
                  noHit = 0,//表示没有选中任何对象
                  vSplitterBox = 1,
                  hSplitterBox = 2,
                  bothSplitterBox = 3,
                  vSplitterBar1 = 101,//代表各个方向的水平分割条
                  vSplitterBar15 = 115,
                  hSplitterBar1 = 201,//代表垂直方向的各个分割条
                  hSplitterBar15 = 215,
                  splitterIntersection1 = 301,//代表各个交叉点
                  splitterIntersection225 = 525
};
          
CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
          {
                  ASSERT_VALID(this);
                  //获得当前的获得焦点的窗口
                  //下面注释粗体的是原有的代码的主要部分。
                  // CWnd* pView = NULL;
                  //CFrameWnd* pFrameWnd = GetParentFrame();
                  //ASSERT_VALID(pFrameWnd);
                  //pView = pFrameWnd->GetActiveView();
                  //if (pView == NULL)
                  // pView = GetFocus();
                  CWnd* pView = GetFocus();
                  if (pView != NULL && !IsChildPane(pView, pRow, pCol))
                          pView = NULL;
                  return pView;
}
          
void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd)
{
                  CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
                  //下面加注释粗体的是原有代码的主要部分。
                  //FrameWnd* pFrameWnd = GetParentFrame();
                  //ASSERT_VALID(pFrameWnd);
                  //pFrameWnd->SetActiveView((CView*)pPane);
                  pPane->SetFocus();//修改后的语句
}
          
void CxSplitterWnd::StartTracking(int ht)
{
                  ASSERT_VALID(this);
                  if (ht == noHit)
                          return;
                  // GetHitRect will restrict ’’’’m_rectLimit’’’’ as appropriate
          
                  GetInsideRect(m_rectLimit);
                  if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
          
                  {
                          // split two directions (two tracking rectangles)
          
                          int row = (ht - splitterIntersection1) / 15;
          
                          int col = (ht - splitterIntersection1) % 15;
          
                          GetHitRect(row + vSplitterBar1, m_rectTracker);
          
                          int yTrackOffset = m_ptTrackOffset.y;
                          m_bTracking2 = TRUE;
                          GetHitRect(col + hSplitterBar1, m_rectTracker2);
          
                          m_ptTrackOffset.y = yTrackOffset;
                  }
                  else if (ht == bothSplitterBox)
                  {
                  // hit on splitter boxes (for keyboard)
                  GetHitRect(vSplitterBox, m_rectTracker);
                  int yTrackOffset = m_ptTrackOffset.y;
                  m_bTracking2 = TRUE;
                  GetHitRect(hSplitterBox, m_rectTracker2);
                  m_ptTrackOffset.y = yTrackOffset; // center it
                  m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
                  m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
                  }
                  else
                  {
                  // only hit one bar
                  GetHitRect(ht, m_rectTracker);
                  }
          
          //下面加注释的将从程序中删去。
          //CView* pView = (CView*)GetActivePane();
          //if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
          //{
          // ASSERT_VALID(pView);
          // CFrameWnd* pFrameWnd = GetParentFrame();
          //ASSERT_VALID(pFrameWnd);
          //pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
          // }
          // steal focus and capture
                  SetCapture();
                  SetFocus();
                  // make sure no updates are pending
                  RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
          
                  // set tracking state and appropriate cursor
                  m_bTracking = TRUE;
                  OnInvertTracker(m_rectTracker);
                  if (m_bTracking2)
                          OnInvertTracker(m_rectTracker2);
                  m_htTrack = ht;
                  SetSplitCursor(ht);
}
          
BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
                  if (CWnd::OnCommand(wParam, lParam))
                          return TRUE;
                  //下面粗体的是原程序的语句
          //return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
          
                  return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);
          
}
BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
                  if (CWnd::OnNotify(wParam, lParam, pResult))
                          return TRUE;
                  //下面粗体的是源程序的语句
                  //*pResult = GetParentFrame()->SendMessage(WM_NOTIFY,
          wParam, lParam);
                  *pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);
                  return TRUE;
}
          
BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam,
                                         LPARAM lParam, LRESULT* pResult)
{
                  // The code line below is necessary if using CxSplitterWnd
          in a regular dll
                  // AFX_MANAGE_STATE(AfxGetStaticModuleState());
                  return CWnd::OnWndMsg(message, wParam, lParam, pResult);
          
}
这样我们就可以在对话框中使用CxSplitterWnd类了。

四、CSplitterWnd的扩展
CSplitterWnd扩展话题是很多的,我们可以通过对原有方法的覆盖或者增加新的方法来扩展CSplitterWnd。我们在此仅举两个方面的例子。
4.1锁定切分条
当用户创建好分割窗口后,有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。锁定切分条的最简单的方法莫过于不让CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息,而是将这些消息交给CWnd窗口进行处理,从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。修改为如下:
void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point) {
        CWnd::OnLButtonDown(nFlags,point);
}
其余的处理方法类似。
4.2切分条的定制
由Window自己生成的切分条总是固定的,没有任何的变化,我们在使用一些软件比如ACDSee的时候却能发现它们的切分条却是和自动生成的切分条不一样的。那么如何定制自己的切分条呢?通过重载CSplitterWnd的虚方法OnDrawSplitter和OnInvertTracker可以达到这样的目的。下面的代码生成的效果是分割窗口的边界颜色为红色,分割条的颜色为绿色.代码如下:
void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &rectArg)
{
                  if(pDC==NULL)
                  {
                  RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);
                  return;
                  }
                  ASSERT_VALID(pDC);
                  CRect rc=rectArg;
                  switch(nType)
                  {
                  case splitBorder:
                  //重画分割窗口边界,使之为红色
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
                          rc.InflateRect(-CX_BORDER,-CY_BORDER);
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
          
                          return;
                  case splitBox:
                          pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
                          rc.InflateRect(-CX_BORDER,-CY_BORDER);
                          pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
                          rc.InflateRect(-CX_BORDER,-CY_BORDER);
                          pDC->FillSolidRect(rc,RGB(0,0,0));
                          pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
                          return;
                  case splitBar:
                  //重画分割条,使之为绿色
                          pDC->FillSolidRect(rc,RGB(255,255,255));
                          rc.InflateRect(-5,-5);
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
          
                          return;
                  default:
                          ASSERT(FALSE);
                  }
                  pDC->FillSolidRect(rc,RGB(0,0,255));
}
void CSplitterWndEx::OnInvertTracker(CRect &rect)
{
                  ASSERT_VALID(this);
                  ASSERT(!rect.IsRectEmpty());
                  ASSERT((GetStyle()&WS_CLIPCHILDREN)==0);
                  CRect rc=rect;
                  rc.InflateRect(2,2);
                  CDC* pDC=GetDC();
                  CBrush* pBrush=CDC::GetHalftoneBrush();
                  HBRUSH hOldBrush=NULL;
                  if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC->m_hDC,
                                                                  pBrush->m_hObject);
                  pDC->PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS);
          
                  if(hOldBrush!=NULL)
                  SelectObject(pDC->m_hDC,hOldBrush);
                  ReleaseDC(pDC);
}
同样我们只要继承CSplitterWnd中的其余的一些虚拟方法就可以生成具有自己个性的分割窗口了。

- 作者: jfw010 2005年08月20日, 星期六 11:17  回复(0) |  引用(0) 加入博采

vc调试及microsoft flexgrid控件找不到

在调试vc程序时,我发现有好多错误(例如说什么前面缺少“:”,其实并不少)都是由于缺少头文件,或者缺少库文件造成的,添加进去之后就可以解决了。

载不知道哪里出错时,可以通过debug找到哪里的错误,一般是缺少文件, 把文件加到所需的地方。

在安装了office2003后,原来程序的microsoft flexgrid 控件不见了,打开程序上面替代的是一个activx控件,从project菜单里面添加也只有一个Herachal Flexgrid控件,并不是所要的,于是,我把visual studio的sp4,sp5不定给加了进去,那个控件居然奇迹般的出现了,程序可以找到原来的控件了,不知道是什么原因造成的,也不知道,添加sp不定是不是解决办法,总之,一切都好了。

- 作者: jfw010 2005年08月9日, 星期二 20:30  回复(1) |  引用(0) 加入博采

微软输入法2527不能输入汉字

在安装office2003 时,附带安装了2527,就是不能输入汉字,还有好多功能都不能使用,用超级兔子卸载不行,没有完全卸载,还是不能用,我下载了一个输入法清理工具,顺利的把它卸载了,这个工具不错。

- 作者: jfw010 2005年08月9日, 星期二 20:21  回复(0) |  引用(0) 加入博采

添加控件(或变量)编译通过,不能显示对话框

我在添加microsoft flexgrid控件后,不能显示对话框(Project-〉Componet添加控件)

通过调试,显示

>>> If this dialog has OLE controls:
>>> AfxEnableControlContainer has not been called yet./////////
>>> You should call it in your app's InitInstance function.
The thread 0x6B0 has exited with code 0 (0x0).
The program 'E:\wang\font\Debug\Day7.exe' has exited with code 0 (0x0).

在初始化函数中添加上面所提示的函数就可以了。

BOOL CDay7App::InitInstance()
{
AfxEnableControlContainer();

- 作者: jfw010 2005年08月5日, 星期五 11:38  回复(0) |  引用(0) 加入博采

CMyFontDialog

The MFC CFontDialog class encapulates the standard Font common dialog box. This dialog allows you to select a font. It display a sample window showing some text in the required font. It is nice to be able to specify your own sample text - for example, if I am editing a text object in a drawing package, I would want to see the actual text in the sample box.

However, the MFC implmentation does not let you easily specify the sample text.

The following class, derived from CFontDialog, lets you do just this. It also lets you set and get the color selected in the dialog. Simple create a CMyFontDialog and specify the sample text in the constructor (or call SetSampleText() before calling DoModal()). Also call SetTextColor() before calling DoModal(), and retrieve the selected color afterwards with TextColor().


// FontDialog.h
// (c) 1997 Roger Onslow

#ifndef _CMyFontDialog_
#define _CMyFontDialog_

class CMyFontDialog : public CFontDialog {
     DECLARE_DYNCREATE(CMyFontDialog);
public:
     CMyFontDialog(LPLOGFONT lplogfont=NULL, LPCTSTR sampletext="Sample Text", CWnd* pParentWnd=NULL);
protected:
     LPCTSTR m_sampletext;
public:
     CString SampleText() const { return m_sampletext; }
     void SetSampleText(LPCTSTR sampletext) { m_sampletext = sampletext; }
public:
     COLORREF TextColor() const { return m_cf.rgbColors; }
     void SetTextColor(COLORREF rgbColors) { m_cf.rgbColors = rgbColors; }
protected:
     // Dialog Data
protected:
     //{{AFX_DATA(CMyFontDialog)
     //}}AFX_DATA
     // Overrides
protected:
     // ClassWizard generate virtual function overrides
     //{{AFX_VIRTUAL(CMyFontDialog)
     virtual BOOL OnInitDialog();
     //}}AFX_VIRTUAL
     // Implementation
protected:
     // Generated message map functions
     //{{AFX_MSG(CMyFontDialog)
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

#endif




////////////////////////////////////////////////////////////////////
// FontDialog.cpp
// (c) 1997 Roger Onslow

#include "stdafx.h"
#include "FontDialog.h"
#include "Dlgs.h "

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CMyFontDialog, CFontDialog)

BEGIN_MESSAGE_MAP(CMyFontDialog, CFontDialog)
//{{AFX_MSG_MAP(CMyFontDialog)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CMyFontDialog::CMyFontDialog(LPLOGFONT lpLogfont, LPCTSTR sampletext, CWnd* pParentWnd)
		: CFontDialog(lpLogfont,CF_EFFECTS | CF_SCREENFONTS, NULL, pParentWnd)
		, m_sampletext(sampletext)
{
}

BOOL CMyFontDialog::OnInitDialog() {
     BOOL r = CFontDialog::OnInitDialog();
     if (m_sampletext) {
          SetDlgItemText(stc5, m_sampletext);
     }
     return r;
}
#include Dlgs.h (in brackets) in order to be able to use 'stc5'. 

- 作者: jfw010 2005年08月5日, 星期五 10:55  回复(0) |  引用(0) 加入博采

实现程序在静态文本控件上的画图

实现程序在静态文本控件上的画图

新建一个dialog工程,在对话框中加如两个控件,一个是静态文本控件,ID改为IDC_WENBEN,一个是按纽控件ID默认,填加它的单击事件,代码如下。

void CWenBenHuaTuDlg::OnButton1() 
{
    // TODO: Add your control notification handler code here
    CPaintDC dc(this);

    CWnd* pWnd=GetDlgItem(IDC_WENBEN);

    CDC* pDC=pWnd->GetDC();
    pWnd->Invalidate();
    pWnd->UpdateWindow();

    pDC->Rectangle(0,0,300,300);


    //创建画笔对象**************画X和Y轴
    CPen* pPenRed=new CPen;
    pPenRed->CreatePen(PS_SOLID,1,RGB(255,0,0));

    CGdiObject* pOldPen=pDC->SelectObject(pPenRed);

    pDC->MoveTo(10,10);
    pDC->LineTo(10,280);
    pDC->LineTo(280,280);


    //创造画笔对象*****************画X轴和Y轴箭头
    CPen* pPenBlue=new CPen;
    pPenBlue->CreatePen(PS_SOLID,1,RGB(0,0,255));

    pDC->SelectObject(pPenBlue);

    pDC->MoveTo(5,15);
    pDC->LineTo(10,10);
    pDC->LineTo(15,15);

    pDC->MoveTo(275,275);
    pDC->LineTo(280,280);
    pDC->LineTo(275,285);

    //写X轴刻值
    CString str;
    str.Format("0");
    pDC->SetTextColor(RGB(0,255,0));
    pDC->TextOut(10,283,str);
    str.Format("50");
    pDC->TextOut(60,283,str);
    str.Format("100");
    pDC->TextOut(110,283,str);
    str.Format("150");
    pDC->TextOut(160,283,str);
    str.Format("200");
    pDC->TextOut(210,283,str);
    str.Format("250");
    pDC->TextOut(260,283,str);


    //写X轴刻度线
    for(int  i=0;i<256;i+=5)
    {
        if((i&1)==0)
        {//10的倍数
            pDC->MoveTo(i+10,280);
            pDC->LineTo(i+10,284);
        }
        else
        {
            pDC->MoveTo(i+10,280);
            pDC->LineTo(i+10,282);

        }

    }


    pDC->SelectObject(pOldPen);

    //删除新的画笔
    delete pPenRed;
    delete pPenBlue;


}

- 作者: jfw010 2005年08月5日, 星期五 08:55  回复(0) |  引用(0) 加入博采

CEdit 调用

GetDlgItemText(IDC_EDIT1, temp);

 SetDlgItemText(IDC_EDIT1, temp);

主要是这两个函数,一个得到里面的内容,一个写入内容。

void opdialog::OnEnChangeEdit1()
{
CString temp;
int i = 0;
GetDlgItemText(IDC_EDIT1, temp);//
while(temp[i])
{
  if( (temp[i] < 48 || temp[i] > 57) && temp[i] != 46)
  {
  temp.Delete(i);
  SetDlgItemText(IDC_EDIT1, temp);//
  break;
  }
  i++;
}
}

- 作者: jfw010 2005年08月4日, 星期四 16:47  回复(0) |  引用(0) 加入博采