关于作者

用户名:volnet
笔名:volnet
地区:
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



MyMSDN

http://www.cppblog.com/mymsdn

MyMSDN图库

http://mymsdn.photo.163.com

访问统计:
文章个数:30
评论个数:3
留言条数:0




Powered by BlogDriver 2.1

玩转C科技

 

玩转C科技:http://pChouse.bokee.com 图片技术文档库:http://myMSDN.photo.163.com 玩转高科技,专注于开发 myMSDN<->pChouse开启网络开发平台

文章

Product:《SysClock(系统关机精灵)》

Product:《SysClock(系统关机精灵)》

《SysClock(系统关机精灵)》


下载地址: http://www.cppblog.com/Files/mymsdn/SysClock(Beta1).rar


软件名称:SysClock(系统关机精灵)
软件语言:简体中文
版本号:1.0(Beta1)
软件大小:372KB(完整安装包3,796KB)
界面预览:

SysClock界面预览(http://www.cppblog.com/images/cppblog_com/mymsdn/2719/o_SysClock.gif

授权方式:免费版/开放源代码(提供开发文档)
应用平台:Windows 2000/XP,其他平台未测试过~欢迎测试并反馈
联系人:volnet@tom.com
开发商:GoCoolSoft:www.cppblog.com/mymsdn


软件介绍:
   想必每个人都有这样的经历,也许开启电脑的时候,你计划在23点~(差不多可以睡觉了),可是每次都是拖了又拖,也许是0点,也许是2点~隔日没事也就罢了,要是有事,就会因此而精神疲乏~,其实这就是我开发这个软件的初衷,限制自己开机的时间,只需输入想要关机的时间,到时候等电脑关机了,我想你应该不会再去打开它吧!即使有重要的事情,也要停一停,关机前5分钟,精灵将提醒您保存手头的工作,也不会让你手忙脚乱,如果您真的有必须熬夜的事,那就在进程里关闭……,时间就是这样显得更有条理,我们有理由相信,SysClock(系统关机精灵)虽然简单,但一定是你调节生活的好伙伴,我们也有理由相信,开放源代码的它能在您的精心改造下更加完善~!让我们一起把它做到最好,让它更个性更完美!


相关文件:
开发档案:《SysClock(系统关机精灵)》
(链接地址:http://www.cppblog.com/mymsdn/archive/2006/09/29/13159.html

产品下载:
http://www.cppblog.com/Files/mymsdn/SysClock(Beta1).rar

- 作者: volnet 2006年09月29日, 星期五 23:06  回复(0) |  引用(0) 加入博采

《从零到知道》(《from0toknow》)专题更新完毕!欢迎前往下载/在线阅读!
一个关于VC++的专题终于还是写完了,主要是关于VC++,MFC开发的部分知识。其中挺重要的两块,网络和数据库没有写进来,原因主要是因为这两块面比较广,而且也是很重点的,网上随便搜索都能搜索得到很多教程之类的。因此我就没有大费周章来这里写了。这个专题我取名叫《从零到知道》(《from0toknow》)。因为都是一些浅层的知识,对入门的门槛要求比较低。知识是比较陈旧的MFC,但是也是很实用的东西,基本思想等内容还是很受用的。
主要特点:《从零到知道》(《from0toknow》)之所以取这个名字,顾名思义是起个入门的作用,文章立足于动手实验,但是不乏理论指导,能提到点到的都记录着呢。学习,最好能看到成效,能做出点东西来,大家就看得比较过瘾,也比较有动力。但是很多朋友在看文档的时候或者由于不是很注意,或者由于文档有疏漏,常常很难编译通过。因此我特地把同步代码更新到相应的压缩包中了。下载压缩包可以获得全部附着代码文档和彩色图文解说的完整版(原因见下)。原因:写它们的时候我是使用WORD,因此有很多图片都贴进去了。在同步更新到Blog后,丢失了很多图片,可能会影响阅读。由于时间有限不能仔细修改,因此直接把他们分别打包起来了放在相应标题的旁边,大家可以点击下载!所有代码均经过测试,详见附录!
以下是本专题在CNBLOGS的文章链接,文章并未附图,如果因为缺失图片而影响阅读,请点旁边的链接下载后用AdobeReader进行阅读。相关代码的开发环境请见附录。

Contents

[1]用预编译指令符避免多文件工程中重复定义的问题 [文档代码下载]:《from0toknow》_c++_pdf.rar
[2]关于MFC画图的一些总结,MFC (Draw) [文档代码下载]:《from0toknow》_Draw_pdf_code.rar
[3]Text文本类的部分操作 [文档代码下载]:《from0toknow》_Text_pdf_code.rar
[4]Menu-静态菜单和动态菜单 [文档代码下载]:《from0toknow》_Menu_pdf_code.rar
[5]对话框资源的各种属性方法的使用 [文档代码下载]:《from0toknow》_Dialog_pdf_code.rar
[6] [文档代码下载]:《from0toknow》_DialogControl_pdf_code.rar
[7]窗体类型的各种变换 [文档代码下载]:《from0toknow》_WndStyle_pdf_code.rar
[8]图像Graphic的相关处理 [文档代码下载]:《from0toknow》_Graphic_pdf_code.rar
[9]窗体重绘,图像更新 [文档代码下载]:《from0toknow》_WndUpdatePicRedraw_pdf_code.rar
[10]文件读写和注册表读写 [文档代码下载]:《from0toknow》_File_pdf_code.rar
[11]文档序列化(理论+实际) [文档代码下载]:《from0toknow》_DocSerial_pdf_code.rar
[12]ActiveX的设计 [文档代码下载]:《from0toknow》_ActiveX_pdf_code.rar
[13]动态链接库的设计(DLL) [文档代码下载]:《from0toknow》_dll_pdf_code.rar
[14]Hooks(钩子)_用于监听消息的方法 [文档代码下载]:《from0toknow》_Hooks_pdf_code.rar

附录:
1、开发工具
Microsoft Visual Studio.NET 2003(VC++7.1)
2、预备知识
基本的C语言知识,类的概念,懂得类的功能和用途。有清晰的思路和敏捷的大脑思维能力和方式。
3、答疑回访
在学习中有任何疑问欢迎发邮件至SupportEmail:mymsdn@163.com或者登陆Blog:http://volnet.cnblogs.com进入VC++/C++相关专题内在相应文章的版面内评论(提问)。

- 作者: volnet 2006年08月22日, 星期二 16:53  回复(1) |  引用(0) 加入博采

Hooks(钩子)_用于监听消息的方法

Hooks(钩子)_用于监听消息的方法

Hooks(钩子)

一、MSDN:索引Hooks

A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.

翻译:一个钩子是一个指向消息句柄机制的指针,它可以确保一个消息到达窗体过程,监视消息队列系统安装子程序。(翻译有点拗口,意思呢就是说钩子程序是一个监视消息队列的程序,可以确保子程序在安装)

Windows消息机制:(图解)

应用程序

窗口过程

消息队列

操作系统

Windows消息机制:(图解)

 

1

2

4

3

Overviews

About Hooks

This topic discusses how hooks should be used.翻译:这个主题讨论钩子的使用

Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.

翻译:因为钩子程序必须对处理系统的每个消息进行处理,增加了系统进程,因此将导致减慢系统。只有当你真正需要的时候才去安装钩子程序,并及时地移除它们。

Hook Chains   钩子链

[由于具体的内容相对较长请自行查阅]主要意思就是说,系统维护一条钩子链,所有的消息都要经过相关钩子过程的处理,比如说WH_MOUSE Hook,系统捕获鼠标消息,并进行相应处理。

Hook Procedures

To take advantage of a particular type of hook, the developer provides a hook procedure and uses the SetWindowsHookEx function to install it into the chain associated with the hook. A hook procedure must have the following syntax:

翻译:利用特殊的钩子类,开发者提供一个钩子过程并使用SetWindowsHookEx函数,来把钩子和钩子链关联起来(将它们加入钩子链),一个钩子过程必须有下面的语法。

LRESULT CALLBACK HookProc(

  int nCode,

  WPARAM wParam,

  LPARAM lParam

);

SetWindowsHookEx Function


The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.

Syntax

翻译:SetWindowsHookEx函数将一个应用程序定义过程添加进钩子链。你添加的钩子链将监视系统中确定类型的事件。这些事件像调用进程一样,关联一个进程或者所有运行于当前桌面的进程。

HHOOK SetWindowsHookEx(      
    int idHook,

    HOOKPROC lpfn,

    HINSTANCE hMod,

    DWORD dwThreadId

);

Return ValueSetWindowsHookEx

If the function succeeds, the return value is the handle to the hook procedure.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

翻译:如果这个函数调用成功,将返回一个钩子句柄。如果调用失败,返回值是NULL。如果想获得失败信息,就调用GetLastError

……更多信息请查看MSDN

二、实际操作

InnerHook

1、新建MFC对话框程序

BOOL CInnerHookDlg::OnInitDialog()中添加钩子过程

     g_hWnd=m_hWnd;

     g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());   //安装鼠标钩子

     g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId());   //安装键盘钩子并定义:

