📄 05o015.htm
字号:
ActiveX技术。但是不容忽视的是,基于传统的DDE数据交换也自有它的应用空间,<br>
使用仍然广泛。目前在Windows 3.x下,基于OLE的远程数据交换还很不成熟,但是<br>
在WFW(Windows for Workgroup)下基于网络动态数据交换的技术却很成熟,目前也<br>
应用非常普遍。关于DDE应用的开发和NetDDE的应用可以参看附录7。<br>
1、回调函数的处理<br>
由于DDEML机制需要使用回调函数,因此使用DDEML的关键是解决在MFC编程体系中回<br>
调函数的使用。回调函数(Callback function)大量用于Windows的系统服务,通过<br>
它,程序员可以安装设备驱动程序和消息过滤系统,以控制Windows的有效使用。<br>
许多程序员都发现,利用MFC或者其它的C++应用编写回调函数是非常麻烦的,其根<br>
本原因是回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以<br>
将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函<br>
数将发生错误,甚至编译就不能通过。通过查询资料发现,其错误是普通的C++成员<br>
函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自<br>
身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为<br>
什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的<br>
作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this<br>
指针使得函数参数个数不匹配,从而导致回调函数安装失败。要解决这一问题的关<br>
键就是不让this指针起作用,通过采用以下两种典型技术可以解决在C++中使用回调<br>
函数所遇到的问题。这种方法具有通用性,适合于任何C++。<br>
1. 不使用成员函数,直接使用普通C函数,为了实现在C函数中可以访问类的成员变<br>
量,可以使用友元操作符(friend),在C++中将该C函数说明为类的友元即可。这种<br>
处理机制与普通的C编程中使用回调函数一样。<br>
2. 使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作<br>
为回调函数了。静态成员函数具有两大特点:其一,可以在没有类实例的情况下使<br>
用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非<br>
静态成员函数。由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有<br>
的成员变量和成员函数,如果作不到这一点将不具有实际意义。解决的办法也很简<br>
单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如<br>
pThis=this,然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函<br>
数了。这种处理办法适用于只有一个类实例的情况,因为多个类实例将共享静态类<br>
成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情<br>
况,可以使用回调函数的一个参数来传递this指针,从而实现数据成员共享。这种<br>
方法稍稍麻烦,这里就不再赘述。<br>
2、在MFC中使用DDEML<br>
对于典型的MFC应用程序,主框架窗口类(CMainFrame)只有一个实例,因此可以使用<br>
静态成员函数作为回调函数,从而实现DDE机制。具体的代码片段如下:<br>
(1) 在CMainFrame类中声明如下静态成员:<br>
static CMainFrame* pThis;<br>
static DWORD idInst;<br>
static HDDEDATA CALLBACK EXPORT
DdeCallback(UINT,UINT,HCONV,HSZ,HSZ,
HDDEDATA,DWORD,DWORD);<br>
(2) 在类的创建代码(OnCreate())中作如下说明:<br>
pThis=this; <br>
lpDdeCallback=MakeProcInstance((FARPROC)DdeCallback,hInstance);<br>
if(DdeInitialize(&idInst,(PFNCALLBACK)lpDdeCallback,CBF_FAIL_EXECUTES<br>
|CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,0L))<br>
{<br>
AfxMessageBox("不能初始化DDE服务","错误");<br>
DestroyWindow();<br>
}<br>
(3) 回调函数实现如下:<br>
HDDEDATA FAR PASCAL _export CMainFrame::DdeCallback(UINT
iType,UINT iFmt, HCONV hConv,HSZ hsz1,HSZ hsz2,HDDEDATA
hData,DWORD dwData1,DWORD dwData2)<br>
{<br>
char szBuffer[16];<br>
int i;<br>
<br>
switch(iType)<br>
{<br>
case XTYP_CONNECT: //hsz1=topiv, hsz2=service<br>
return (HDDEDATA)TRUE;//TRUE;<br>
case XTYP_ADVSTART: //hsz1=topic, hsz2=item<br>
case XTYP_REQUEST:<br>
case XTYP_ADVREQ:<br>
case XTYP_POKE: //hsz1=Topic, hsz2=item, hData=data<br>
case XTYP_ADVSTOP:<br>
return NULL;<br>
}<br>
}<br>
3、避免变量类型冲突<br>
如果在MFC应用直接使用DDEML服务,那么该MFC应用在编译时将会遇到变量类型HSZ<br>
重复定义错误。经过追踪发现,错误在于在DDEML.H对HSZ作了如下定义:<br>
DECLARE_HANDLE32(HSZ);<br>
而在AFXEXT.H(通过stdafx.h引入)中对HSZ又作了如下说明:<br>
typedef BPSTR FAR* HSZ; // Long handle to a string<br>
两个定义一个为32位整数,一个为BASIC字符串指针,当然会发生编译器不能作变量<br>
类型转换的错误。实际上,将HSZ声明为BASIC字符串指针主要用于在MFC应用中使用<br>
VBX控制。要改正这一错误,就必须保证不要在同一个代码模块中使用DDEML和VBX支<br>
持,通过将使用DDEML和VBX的代码分开,并在使用DDEML代码的模块中最开头定义如<br>
下编译器宏就可以解决上述问题:<br>
#define NO_VBX_SUPPORT<br>
五、使用3D控制<br>
毫无疑问,3D控制的使用可以显著提高Windows应用程序的界面友好性,目前,许多<br>
流行的Windows应用程序都使用了3D控制,典型的如Microsoft公司的Office系列软<br>
件,而且,在Windows 95和Windows NT 4.0中,3D控制更是作为操作系统的一部分<br>
直接提供,这意味着在其上运行的软件不需要作任何特殊处理,就具有3D界面效<br>
果,但是,很遗憾的是,在Windows 3.x中,除了命令按钮控制使用3D控制以外,其<br>
余所有的控制,如编辑框,列表框,检查框等都只使用2D控制,要想使用3D控制,<br>
程序设计人员就必须在自己的程序中作一定的修改,考虑到目前3D效果的流行,这<br>
点努力是值得的。<br>
为了支持3D效果,Microsoft公司提供了一个专门用于3D控制的动态连接库,即<br>
CTL3D.DLL,但是在其Visual C++中却没有如何使用3D控制的讨论,并且,Visual
<br>
C++也不直接支持3D编码,因为它不包括使用3D控制所必须的头文件。但是,这并不<br>
意味着在Visual C++中不能使用3D控制,只不过用户需要从其它地方获取技术支持<br>
罢了。由于使用的是动态连接库机制,因此,任何其它语言提供的3D头文件和<br>
CTL3D.DLL的输入库都是可用的。作者使用的就是Borland公司的Borland
C++中提供<br>
的CTL3D.H和CTL3D.LIB。在C/C++中使用3D控制的方法也有很多种,在这里,为节约<br>
篇幅,只讨论与本文相关的主题,即使用MFC编程时如何使用3D控制。<br>
在MFC的所有对话框中使用3D控制可以遵循如下步骤:<br>
1. 在CWinApp::InitInstance函数中调用Ctl3dRegister和Ctl3dAutosubclass函<br>
数:<br>
Ctl3dRegister(AfxGetInstanceHandle());<br>
Ctl3dAutoSubclass(AfxGetInstanceHandle());<br>
值得一提的是,在AppWizard产生的应用框架的CWinApp::InitInstance中有一个函<br>
数调用为SetDialogBkColor,此函数的作用是将所有对话框的背景颜色设置为灰<br>
色,这个功能与3D界面实现相同的功能,可以移去此语句。<br>
由于CTL3D在初始化时读入所有的系统颜色并自己维持,为了使应用程序能够正确反<br>
映系统颜色的变化,MFC应用程序可以在WM_SYSCOLORCHANGE消息中调用<br>
Ctl3dColorChange函数。<br>
2. 在MFC应用程序的CWinApp类中的ExitInstance函数中调用Ctl3dUnregister函<br>
数,以方便Windows对CTL3D库的正确管理。<br>
3. 在MFC应用程序的项目文件中加入CTL3D.LIB(可以用IMPORT.EXE产生)。<br>
使用上述CTL3D的自动子类化的机制可以大大简化使用3D控制,如果这不满足你的要<br>
求,那么你就必须单独在需要使用3D控制的对话框的OnInitDialog()中自行子类化<br>
相关的控制类了,典型的如下代码片断所示:<br>
BOOL CMyDialog::OnInitDialog()<br>
{<br>
Ctl3dSubclassDlgEx(m_hWnd,CTL3D_ALL);<br>
return TRUE;<br>
}<br>
上面讲了在对话框中使用3D效果的办法,如果用户想在非对话框中使用3D控制,典<br>
型的在FormView导出类中使用,可以在导出类的OnInitialUpdate函数中进行适当修<br>
改,修改的大小取决于你是否使用了3D控制的自动子类化机制。如果使用前面提到<br>
的自动子类化方法,那么仅需要在相应的OnInitialUpdate函数中调用<br>
Ctl3dSubclassDlg函数了,如下代码片断所示:<br>
void CMyView::OnInitialUpdate()<br>
{<br>
Ctl3dSubclassDlg(m_hWnd,CTL3D_ALL);<br>
}<br>
否则,则需要修改如下:<br>
void CMyView::OnInitialUpdate()<br>
{<br>
Ctl3dSubclassDlgEx(m_hWnd,CTL3D_ALL);<br>
}<br>
六、使用自定义消息<br>
1、MFC的消息映射机制<br>
Windows是一个典型的消息驱动的操作系统,程序的运行是靠对各种消息的响应来实<br>
现的,这些消息的来源非常广泛,既包括Windows系统本身,如WM_CLOSE、<br>
WM_PAINT、WM_CREATE和WM_TIMER等常用消息,又包括用户菜单选择、键盘加速<br>
键以及工具条和对话框按钮等等,如果应用程序要与其它程序协同工作,那么消息的来<br>
源还包括其它应用程序发送的消息,串行口和并行口等硬件发送的消息等等。总<br>
之,Windows程序的开发是围绕着对众多消息的合理响应和实现来实现程序的各种功<br>
能的。使用过C语言来开发Windows程序的人都知道,在Windows程序的窗口回调函数<br>
中需要安排Switch语句来响应大量的消息,同时由于消息的间断性使得不同的消息<br>
响应之间信息的传递是通过大量的全局变量或者静态数据来实现的。<br>
人们常用的两种类库OWL和MFC都提供了消息映射机制用以加速开发速度,使用者只<br>
需要按规定定义好对应消息的处理函数自身即可,至于实际调用由类库本身所提供<br>
的机制进行,或采用虚函数,或采用消息映射宏。为了有效节约内存,MFC并不大量<br>
采用虚函数机制,而是采用宏来将特定的消息映射到派生类中的响应成员函数。这<br>
种机制不但适用于Windows自身的140条消息,而且适用于菜单命令消息和按钮控制<br>
消息。MFC提供的消息映射机制是非常强大的,它允许在类的各个层次上对消息进行<br>
控制,而不简单的局限于消息产生者本身。在应用程序接收到窗口命令时,MFC将按<br>
如下次序寻找相应的消息控制函数:<br>
SDI应用<br>
MDI应用<br>
视口<br>
视口<br>
文档<br>
文档<br>
SDI主框架<br>
MDI子框架<br>
应用<br>
MDI主框架<br>
<br>
应用<br>
大多数应用对每一个命令通常都只有一个特定的命令控制函数,而这个命令控制函<br>
数也只属于某一特定的类,但是如果在应用中对同一消息有多个命令控制函数,那<br>
么只有优先级较高的命令控制函数才会被调用。为了简化对常用命令的处理,MFC在<br>
基类中提供并实现了许多消息映射的入口,如打印命令,打印预览命令,退出命令<br>
以及联机帮助命令等,这样在派生类中就继承了所有的基类中的消息映射函数,从<br>
而可以大大简化编程。如果我们要在自己派生类中实现对消息的控制,那么必须在<br>
派生类中加上相应的控制函数和映射入口。<br>
2、使用自己的消息<br>
在程序设计的更深层次,人们常常会发现只依赖于菜单和命令按钮产生的消息是不<br>
够的,常常因为程序运行的逻辑结构和不同视口之间数据的同步而需要使用一些自<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -