博文

介绍Windows的窗口、消息、子类化和超类化(2008-09-21 14:15:00)

摘要:

眼见为实(2):介绍Windows的窗口、消息、子类化和超类化 介绍Windows的窗口、消息、子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词。为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程。子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法。不要把“子类化、超类化”与面向对象语言中的派生类、基类混淆起来。“子类化、超类化”中的“类”是指Windows的窗口类。 0 运行程序 希望读者在阅读本节前先看看"谈谈Windows程序中的字符编码"开头的第0节和附录0。第0节介绍了Windows系统的几个重要模块。附录0概述了Windows的启动过程,从上电到启动Explorer.exe。本节介绍的是运行程序时发生的事情。 0.1 程序的启动 当我们通过Explorer.exe运行一个程序时,Explorer.exe会调用CreateProcess函数请求系统为这个程序创建进程。当然,其它程序也可以调用CreateProcess函数创建进程。 系统在为进程分配内部资源,建立独立的地址空间后,会为进程创建一个主线程。我们可以把进程看作单位,把线程看作员工。进程拥有资源,但真正在 CPU上运行和调度的是线程。系统以挂起状态创建主线程,即主线程创建好,不会立即运行,而是等待系统调度。系统向Win32子系统的管理员 csrss.exe登记新创建的进程和线程。登记结束后,系统通知挂起的主线程可以运行,新程序才开始运行。 这时,在创建进程中CreateProcess函数返回;在被创建进程中,主线程在完成最后的初始化后进入程序的入口函数(Entry-point)。创建进程与被创建进程在各自的地址空间独立运行。这时,即使我们结束创建进程,也不会影响被创建进程。 0.2 程序的执行 可执行文件(PE文件)的文件头结构包含入口函数的地址。入口函数一般是Windows在运行时库中提供的,我们在编译时可以根据程序类型设定。在VC中编译、运行程序的小知识点讨论了Entry-point,读者可以参考。 入口函数前的过程可以被看作程序的装载过程。在装载时,系统已经做过全局和静态变量(在编译时可以确定地址)的初始化,有初......

阅读全文(5091) | 评论:0

Visual C++ MFC 简明教程(2008-05-31 17:27:00)

摘要:第一部分:MFC导论

  Visual C++ 不仅仅是一个编译器。它是一个全面的应用程序开发环境,使用它你充分利用具有面向对象特性的 C++ 来开发出专业级的 Windows 应用程序。为了能充分利用这些特性,你必须理解 C++ 程序设计语言。掌握了C++,你就必须掌握 Microsoft 基本类库 (MFC) 的层次结构。该层次 结构包容了 Windows API 中的用户界面部分,并使你能够很容易地以面向对象的方式建立 Windows 应用程序。这种层次结构适用于所有版本的 Windows 并彼此兼容。你用 MFC 所建立的代码是完全可移植的。
  该教程将向你介绍MFC的基本概念和术语以及事件驱动程序设计方法。在本节中,你将会输入、编译和运行一个简单的MFC程序。下一节中将向你详细解释这些代码。第三部分讨论了MFC控制和如何定制它们。第四部分将介绍消息映射,你将会处理MFC的事件。
  什么是MFC?
  如果你要建立一个 Windows 应用程序,应该如何下手?
  好的开端是从设计用户界面开始。首先,你要决定什么样的用户能使用该程序并根据需要来设置相应的用户界面对象。Windows 用户界面有一些标准的控制,如按钮、菜单、滚动条和列表等,这对那些 Windows 用户已经是很熟悉了。要记住的是,作为程序员必须选择一组控制并决定如何把它们安排到屏幕上。传统上,你需要在纸上做一下用户界面的草图,直到对各元素感到满意为止。这对于一些比较小的项目,以及一些大项目的早期原型阶段是可以的。
  下一步,是要实现代码。为任何 Windows 平台建立应用程序时,程序员都有两种选择:C 或 C++。 使用 C,程序员是在 Windows 应用程序界面 ( API ) 的水平上编写代码。该界面是由几百个 C 函数所组成,这些函数在Windows API 参考手册中都有介绍。对于Windows NT, API 被称为 “Win32 API”,以区别于其用于Windows 3.1的16位 API。
  Microsoft 也提供了 C++ 库,它位于任何 Windows API 之上,能够使程序员的工作更容易。它就是Microsoft基本类库 (MFC),该库的主要优点是效率高。它减少了大量在建立 Windows 程序时必须......

阅读全文(2565) | 评论:0

DLL编程(2008-04-22 09:20:00)