// CInnerHookDlg 消息处理程序

HHOOK g_hKeyboard=NULL;

HHOOK g_hMouse;

HWND g_hWnd=NULL;

 

LRESULT CALLBACK MouseProc(

  int nCode,     

  WPARAM wParam,

  LPARAM lParam  

)

{

     return 1;

}

 

LRESULT CALLBACK KeyboardProc(

  int code,    

  WPARAM wParam, 

  LPARAM lParam 

)

{

     //if(VK_SPACE==wParam || VK_RETURN==wParam)    //空格或者回车

     /*if(VK_F4==wParam && (1==(lParam>>29 & 1)))   //ALT+F4

         return 1; //表示已经处理过该消息了//屏蔽消息

     else

         return CallNextHookEx(g_hKeyboard,code,wParam,lParam);  //将消息传递给下一个Hooks函数

     */  

     if(VK_F2==wParam)

     {

         ::SendMessage(g_hWnd,WM_CLOSE,0,0);  //发送消息将其窗口关闭

         UnhookWindowsHookEx(g_hKeyboard);    //移除钩子过程

         UnhookWindowsHookEx(g_hMouse)   //移除钩子过程

     }

     return 1;

}

2、  屏蔽所有当前正在运行的进程/线程的钩子程序

需要将钩子代码放置动态链接库才可生效

SetWindowsHookEx

获得动态链接库的句柄可以使DllMain也可以用GetModuleHandle("HookDll")

HINSTANCE g_hInst;

BOOL WINAPI DllMain(

  HINSTANCE hinstDLL,  // handle to the DLL module

  DWORD fdwReason,     // reason for calling function

  LPVOID lpvReserved   // reserved

)

{

     g_hInst=hinstDLL;

}

 

GetModuleHandle("HookDll")  -> [等价写法:]GetModuleHandle("HookDll.dll")

 

3、  如果要获得调用窗体的句柄,最巧妙的方法是直接让调用程序把自己的句柄传递进来。

区别:void SetHook() void SetHook(HWND hwnd)

4、  将窗口设置为最顶层窗口SetWindowPos(wndTopMost,……)

配合其他的参数,可以将窗口设置为顶层,并占据整个窗口,并将其他窗口挡住!

5、  动态链接库中定义的全局变量,由各个程序分别调用,但是并不是当前窗口(标题栏不是蓝色而是灰色),这时候,即使使用程序中设置的F2来关闭窗口,也是无法完成任务的。那么要让传入的hWnd能够变成大家共享的一个,也就是任何进程都可以调用的话。那么可以使用写入式拷贝

利用dumpbin –headers HookDll.dll可以查看“节”的信息,我们可以创建一个节来放置特殊的变量。比如定义一个可写入,读取,并共享的节,并将hWnd放置在里面

HookDll.cpp文件中定义节

//定义一个新节

#pragma data_seg("MySec")   //()内的字符数量必须小于等于8

HWND g_hWnd=NULL;

#pragma data_seg()

//设置节的属性

#pragma    //设置节的属性RWS=ReadWriteShared的缩写!

另外一种设置节的属性的方法是在.def文件中

SEGMENTS

MySec    READ WRITE SHARED

详细用法:MSDNpragma directives (#pragma)

6、  注意事项:使用钩子要十分注意,比如刚才一定要给自己留个“退出”的方法,否则后果十分严重!

- 作者: volnet 2006年08月22日, 星期二 12:09  回复(0) |  引用(0) 加入博采

动态链接库的设计(DLL)

动态链接库的设计(DLL)

您好,由于文章中带有一些截图,因此我把文章以及原代码一起打包了,需要的朋友可以下载后用AdobeReader进行查看。
代码是基于Visual Studio.NET2003 VC++编写的,例子都很简单,旨在阐明原理。如果对您的阅读带来的极大的不便,请跟帖提出,如果群众呼声过大的话我会考虑重新编排,直接在这里发表的。
顺便请问,有什么办法可以使保存在WORD里面的图片,直接粘贴过来后,图片也能随着文字一起发表呢?

下载文件:
《from0toknow》_dll_pdf_code.rar

动态链接库

一、相关概念

1、动态链接库

l         自从微软推出第一个版本的Windows操作系统以来,动态链接库(DLL)一直是Windows操作系统的基础。

l         动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其它DLL调用来完成某项工作的函数。只有在其它模块调用动态链接库中的函数时,它才发挥作用。

l         Windows API中的所有函数都包含在DLL中。其中有3个最重要的DLLKernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

2、静态库和动态库

l         静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)

l         在使用动态库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。

3、使用动态链接库的好处

l         可以采用多种编程语言来编写。

l         增强产品的功能。

l         提供二次开发的平台。

l         简化项目管理。

l         可以节省磁盘空间和内存。

l         有助于资源的共享。

l         有助于实现应用程序的本地化。

4、动态链接库被多个进程访问

5、动态链接库加载的两种方式

l         隐式链接

l         显示加载

二、实际操作

可以通过MSDN:索引DLL查到相关内容

创建一个新的项目选WIN32控制台应用程序

在应用程序设置中选择DLL,空项目

添加C++源文件(*.cpp),命名为DLL_1.cpp

添加代码:

int add(int a,int b)

{    return a+b;   }

int sub(int a,int b)

{    return a-b;   }

利用VS工具目录下的命令提示符进入相应DLL所在目录,然后用dumpbin命令查看导出函数:

例如:E:\mytest\sx\Lesson19\DLL_1\Debug>dumpbin -exports dll_1.dll

但是仅有以上代码是不会有导出函数的,总的来说有2种方法来导出函数

l         __declspec(dllexport) 关键字

l         .DEF 文件

使用 .DEF 文件的优缺点

.DEF 文件中导出函数使您得以控制导出序号。当将附加的导出函数添加到 DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。当您进行此操作时,使用隐式链接的应用程序不必与包含新函数的新导入库重新链接。这非常重要,例如,在设计将由许多应用程序使用的第三方 DLL 时。可以通过添加附加的功能不断地增强 DLL,同时确保现有应用程序继续正常使用新的 DLLMFC DLL 是用 .DEF 文件生成的。

使用 .DEF 文件的另一个优点是:可以使用 NONAME 属性导出函数,该属性仅将序号放到 DLL 的导出表中。对具有大量导出函数的 DLL,使用 NONAME 属性可以减小 DLL 文件的大小。有关编写模块定义语句的信息,请参见模块定义语句的规则。有关序号导出的更多信息,请参见按序号而不是按名称从 DLL 导出函数。

使用 .DEF 文件的主要缺点是:在 C++ 文件中导出函数时,需要将修饰名放到 .DEF 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。

如果需要将修饰名放到 .DEF 文件中,可以通过使用 Dumpbin 工具或通过使用 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .DEF 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL .DEF 文件中的导出名相匹配。

使用 __declspec(dllexport) 的优缺点

使用 __declspec(dllexport) 非常方便,因为不需要考虑维护 .DEF 文件和获取导出函数的修饰名。但是,无法控制编译器生成的导出序号。此方法适合某些情况,例如,在设计要与控制的应用程序一起使用的 DLL 时;如果用新导出重新生成 DLL,则还需要重新生成应用程序。

下面分别介绍两种方法的使用:

l         __declspec(dllexport) 关键字

在每个需要导出的函数前添加该关键字:

_declspec(dllexport) int add(int a,int b)

{    return a+b;   }

_declspec(dllexport) int sub(int a,int b)

{    return a-b;   }

之后能在dumpbin查看文件中发现如下字段:

    ordinal     hint        RVA             name

          1         0       000112A8        ?add@@YAHHH@Z

          2         1       00011663        ?sub@@YAHHH@Z

其中hint:提示码  RVA:列出地址值