摘要:【转】http://blog.programfan.com/article.asp?id=33275 静态链接:每个应用程序使用函数库,必须拥有一份库的备份。多个应用程序运行时,内存中就有多份函数库代码的备份。 动态连接库:多个应用程序可以共享一份函数库的备份。 DLL的调用方式:即DLL的使用者在调用库中输出函数时,函数参数的压栈和出栈顺序和方法。 VC++支持四种方式: _cdecl调用方式: 也叫C调用方式,函数参数的压栈顺序是从右至左,参数的出栈方式由调用者完成,在调用DLL的函数的地方都应包含清空堆栈的代码,它是C/C++缺省的调用方式。 _stdcall标准调用方式:函数参数压栈顺序是从右至左,参数出栈工作由被调用者负责完成。系统将加在函数原型定义前的”WINAPI”宏翻译为适当的调用方式,对于Win32是_stdcall调用方式。 _fastcall:主要特点是调用速度快,被调用的函数参数传递不依靠堆栈,而是通过寄存器,但并不是对所有的参数传递均使用寄存器,往往只是用ECX和EDX传送前两个双字或比较小的参数,其余的参数传递仍然采用从右至左压栈方式,出栈工作由被调用的函数完成。 thiscall:前三种是关键字,可以加到函数前作修饰,thiscall不是关键字,因此程序中不能显式写入,这种方式仅应用于C++成员函数,this指C++中指向对象的指针,this存放在ECX寄存器中,参数从右至左压栈,出栈由被调用者完成。 DLL的入口函数 DllMain()函数负责完成DLL的初始化和解说DLL调用后的清理工作。当加载DLL时,如存在DllMain()函数则调用它。 MFC DLL   MFC DLL可以让我们的程序使用MFC库,它分为3类: Regular Dll with MFC Statically linked   (正规) 静态链接MFC库,在DLL工程中将包含工程中所需的MFC库代码的拷贝,因此,程序可以脱离MFC库使用。 Regular Dll using shared MFC Dll      (正规) 动态共享MFC库,工程必须在装有MFC库的机器上才能运行。 MFC正规DLL编写注意......

阅读全文(2760) | 评论:0

浅析mfc的Cstring类的copy-on-write(写时复制)功能 (2008-04-11 08:16:00)

摘要:CString类是vc中一个有关字符串处理的类,其中用到了很多好的技法,如写时复制 技法,内存管理技法等,处理字符串的效率是很高的。 这里仅谈谈copy-on-write技法的实现。 我以vc6中的mfc为例。 或许你曾写过如下的代码: CString  str_a = "abcd"; CString  str_b = str_a; 当你调试程序时,你可能发现str_a 和 str_b 这两个对象中的唯一的一个data member  m_pchDATA是指向同样的一个内存地址,即字符串abcd在堆中的首地址,也即字符串abcd在内存中只有一份copy,并不是两份数据。 当你再次写下如下代码时 str_a = “efg”; 会发现str_a的m_pchDATA指向了堆中的另一块内存efg,并没有将str_b中包含的数据覆盖掉。 可是CString的内存布局只有一个data member(因为它没有继承关系,所以没有虚函数,也就没有虚函数表指针)m_pchData,指向字符串数据,我们也没看见什么引用计数的变量呀。 那么CString是如何做到这一点(copy-on-write)的呢? 原来奥秘就在于有一个叫做CStringData的结构体,定义如下(见afx.h) struct CStringData {        long nRefs;             // reference count        int nDataLength;        // length of data (including terminator)        int nAllocLength;       // length of allocation  &nbs......

阅读全文(7448) | 评论:3

调试技巧之调用堆栈(2008-04-09 22:45:00)

摘要:简单介绍调试是程序开发者必备技巧。如果不会调试,自己写的程序一旦出问题,往往无从下手。本人总结10年使用VC经验,对调试技巧做一个粗浅的介绍。希望对大家有所帮助。今天简单的介绍介绍调用堆栈。调用堆栈在我的专栏的文章VC调试入门http://www.vckbase.com/document/viewdoc/?id=924提了一下,但是没有详细介绍。首先介绍一下什么叫调用堆栈:假设我们有几个函数,分别是function1,function2,function3,funtion4,且function1调用function2,function2调用function3,function3调用function4。在function4运行过程中,我们可以从线程当前堆栈中了解到调用他的那几个函数分别是谁。把函数的顺序关系看,function4、function3、function2、function1呈现出一种“堆栈”的特征,最后被调用的函数出现在最上方。因此称呼这种关系为调用堆栈(call stack)。当故障发生时,如果程序被中断,我们基本上只可以看到最后出错的函数。利用call stack,我们可以知道当出错函数被谁调用的时候出错。这样一层层的看上去,有时可以猜测出错误的原因。常见的这种中断时ASSERT宏导致的中断。在程序被中断时,debug工具条的右侧倒数第二个按钮一般是call stack按钮,这个按钮被按下后,你就可以看到当前的调用堆栈。 实例一:介绍我们首先演示一下调用堆栈。首先我们创建一个名为Debug的对话框工程。工程创建好以后,双击OK按钮创建消息映射函数,并添加如下代码: void CDebugDlg::OnOK() { // TODO: Add extra validation here ASSERT(FALSE); } 我们按F5开始调试程序。程序运行后,点击OK按钮,程序就会被中断。这时查看call stack窗口,就会发现内容如下: CDebugDlg::OnOK() line 176 + 34 bytes_AfxDispatchCmdMsg(CCmdTarget * 0x0012fe74 {CDebugDlg}, unsigned int 1, int 0, void (void)* 0x5f402a00 `vcall'(void), void ......

阅读全文(4028) | 评论:0