注意到先前的函数名add变为?add@@YAHHH@Z,因此这样的函数只能在由相同编译器编译的程序中进行调用,而其他程序调用则会出错。之所以篡改名字是为了C++的函数重载!(如何调用请查看调用方法1

l         .DEF 文件(模块定义文件)[推荐,适合于DELPHI等项目使用C++DLL]

dll工程项目下,添加新项->模块定义文件,添加一个新的文件DLL_2.def

在其中写:其中add,sub为要导出的函数的名称

LIBRARY  DLL_2

 

EXPORTS  //表示要导出那些函数

add

sub

由此生成的文件不会改变导出函数名

 

从应用程序调用动态链接库的方法

调用方法1:隐式链接

1、  DLL文件夹DEBUG下生成的DLL文件,.lib文件拷贝到测试的应用程序(DLL_test)的工程目录下

2、  在应用程序的开发界面中,项目->DLL_test的属性,在其属性页中,链接器->输入->附加的依赖项中添加刚才生成的DLL_1.lib导入文件。

3、  在调用函数前,使用extern关键字声明调用的函数是来自外部的程序或者使用_declspec(dllimport)关键字。如本程序中:

//方法1:告诉编译器我们所引用的符号是外部程序  //效率相对方法2

extern int add(int a,int b);

extern int sub(int a,int b);

//方法2:告诉编译器我们所引用的符号是从.lib导入文件中导入的动态链接库程序  //效率相对高

_declspec(dllimport) int add(int a,int b);

_declspec(dllimport) int sub(int a,int b);

//方法3:在设计DLL_1的时候就把方法2中的代码添加到头文件,然后在当前文件的#include中包含#include "..\DLL_1\DLL_1.h"   //好处就是如果DLL文件和应用程序不是同一人编写的时候可以更好地调用!

#include "..\DLL_1\DLL_1.h"

//方法4:宏定义,这样可以用一个宏定义来完成_declspec(dllimport) _declspec(dllemport),而且可以使该宏不仅可以给动态接库的开发方使用,也可以给动态链接库的调用方来使用

//DLL_1.h(DLL设计文件)

#ifdef DLL_API

#else

#define DLL_API    _declspec(dllimport)

#endif

 

DLL_API int add(int a,int b);

DLL_API int sub(int a,int b);

//DLL_1.cpp(DLL设计文件)

#define DLL_API _declspec(dllexport)

#include "DLL_1.h"

DLL_API int add(int a,int b)

{   

     return a+b;  

}

DLL_API int sub(int a,int b)

{   

     return a-b;  

}

4、  然后可正常使用程序。

类的导出:

同样利用调用方法1中方法4DLL_API宏来定义类。

例子:

DLL_1.h

class DLL_API Point

{

public:

     void output(int x,int y);

};

DLL_1.cpp

#include <stdio.h>

#include <Windows.h>

……

void Point::output(int x,int y)

{

     HWND hWnd = GetForegroundWindow();

     HDC hDC = GetDC(hWnd);

     char buf[20];

     memset(buf,0,20);

     sprintf(buf,"x=%d,y=%d",x,y);

     TextOut(hDC,0,0,buf,strlen(buf));

ReleaseDC(hWnd,hDC);

}

DLL_test.cpp添加一按钮控件响应

void CDLL_testDlg::OnBnClickedBtnOutput()

{

     // TODO: 在此添加控件通知处理程序代码

     Point point;

     point.output(5,3);

}

对于class DLL_API Point则导出整个类,如果只要求导出类中的一个函数的话,那么只需要在相应成员函数前加DLL_API,

class Point

{

public:

     DLL_API void output(int x,int y);

};

避免名字改编

我们通过C++编写的DLL,会自动改编名字,比如add->?add@@YAHHH@Z,如何才能不改变呢?

方法:

将之前#define DLL_API _declspec(dllexport)改变为:

#define DLL_API extern "C" _declspec(dllexport)

该方法的缺陷:只能对全局函数,而不能正确地导出类的成员函数。如果我们导出的函数的调用约定改变,即使我们用了extern "C"做声明,也会发生改编。

比如:(这种情况一般为DELPHI等程序调用C++写的DLL时)(这时的名字仍然会发生改编)

#define DLL_API extern "C" _declspec(dllexport)

#include "DLL_1.h"

DLL_API int _stdcall add(int a,int b)

{   

     return a+b;  

}

DLL_API int _stdcall sub(int a,int b)

{   

     return a-b;  

}

名字:

    ordinal hint RVA      name

          1    0 000114B5 _add@8

          2    1 00011767 _sub@8

其中:@8中的8代表后面所带参数的字节数

 

 

 

调用方法2:动态调用

     //动态调用动态链接库

     HINSTANCE hInst;

     hInst=LoadLibrary("Dll_2.dll");

     typedef int (*ADDPROC)(int a,int b);

//typedef int (_stdcall *ADDPROC)(int a,int b);    //如果在DLL设计过程中是用_stdcall的,那这里也要用_stdcall,否则系统将报错!

     ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");  //"add"这里为用dumpbin exports dll_2.dll中显示的名字,如果为?add@@YAHHH@Z,那么在这里也应该写ADDPROC Add=(ADDPROC)GetProcAddress(hInst," ?add@@YAHHH@Z ");

     //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1)); //利用序号来调用

     if(!Add)

     {

         MessageBox("获取函数地址失败!");

         return;

     }

     CString str;

     str.Format("5+3=%d",Add(5,3));

     MessageBox(str);

     FreeLibrary(hInst);

 

 

DllMain,一个可选的动态链接库主入口函数

BOOL WINAPI DllMain(

  HINSTANCE hinstDLL,

  DWORD fdwReason,

  LPVOID lpvReserved

);

但在这个函数中不要做过于复杂的调用,因为,在加载的时候,某些核心程序并未加载,因此将出现错误。

下载文件:
《from0toknow》_dll_pdf_code.rar

- 作者: volnet 2006年08月21日, 星期一 12:09  回复(1) |  引用(0) 加入博采

ActiveX控件(下)

下载文件:

《from0toknow》_ActiveX_pdf_code.rar

测试:利用ActiveX控件测试容器来测试,添加该控件,测试自定义属性要通过“控件”->“调用方法”

出现如下对话框:

 

通过设置参数值“设置值”,再“调用”,可观察到时间的变化!

但就此设置,并未添加“属性页”(特性),因此在资源视图中修改IDD_PROPPAGE_AX_CLOCK

为如下所示:

关联控件与相关属性:

自定义 DoDataExchange 函数(MSDN

属性页 DoDataExchange 函数使您得以将属性页值与控件中的实际属性值链接。若要建立链接,必须将适当的属性页字段映射到它们各自的控件属性。

使用属性页 DDP_ 函数实现这些映射。DDP_ 函数的运行与标准 MFC 对话框中使用的 DDX_ 函数相似,但有一处例外。除对成员变量的引用外,DDP_ 函数还使用控件属性的名称。下面是属性页的 DoDataExchange 函数中的一个典型项。

DDP_Text(pDX, IDC_CAPTION, m_caption, _T("Caption"));

此函数使用 DDP_TEXT 函数将属性页的 m_caption 成员变量与 Caption 相关联。

插入属性页控件后,需要使用 DDP_Text 函数在属性页控件 IDC_CAPTION 与实际控件属性 Caption 之间建立链接(如前所述)。

其他对话框控件类型(如复选框、单选按钮和列表框)也可以使用 DDP 函数。下表列出了完整的属性页 DDP_ 函数集及各函数的作用:

属性页函数

函数名

函数的作用

DDP_CBIndex

链接组合框中选定字符串的索引与控件属性。

DDP_CBString

链接组合框中的选定字符串与控件属性。所选字符串可以以与属性值相同的字母开始,但不必完全与其匹配。

DDP_CBStringExact

链接组合框中的选定字符串与控件属性。所选字符串和属性的字符串值必须完全匹配。

DDP_Check

链接复选框与控件属性。

DDP_LBIndex

链接列表框中选定字符串的索引与控件属性。

DDP_LBString

链接列表框中的选定字符串与控件属性。所选字符串可以以与属性值相同的字母开始,但不必完全与其匹配。

DDP_LBStringExact

链接列表框中的选定字符串与控件属性。所选字符串和属性的字符串值必须完全匹配。

DDP_Radio

链接单选按钮与控件属性。

DDP_Text

链接文本与控件属性。

具体操作:

为属性页资源中的编辑框添加成员变量:

在属性页的AX_ClockPropPage.cppDoDataExchange(CDataExchange* pDX)

void CAX_ClockPropPage::DoDataExchange(CDataExchange* pDX)

{

     DDP_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval, _T("Interval"));   //将两个内外属性关联起来

     DDX_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval); //关联成员变量的时候系统自动添加的用于:标准MFC从控件传递参数给变量

DDP_PostProcessing(pDX);

}

但是此时直接打开属性的时候出现的是一个很大的负数,那是因为我们没有设置初始值。利用以下方法可以使其拥有默认值,而且还可以将用户设置的属性得以保存,不添加以下方法,每次关闭控件后再打开,其设置后的值又无效了。

具体方法:

void CAX_ClockCtrl::DoPropExchange(CPropExchange* pPX)为每个持久的自定义属性调用 PX_ 函数

void CAX_ClockCtrl::DoPropExchange(CPropExchange* pPX)

{

     ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

     COleControl::DoPropExchange(pPX);

     // TODO: 为每个持久的自定义属性调用 PX_ 函数。

     PX_Short(pPX,"Interval",m_Interval,1000);

     //PX_Short((pPx,"Interval",m_Interval);   //不设置初始值可以调用该方法

         /*

         BOOL PX_Short(CPropExchange* pPX,LPCTSTR pszPropName,short& sValue,short sDefault);

         pszPropName :被改变的属性的名字,此例为:"Interval"

         sValue :属性存储的变量(类的一个成员变量),此例为:m_Interval

     &nsp;   sDefault :属性的默认值,此例为:1000

         */

}

*         增加方法

在类中添加该方法的响应函数,方法是给外部调用的,当控件嵌入容器后,容器就可以像添加一个按钮一样有很多预先的方法,而我们的Hello具体的响应函数将响应容器方的调用。

比如:在Hello中添加MessageBox(“HELLO”);

那么在外部调用方法比如:Clock1.Hello();一旦这个方法调用,就将响应MessageBox(“HELLO”);

*         增加事件

控件的事件响应是由容器来实现的,而不是控件,控件只负责发送事件!!!

l         增加系统默认(常用)事件

启动“添加事件向导”

1、 在“类视图”中,右击 ActiveX 控件类以打开快捷菜单。

2、 在快捷菜单中,单击“添加”,然后单击“添加事件”。

3、 此操作将打开“添加事件向导”。在“事件名称”下拉列表中,选择“Click”。

4、 单击“完成”。

类视图发生如下变化(新增)

[default, source] dispinterface _DAX_ClockEvents;

原接口(source):控件将用这个接口来发送通知事件,其不是控件本身的方法,实现该事件的方法,不是由控件本身完成,而是由容器来完成。

测试:在VB中添加click的事件来响应当该事件发送的时候,响应相关代码。

l         增加自定义事件

测试:(先在控件中相关位置调用一个发送事件的方法)

1、 OnDraw()

CTime time=CTime::GetCurrentTime();

     if(0 == time.GetSecond())   //当秒数为0的时候

     {

         NewMinute();  //发送事件

     }

     CString strTime;

2、观察当时钟的秒数为0的时候出现相应事件!

*         获取环境属性来改变控件的状态

void CAX_ClockCtrl::OnTimer(UINT nIDEvent)

{

     if(AmbientUserMode())  //可以使用户在开发环境的时候时间不走动,在使用的时候时间走动

         InvalidateControl();   //以调用OnDraw函数来显示新的时间

     COleControl::OnTimer(nIDEvent);

}

二、在VC中添加控件

基于对话框的话,可以右键“插入ActiveX控件”,或者通过自定义工具箱也可以添加!

下载文件:

《from0toknow》_ActiveX_pdf_code.rar

大家也可以使用http://msdn.microsoft.com/en-us/library/599w5e7x.aspx中提到的ATL的方式实现

 

 

- 作者: volnet 2006年08月21日, 星期一 12:07  回复(0) |  引用(0) 加入博采

ActiveX的设计(上)

ActiveX的设计

您好,由于文章中带有一些截图,因此我把文章以及原代码一起打包了,需要的朋友可以下载后用AdobeReader进行查看。
代码是基于Visual Studio.NET2003 VC++编写的,例子都很简单,旨在阐明原理。如果对您的阅读带来的极大的不便,请跟帖提出,如果群众呼声过大的话我会考虑重新编排,直接在这里发表的。
顺便请问,有什么办法可以使保存在WORD里面的图片,直接粘贴过来后,图片也能随着文字一起发表呢?

下载文件:
《from0toknow》_ActiveX_pdf_code.rar

ActiveX控件

一、以一个时钟控件为例设计一个控件

1、 新建项目->MFC->ActiveX控件进入一个控件编辑的开发环境

2、 由于要设计时钟控件。因此

*         基本时钟设置

l         创建一个计时器OnCreate

l         获取当前时间,并在计时器触发的时候进行显示OnDraw

l         在触发函数中添加代码OnTimer

*         增加属性页

l         增加系统默认属性页(以改变控件颜色为例)

l         增加自定义属性页(以改变时间跳跃间隔为例)

*         增加方法

*         增加事件

l         增加系统默认事件

l         增加自定义事件

*         获取环境属性来改变控件的状态

3、 具体代码实现与VS使用方法:

*         基本时钟设置

l         创建一个计时器OnCreate

int CAX_ClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

     if (COleControl::OnCreate(lpCreateStruct) == -1)

         return -1;

     SetTimer(1,1000,NULL); //创建控件的时候添加一个计时器

     return 0;

}

l         获取当前时间,并在计时器触发的时候进行显示OnDraw

void CAX_ClockCtrl::OnDraw(

              CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

     if (!pdc)

         return;

     CTime time=CTime::GetCurrentTime();

     CString strTime;

     strTime=time.Format("%H:%M:%S");

     pdc->TextOut(0,0,strTime);

}

l         在触发函数中添加代码OnTimer

void CAX_ClockCtrl::OnTimer(UINT nIDEvent)

{

     InvalidateControl();   //以调用OnDraw函数来显示新的时间

     COleControl::OnTimer(nIDEvent);

}

*         增加属性页

l         增加系统默认属性页(以改变控件颜色为例)(方法:在“类视图”中,右击要向其添加属性的接口名称。)

类视图:

添加属性向导:(属性名选择由系统提供的属性,然后实现类型为“常用”,按“完成”)(此处我们添加BackColorForeColor

但是要使这两个属性起作用,还需要在程序中添加代码

GetBackColor():返回一个指定的当前颜色的值,可以从容器中获得,返回值是OLE_COLOR

RemarkThe return value specifies the current background color as a OLE_COLOR value, if successful. This value can be translated to a COLORREF value with a call to TranslateColor.

TranslateColor():将OLE_COLOR转换为COLORREF

测试:在VB中添加控件(工具->添加/移除工具箱项->COM组件->AX_Clock Control->在工具箱中找到相应控件,然后将其向普通控件一样添加,然后通过“属性”来进行设置)

但此时该ActiveX控件的属性页并没有设计,通过右键点刚才添加的ActiveX控件->特性,仍然只能显示默认的属性,现在通过以下设置添加刚刚修改的颜色属性到属性页中去。

方法:在AX_ClockCtrl.cpp中找到

// 属性页

// TODO: 按需要添加更多属性页。请记住增加计数!

BEGIN_PROPPAGEIDS(CAX_ClockCtrl, 1)

     PROPPAGEID(CAX_ClockPropPage::guid)

END_PROPPAGEIDS(CAX_ClockCtrl)

这个是系统自动添加的代码,仿照代码可以添加更多的属性页,但记住注释中“记住增加计数!”

因此修改这段代码即可![MFC 提供了三个与 ActiveX 控件一起使用的常用属性页:CLSID_CColorPropPageCLSID_CFontPropPage CLSID_CPicturePropPage。这些页分别显示常用颜色、字体和图片属性的用户界面。]

BEGIN_PROPPAGEIDS(CAX_ClockCtrl, 2)

     PROPPAGEID(CAX_ClockPropPage::guid)

     PROPPAGEID(CLSID_CColorPropPage);

END_PROPPAGEIDS(CAX_ClockCtrl)

此后就可以在应用该控件时候右键控件的时候显示:

l         增加自定义属性页(以改变时间跳跃间隔为例)

同样在“添加属性向导”中添加,但此时属性名为自己定义的“Interval

OnIntervalChanged()中添加如下代码:

void CAX_ClockCtrl::OnIntervalChanged(void)

{

     AFX_MANAGE_STATE(AfxGetStaticModuleState());

     // TODO: 在此添加属性处理程序代码

     if(m_Interval<1000 || m_Interval>6000)

     {

         m_Interval=1000;

     }

     else

     {

         m_Interval=m_Interval/1000*1000; //取整,比如用户输入2345,则2345/1000*1000=>2345/1000=22*1000=2000,则实现整秒数

         KillTimer(1); //将先前的Timer1删除掉,因为要重新设置新的跳跃间隔

         SetTimer(1,m_Interval,NULL);

         BoundPropertyChanged(0x1);  //将属性页的值与容器中相关控件属性的显示表示为同步!系统自带的不需要这步!(其中0x1为控件ID号)

         //可以从AX_Clock.idl中查到这句[id(1), helpstring("属性 Interval")] SHORT Interval;//用以找到ID

     }

     SetModifiedFlag();

}

BoundPropertyChanged(0x1);    //ID号的查找可以依靠双击类视图相应接口下的相应参数:



下载文件:

 

 

 

《from0toknow》_ActiveX_pdf_code.rar

- 作者: volnet 2006年08月21日, 星期一 12:05  回复(0) |  引用(0) 加入博采

文档序列化(理论+实际) (下)

下面直接在 Doc 类中使用 CObArray 对象(取消之前定义在 View 类中的该类对象)

Doc 类中定义 CObArray m_obArray;

存图形数据的代码修改为:

     CGraphicDoc *pDoc=GetDocument();// 通过视类提供的GetDocument()方法来获得Doc类指针

     pDoc->m_obArray.Add(pGraph);

修改其他位置的 m_obArray

void CGraphicSerialView::OnDraw(CDC* pDC)

{

     ……

     nCount=pDoc->m_obArray.GetSize();

     for(int i=0;i

     {

          ((CGraph*)pDoc->m_obArray.GetAt(i))->Draw(pDC);   //Doc类中定义m_obArray的情况

     }

}

void CGraphicSerialDoc::Serialize(CArchive& ar)

{

……

     m_obArray.Serialize(ar);     //Doc 类中定义m_obArray的情况

}



当我们新建和打开文档的时候,我们之前的文档对象并没有被销毁(系统利用OnNewDocument方法所新建的对象)之前在堆上建立了文档对象。此时应删除文档对象。

在文档类中重载函数void CGraphicSerialDoc::DeleteContents()来删除文档对象,以避免内存泄露。

void CGraphicSerialDoc::DeleteContents()

{

     int nCount;

     nCount=m_obArray.GetSize();

     /*for(int i=0;i

     {

         delete m_obArray.GetAt(i);

         //m_obArray.RemoveAt(i);

     }

     m_obArray.RemoveAll();*/

     while(nCount--)

     {

         delete m_obArray.GetAt(nCount);

         m_obArray.RemoveAt(nCount);

     }

     CDocument::DeleteContents();

}

下面是关于内存泄露的在此的一段论述。(此文同时发布于)

http://www.cppblog.com/mymsdn/archive/2006/08/16/11266.html

关于内存泄露的问题(解决+剖析)

首先先阐明这篇随笔的意图,只在告诉读者,内存泄露的神不知鬼不觉,希望能引起大家的注意。
一段代码的意思如何正确表达,才能不造成内存泄露呢?很多朋友经常泄露了内存但却查找不到原因。当然在CLI/C++中利用托管对象堆上的垃圾收集器是可以更好地避免这一点。但是在更早的版本中,程序员有必要去手动删除这些相关资源。否则将在程序关闭的时候出现一些错误。
MFC
现在我们去重载一个虚函数virtualvoidDeleteContents();用来在销毁文档数据前调用框架删除一些文档类的数据,(MSDNCalled by the framework to delete the document's data without destroying the CDocument object itself.
先批评一段代码:

1 void  CGraphicDoc::DeleteContents() 
2 {
3      for ( int  i = 0 ;i < m_obArray.GetSize();i ++ )
4      {
5         ……
6     }
7     CDocument::DeleteContents();
8 }

评价:这段代码看似简练,但是却很浪费资源,在第3行的for循环中,i当每次进行判断的时候将再次调用GetSize(),如果这个数据的量是个天文数字,那么这样的调用无疑是一种灾难。

优化:

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     for(int i=0;i 6      {
 7         ……
 8     }
 9     CDocument::DeleteContents();
10 }


填写for循环内的语句。这里的任务:删除之前利用CObArray : m_obArray对象保存的一个指针所指向的对象,以及指针本身。因此(以下提供几种常见的错误代码)
代码A

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     for(int i=0;i 6      {
 7         delete m_obArray.GetAt(i);   //删除对象指针所指向的对象
 8         m_obArray.RemoveAt(i);   //删除对应的指针本身
 9     }
10     CDocument::DeleteContents();
11 }

代码B

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     for(int i=0;i 6      {
 7         delete m_obArray.GetAt(i);
 8         m_obArray.RemoveAt(0);
 9     }
10     CDocument::DeleteContents();
11 }

代码A看起来似乎很符合常规思维,因此也很容易迷惑人。但是在程序运行的时候出现了错误。但是在MSDN中查找CObArray Class Members,查看RemoveAt,其中remarks中有这样一句:In the process, it shifts down all the elements above the removed element(s). It decrements the upper bound of the array but does not free memory. 它的意思就是假设你一共有3个数据 p[0]=ap[1]=bp[2]=c,当你删除第0个数据之后,数据将整体向前移动,变为p[0]=bp[1]=c。这样当你i=nCount-1的时候就根本没有这样的数让你删除,因为假设有那个时刻的话,数据的元素只有1个,而他的编号是0而不是nCount.因此出现了无法删除的现象。因此也就隐含了问题了。
因此有人突发奇想:如果我每次只删除第0个数据的话,那么是否就可以了呢?于是代码B诞生了。可是问题终究没能得到解决。因为假设有一组数据一共3个。删除了编号0的元素(delete语句),移除了该元素的指针,此时i=1,进入删除,又到了delete语句,这时候删除元素i=1这样的语句,这时实际上是删除了先前元素中的第二个元素,而不是第一个。而02中间的第1个元素则未被删除。又出现了隐含问题。其实只要将两个都改为0,每次都删除第一个就可以了。

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     for(int i=0;i 6      {
 7         delete m_obArray.GetAt(0);
 8         m_obArray.RemoveAt(0);
 9     }
10     CDocument::DeleteContents();
11 }

另外可以将程序倒写过来,避免RemoveAt对其进行重新整合队列做产生的不可预料的麻烦。

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     while(nCount--)
 6      {
 7         delete m_obArray.GetAt(nCount);
 8         m_obArray.RemoveAt(nCount);
 9     }
10     CDocument::DeleteContents();
11 }
12

或者仔细察看MSDN中还有一个函数叫RemoveAll()它是用来删除整个CObArray集合对象的。

 1 void CGraphicDoc::DeleteContents() 
 2 {
 3     int nCount;
 4     nCount=m_obArray.GetSize();
 5     for(int i=0;i 6      {
 7         delete m_obArray.GetAt(i);
 8     }
 9     m_obArray.RemoveAll();
10     CDocument::DeleteContents();
11 }

- 作者: volnet 2006年08月17日, 星期四 01:24  回复(0) |  引用(0) 加入博采

文档序列化(理论+实际) (中下)

View 类中定义 CObArray m_obArray;

下面按这个思路来完成:(在 C Graph 子类中完成重绘的画图功能)

Graph.cpp

#include "StdAfx.h"

#include ".\graph.h"

IMPLEMENT_SERIAL(CGraph, CObject, 1 )

CGraph::CGraph(void)

: m_ptOrigin(0)

, m_ptEnd(0)

, m_nDrawType(0)

{

}

CGraph::CGraph(CPoint m_ptOrigin,CPoint m_ptEnd,UINT m_nDrawType)

{

     this->m_ptOrigin=m_ptOrigin;

     this->m_ptEnd=m_ptEnd;

     this->m_nDrawType=m_nDrawType;

}

CGraph::~CGraph(void)

{

}

void CGraph::Serialize( CArchive& ar )

{

     // 继承基类的CObject

    CObject::Serialize( ar );

    if( ar.IsStoring() )

     {

        ar << m_ptOrigin << m_ptEnd << m_nDrawType ;

     }

     else

     {

         ar >> m_ptOrigin >> m_ptEnd >> m_nDrawType ;

     }

}

void CGraph::Draw(CDC* pDC)

{

     CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

     CBrush *pOldBrush=pDC->SelectObject(pBrush);

     switch(m_nDrawType)

     {

     case 1:

         pDC->SetPixel(m_ptEnd,RGB(0,0,0));

         break;

     case 2:

         pDC->MoveTo(m_ptOrigin);

         pDC->LineTo(m_ptEnd);

         break;

     case 3:

         pDC->Rectangle(CRect(m_ptOrigin,m_ptEnd));

         break;

     case 4:

         pDC->Ellipse(CRect(m_ptOrigin,m_ptEnd));

         break;

     }

     pDC->SelectObject(pOldBrush);

}

Graph..h

#pragma once

#include "atltypes.h"

class CGraph : publicCObject

{

public :

     DECLARE_SERIAL( CGraph )

     CGraph(void);

     CGraph::CGraph(CPoint m_ptOrigin,CPoint m_ptEnd,UINT m_nDrawType);

     void Serialize( CArchive& ar );

     ~CGraph(void);

     CPoint m_ptOrigin;

     CPoint m_ptEnd;

     UINT m_nDrawType;

     void Draw(CDC* pDC);

};

View 类中添加画图功能

void CGraphicSerialView::OnDot()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=1;

}

void CGraphicSerialView::OnLine()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=2;

}

void CGraphicSerialView::OnRectangle()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=3;

}

void CGraphicSerialView::OnEllipse()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=4;

}

void CGraphicSerialView::OnLButtonDown(UINT nFlags, CPoint point)

{

     // TODO: 在此添加消息处理程序代码和/或调用默认值

     m_ptOrigin = point;

     CView::OnLButtonDown(nFlags, point);

}

void CGraphicSerialView::OnLButtonUp(UINT nFlags, CPoint point)

{

     // TODO: 在此添加消息处理程序代码和/或调用默认值

     CClientDC dc(this);

     CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));     // 选择一个透明画刷

     dc.SelectObject(pBrush);

     switch(m_nDrawType)

     {

     case 1:

         dc.SetPixel(point,RGB(0,0,0));

         break;

     case 2:

         dc.MoveTo(m_ptOrigin);

         dc.LineTo(point);

         break;

     case 3:

         dc.Rectangle(m_ptOrigin.x,m_ptOrigin.y,point.x,point.y);

         break;

     case 4:

         dc.Ellipse(CRect(m_ptOrigin,point));

         break;

     default:

         break;

     }

     // 将图形信息保存起来

     CGraph *pGraph=new CGraph( m_ptOrigin , point , m_nDrawType );   // 创建一个pGraph指针指向一个存储图形信息的“图形”

     m_obArray.Add(pGraph);// 将一群这样的pGraph组织在一起放进m_obArray

     CView::OnLButtonUp(nFlags, point);

}

但是要保存和读取文件需要用到 Serialize

因此转到 Doc

void CGraphicSerialDoc::Serialize(CArchive& ar)

{

     POSITION pos=GetFirstViewPosition();// 获得第一个视类对象在对象列表中的位置

     CGraphicSerialView *pView=(CGraphicSerialView*)GetNextView(pos);// 找到当前视类对象的指针,由于这是单文档,因此只有一个视类对象

     if (ar.IsStoring())

     {

         // TODO: 在此添加存储代码

         int nCount=pView->m_obArray.GetSize();

         ar<   // 为了在读取文件的时候能够知道元素个数,因此在保存的时候把个数也存进去

         for(int i=0;i

         {

              ar<m_obArray.GetAt(i);

         }

     }

     else

     {

         // TODO: 在此添加加载代码

         int nCount;

         ar>>nCount;

         CGraph *pGraph;

         for(int i=0;

         {

              ar>>pGraph;

              pView->m_obArray.Add(pGraph);

         }

     }

}

但是读取数据的时候还需要重绘图像:转回View类,因为在View类加载的时候会自动调用OnDraw(),OnDraw()中添加如下语句。

     int nCount;

     nCount=m_obArray.GetSize();

     for(int i=0;i

     {

         ((CGraph*)m_obArray.GetAt(i))->Draw(pDC);

     }

基本上完成了通过serialize保存和打开文件。

serialize的好处:通过缓存来保存,当缓存区满了才进行一次读/写,因此提高了应用程序的效率

 

另外 Archive 对象是支持序列化的,因此在读写数据的时候即可以按以上方法在 Doc 类的 Serialize 来保存和打开数据。

因此修改 Doc 中的 Serialize

void CGraphicSerialDoc::Serialize(CArchive& ar)

     POSITION pos=GetFirstViewPosition();// 获得第一个视类对象在对象列表中的位置

     CGraphicSerialView *pView=(CGraphicSerialView*)GetNextView(pos);// 找到当前视类对象的指针,由于这是单文档,因此只有一个视类对象

     if (ar.IsStoring())

     {

         // TODO: 在此添加存储代码

     }

     else

     {

         // TODO: 在此添加加载代码

     }

     pView->m_obArray.Serialize(ar);  // 将这个数据传递给

}

其中CObArray类对象读取数据的方法:(以下是MFC源代码,无须用户添加)

void CObArray::Serialize(CArchive& ar)

{

     ASSERT_VALID(this);

     CObject::Serialize(ar);

     if (ar.IsStoring())

     {

         ar.WriteCount(m_nSize);

         for (INT_PTR i = 0; i < m_nSize; i++)

              ar << m_pData[i];

     }

     else

     {

         DWORD_PTR nOldSize = ar.ReadCount();

         SetSize(nOldSize);

         for (INT_PTR i = 0; i < m_nSize; i++)

              ar >> m_pData[i];

     }

}

 

 

 

- 作者: volnet 2006年08月17日, 星期四 01:18  回复(0) |  引用(0) 加入博采

文档序列化(理论+实际) (中上)

使用 CArchive 对象的“ << ”和“ >> ”操作符

CArchive 提供“ << ”和“ gt;> ”运算符,用于向文件中写入简单的数据类型和 CObjects 以及从文件中读取它们。

通过存档将对象存储在文件中

以下示例显示了如何通过存档将对象存储在文件中:

CArchive ar(&theFile, CArchive::store);

WORD wEmployeeID;

...

ar << wEmployeeID;

从先前存储在文件中的值加载对象

以下示例显示了如何从先前存储在文件中的值加载对象:

CArchive ar(&theFile, CArchive::load);

WORD wEmployeeID;

...

ar >> wEmployeeID;

通常,通过 CObject 派生类的 Serialize 函数中的存档将数据存储到文件中或从文件中加载数据,必须已用 DECLARE_SERIALIZE 宏来声明这些函数。将 CArchive 对象的引用传递到 Serialize 函数。调用 CArchive 对象的 IsLoading 函数以确定是否已调用 Serialize 函数来从文件中加载数据或将数据存储到文件中。

可序列化的 CObject 派生类的 Serialize 函数通常具有以下形式:

void CPerson::Serialize(CArchive& ar)

{

    CObject::Serialize(ar);

    if (ar.IsStoring())

    {

        // TODO:  add storing code here

    }

    else

    {

    // TODO:  add loading code here

    }

}

上面的代码模板与 AppWizard 为该文档(从 CDocument 派生的类)的 Serialize 函数所创建的代码模板完全相同。由于存储代码和加载代码总是并行,该代码模板有助于写的代码更容易复查,如下例中所示:

void CPerson:Serialize(CArchive& ar)

{

    if (ar.IsStoring())

    {

        ar << m_strName;

        ar << m_wAge;

    }

    else

    {

        ar >> m_strName;

        ar >> m_wAge;

    }

}

库将 CArchive 的“ << ”和“ >> ”运算符定义为第一操作数,将下列数据类型和类类型定义为第二操作数:

CObject*

SIZE CSize

float

WORD

CString

POINT CPoint

DWORD

BYTE

RECT CRect

double

LONG

CTime CTimeSpan

int

COleCurrency

COleVariant

COleDateTime

COleDateTimeSpan

 

注意    通过存档存储及加载 CObjects 需要额外注意。有关更多信息,请参见通过存档存储和加载 CObjects

CArchive 的“ << ”和“ >> ”运算符总是返回 CArchive 对象的引用,该引用为第一操作数。这使您可以链接运算符,如下所示:

BYTE bSomeByte;

WORD wSomeWord;

DWORD wSomeDoubleWord;

...

ar << bSomeByte << wSomeWord << wSomeDoubleWord;

通过存档存储及加载 CObject (见前)

下面用一个示例来解释这个问题。

目标:一个画图程序,通过保存打开按钮存取图片。方法:保存图片绘制信息。

按步骤:

l         创建可序列化的类 ->Graph.cpp+Graph.h

l         View 类中添加对控件的响应,实现画图功能,每次鼠标弹起的时候保存绘图信息

l         保存文件(通过 Doc Serialize 来保存数据)

l         打开文件(通过 Doc Serialize 来读取数据,并将其重绘)

- 作者: volnet 2006年08月17日, 星期四 01:15  回复(0) |  引用(0) 加入博采

文档序列化(理论+实际) (上)

文档序列化(理论+实际)

一、文档的基本特征

文档类文件是从CDocument继承而来的。

The CDocument class provides the basic functionality for user-defined document classes. A document represents the unit of data that the user typically opens with the File Open command and saves with the File Save command.

翻译:文档类提供用户自定义文档类的基本功能。一个文档的打开命令和保存命令是数据单元特征。

CDocument supports standard operations such as creating a document, loading it, and saving it. The framework manipulates documents using the interface defined by CDocument.

翻译:文档类支持标准操作有:创建文档,载入文档,和保存。框架界面使用由文档类定义的界面。

An application can support more than one type of document; for example, an application might support both spreadsheets and text documents. Each type of document has an associated document template; the document template specifies what resources (for example, menu, icon, or accelerator table) are used for that type of document. Each document contains a pointer to its associated CDocTemplate object.

翻译:一个应用程序可以支持多于一种类型的文档;例如,一个应用程序可能同时支持电子表格和普通文本文档。每一种类型的文档有一种关联文档模板;文档模板指定了(例如,菜单,图标,或者加速表格)使用这些类型的