图1 建立一个静态链接库 |
//文件:lib.h #ifndef LIB_H #define LIB_H extern "C" int add(int x,int y); //声明为C编译、连接方式的外部函数 #endif //文件:lib.cpp #include "lib.h" int add(int x,int y) { return x + y; } |
#include #include "..\lib.h" #pragma comment( lib, "..\\debug\\libTest.lib" ) //指定与静态库一起连接 int main(int argc, char* argv[]) { printf( "2 + 3 = %d", add( 2, 3 ) ); } |
图2 在VC中设置库文件路径 |
图3 库的调试与“运行” |
图4 把库工程和调用库的工程放入同一工作区进行调试 |
图5 用Depends查看DLL |
当然Depends工具也可以显示DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL。
好,让我们正式进入动态链接库的世界,先来看看最一般的DLL,即非MFC DLL。
4.1一个简单的DLL
第2节给出了以静态链接库方式提供add函数接口的方法,接下来我们来看看怎样用动态链接库实现一个同样功能的add函数。
如图6,在VC++中new一个Win32 Dynamic-Link Library工程dllTest(单击此处下载本工程)。注意不要选择MFC AppWizard(dll),因为用MFC AppWizard(dll)建立的将是第5、6节要讲述的MFC 动态链接库。
图6 建立一个非MFC DLL |
/* 文件名:lib.h */ #ifndef LIB_H #define LIB_H extern "C" int __declspec(dllexport)add(int x, int y); #endif /* 文件名:lib.cpp */ #include "lib.h" int add(int x, int y) { return x + y; } |
#include #include typedef int(*lpAddFun)(int, int); //宏定义函数指针类型 int main(int argc, char *argv[]) { HINSTANCE hDll; //DLL句柄 lpAddFun addFun; //函数指针 hDll = LoadLibrary("..\\Debug\\dllTest.dll"); if (hDll != NULL) { addFun = (lpAddFun)GetProcAddress(hDll, "add"); if (addFun != NULL) { int result = addFun(2, 3); printf("%d", result); } FreeLibrary(hDll); } return 0; } |
; lib.def : 导出DLL函数 LIBRARY dllTest EXPORTS add @ 1 |
#pragma comment(lib,"dllTest.lib") //.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息 extern "C" __declspec(dllimport) add(int x,int y); int main(int argc, char* argv[]) { int result = add(2,3); printf("%d",result); return 0; } |
4.4 DllMain函数
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: printf("\nprocess attach of dll"); break; case DLL_THREAD_ATTACH: printf("\nthread attach of dll"); break; case DLL_THREAD_DETACH: printf("\nthread detach of dll"); break; case DLL_PROCESS_DETACH: printf("\nprocess detach of dll"); break; } return TRUE; } |
hDll = LoadLibrary("..\\Debug\\dllTest.dll"); if (hDll != NULL) { addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1)); //MAKEINTRESOURCE直接使用导出文件中的序号 if (addFun != NULL) { int result = addFun(2, 3); printf("\ncall add in dll:%d", result); } FreeLibrary(hDll); } |
process attach of dll call add in dll:5 process detach of dll |
#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i))) #define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i))) #ifdef UNICODE #define MAKEINTRESOURCE MAKEINTRESOURCEW #else #define MAKEINTRESOURCE MAKEINTRESOURCEA |
#define CALLBACK __stdcall //这就是传说中的回调函数 #define WINAPI __stdcall //这就是传说中的WINAPI #define WINAPIV __cdecl #define APIENTRY WINAPI //DllMain的入口就在这里 #define APIPRIVATE __stdcall #define PASCAL __stdcall |
int __stdcall add(int x, int y); |
typedef int(__stdcall *lpAddFun)(int, int); |
图7 调用约定不匹配时的运行错误 |
/* 文件名:lib.h */ #ifndef LIB_H #define LIB_H extern int dllGlobalVar; #endif /* 文件名:lib.cpp */ #include "lib.h" #include int dllGlobalVar; BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: dllGlobalVar = 100; //在dll被加载时,赋全局变量为100 break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } |
LIBRARY "dllTest" EXPORTS dllGlobalVar CONSTANT ;或dllGlobalVar DATA GetGlobalVar |
#include #pragma comment(lib,"dllTest.lib") extern int dllGlobalVar; int main(int argc, char *argv[]) { printf("%d ", *(int*)dllGlobalVar); *(int*)dllGlobalVar = 1; printf("%d ", *(int*)dllGlobalVar); return 0; } |
dllGlobalVar = 1; |
#include #pragma comment(lib,"dllTest.lib") extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入 int main(int argc, char *argv[]) { printf("%d ", dllGlobalVar); dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换 printf("%d ", dllGlobalVar); return 0; } |
//文件名:point.h,point类的声明 #ifndef POINT_H #define POINT_H #ifdef DLL_FILE class _declspec(dllexport) point //导出类point #else class _declspec(dllimport) point //导入类point #endif { public: float y; float x; point(); point(float x_coordinate, float y_coordinate); }; #endif //文件名:point.cpp,point类的实现 #ifndef DLL_FILE #define DLL_FILE #endif #include "point.h" //类point的缺省构造函数 point::point() { x = 0.0; y = 0.0; } //类point的构造函数 point::point(float x_coordinate, float y_coordinate) { x = x_coordinate; y = y_coordinate; } //文件名:circle.h,circle类的声明 #ifndef CIRCLE_H #define CIRCLE_H #include "point.h" #ifdef DLL_FILE class _declspec(dllexport)circle //导出类circle #else class _declspec(dllimport)circle //导入类circle #endif { public: void SetCentre(const point ¢rePoint); void SetRadius(float r); float GetGirth(); float GetArea(); circle(); private: float radius; point centre; }; #endif //文件名:circle.cpp,circle类的实现 #ifndef DLL_FILE #define DLL_FILE #endif #include "circle.h" #define PI 3.1415926 //circle类的构造函数 circle::circle() { centre = point(0, 0); radius = 0; } //得到圆的面积 float circle::GetArea() { return PI *radius * radius; } //得到圆的周长 float circle::GetGirth() { return 2 *PI * radius; } //设置圆心坐标 void circle::SetCentre(const point ¢rePoint) { centre = centrePoint; } //设置圆的半径 void circle::SetRadius(float r) { radius = r; } |
#include "..\circle.h" //包含类声明头文件 #pragma comment(lib,"dllTest.lib"); int main(int argc, char *argv[]) { circle c; point p(2.0, 2.0); c.SetCentre(p); c.SetRadius(1.0); printf("area:%f girth:%f", c.GetArea(), c.GetGirth()); return 0; } |
class _declspec(dllexport) point //导出类point { … } |
class _declspec(dllexport) circle //导出类circle { … } |
class _declspec(dllimport) point //导入类point { … } |
class _declspec(dllimport) circle //导入类circle { … } |
class _declspec(dllexport) class_name //导出类circle { … } |
class _declspec(dllimport) class_name //导入类 { … } |
#ifdef DLL_FILE class _declspec(dllexport) class_name //导出类 #else class _declspec(dllimport) class_name //导入类 #endif |
图8 设置动态/静态链接MFC DLL |
图9 MFC DLL工程的创建 |
图10所示对话框中的1区选择MFC DLL的类别。 |
BOOL CRegularDllSocketApp::InitInstance() { if (!AfxSocketInit()) { AfxMessageBox(IDP_SOCKETS_INIT_FAILED); return FALSE; } return TRUE; } |
图10 MFC DLL的创建选项
5.3 一个简单的MFC规则DLL
这个DLL的例子(属于静态链接到MFC 的规则DLL)中提供了一个如图11所示的对话框。
在DLL中添加对话框的方式与在MFC应用程序中是一样的。在图11所示DLL中的对话框的Hello按钮上点击时将MessageBox一个“Hello,pconline的网友”对话框,下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释(下载本工程): 第一组文件:CWinApp继承类的声明与实现
分析: 在这一组文件中定义了一个继承自CWinApp的类CRegularDllApp,并同时定义了其的一个实例theApp。乍一看,您会以为它是一个MFC应用程序,因为MFC应用程序也包含这样的在工程名后添加“App”组成类名的类(并继承自CWinApp类),也定义了这个类的一个全局实例theApp。 我们知道,在MFC应用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三个函数完成:
但是MFC规则DLL并不是MFC应用程序,它所继承自CWinApp的类不包含消息循环。 这是因为,MFC规则DLL不包含CWinApp::Run机制,主消息泵仍然由应用程序拥有。如果DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从DLL 导出的函数来调用PreTranslateMessage成员函数。 另外,MFC规则DLL与MFC 应用程序中一样,需要将所有 DLL中元素的初始化放到InitInstance 成员函数中。 第二组文件 自定义对话框类声明及实现
分析: 这一部分的编程与一般的应用程序根本没有什么不同,我们照样可以利用MFC类向导来自动为对话框上的控件添加事件。MFC类向导照样会生成类似ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映射宏。 第三组文件 DLL中的资源文件
分析: 在MFC规则DLL中使用资源也与在MFC应用程序中使用资源没有什么不同,我们照样可以用Visual C++的资源编辑工具进行资源的添加、删除和属性的更改。 第四组文件 MFC规则DLL接口函数
分析: 这个接口并不使用MFC,但是在其中却可以调用MFC扩展类CdllDialog的函数,这体现了“规则”的概类。 与非MFC DLL完全相同,我们可以使用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口。 5.4 MFC规则DLL的调用 笔者编写了如图12的对话框MFC程序(下载本工程)来调用5.3节的MFC规则DLL,在这个程序的对话框上点击“调用DLL”按钮时弹出5.3节MFC规则DLL中的对话框。
下面是“调用DLL”按钮单击事件的消息处理函数:
上述例子中给出的是显示调用的方式,可以看出,其调用方式与第4节中非MFC DLL的调用方式没有什么不同。 我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件(图12所示对话框类的实现文件)的顶部添加:
并将void CRegularDllCallDlg::OnCalldllButton() 改为:
5.5 共享MFC DLL的规则DLL的模块切换 应用程序进程本身及其调用的每个DLL模块都具有一个全局唯一的HINSTANCE句柄,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的缺省句柄为0x10000000。如果程序同时加载了多个DLL,则每个DLL模块都会有不同的HINSTANCE。应用程序在加载DLL时对其进行了重定位。 共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。 这次我们创建一个动态链接到MFC DLL的规则DLL(下载本工程),在其中包含如图13的对话框。
另外,在与这个DLL相同的工作区中生成一个基于对话框的MFC程序,其对话框与图12完全一样。但是在此工程中我们另外添加了一个如图14的对话框。
图13和图14中的对话框除了caption不同(以示区别)以外,其它的都相同。 尤其值得特别注意,在DLL和EXE中我们对图13和图14的对话框使用了相同的资源ID=2000,在DLL和EXE工程的resource.h中分别有如下的宏:
与5.3节静态链接MFC DLL的规则DLL相同,我们还是在规则DLL中定义接口函数ShowDlg,原型如下:
而为应用工程主对话框的“调用DLL”的单击事件添加如下消息处理函数:
我们以为单击“调用DLL”会弹出如图13所示DLL中的对话框,可是可怕的事情发生了,我们看到是图14所示EXE中的对话框! 惊讶? 产生这个问题的根源在于应用程序与MFC规则DLL共享MFC DLL(或MFC扩展DLL)的程序总是默认使用EXE的资源,我们必须进行资源模块句柄的切换,其实现方法有三: 方法一 在DLL接口函数中使用:
我们将DLL中的接口函数ShowDlg改为:
这次我们再点击EXE程序中的“调用DLL”按钮,弹出的是DLL中的如图13的对话框!嘿嘿,弹出了正确的对话框资源。 AfxGetStaticModuleState是一个函数,其原型为:
该函数的功能是在栈上(这意味着其作用域是局部的)创建一个AFX_MODULE_STATE类(模块全局数据也就是模块状态)的实例,对其进行设置,并将其指针pModuleState返回。 AFX_MODULE_STATE类的原型如下:
AFX_MODULE_STATE类利用其构造函数和析构函数进行存储模块状态现场及恢复现场的工作,类似汇编中call指令对pc指针和sp寄存器的保存与恢复、中断服务程序的中断现场压栈与恢复以及操作系统线程调度的任务控制块保存与恢复。 许多看似不着边际的知识点居然有惊人的相似! AFX_MANAGE_STATE是一个宏,其原型为:
该宏用于将pModuleState设置为当前的有效模块状态。当离开该宏的作用域时(也就离开了pModuleState所指向栈上对象的作用域),先前的模块状态将由AFX_MODULE_STATE的析构函数恢复。 方法二 在DLL接口函数中使用:
AfxGetResourceHandle用于获取当前资源模块句柄,而AfxSetResourceHandle则用于设置程序目前要使用的资源模块句柄。 我们将DLL中的接口函数ShowDlg改为:
通过AfxGetResourceHandle和AfxSetResourceHandle的合理变更,我们能够灵活地设置程序的资源模块句柄,而方法一则只能在DLL接口函数退出的时候才会恢复模块句柄。方法二则不同,如果将ShowDlg改为:
在应用程序主对话框的“调用DLL”按钮上点击,将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。 方法三 由应用程序自身切换 资源模块的切换除了可以由DLL接口函数完成以外,由应用程序自身也能完成(下载本工程)。 现在我们把DLL中的接口函数改为最简单的:
而将应用程序的OnCalldllButton函数改为:
方法三中的Win32函数GetModuleHandle可以根据DLL的文件名获取DLL的模块句柄。如果需要得到EXE模块的句柄,则应调用带有Null参数的GetModuleHandle。 方法三与方法二的不同在于方法三是在应用程序中利用AfxGetResourceHandle和AfxSetResourceHandle进行资源模块句柄切换的。同样地,在应用程序主对话框的“调用DLL”按钮上点击,也将看到两个对话框,相继为DLL中的对话框(图13)和EXE中的对话框(图14)。 在下一节我们将对MFC扩展DLL进行详细分析和实例讲解,欢迎您继续关注本系列连载。
前文我们对非MFC DLL和MFC规则DLL进行了介绍,现在开始详细分析DLL的最后一种类型――MFC扩展DLL。
6.1概论 MFC扩展DLL与MFC规则DLL的相同点在于在两种DLL的内部都可以使用MFC类库,其不同点在于MFC扩展DLL与应用程序的接口可以是MFC的。MFC扩展DLL的含义在于它是MFC的扩展, 其主要功能是实现从现有MFC库类中派生出可重用的类。MFC扩展DLL使用MFC 动态链接库版本,因此只有用共享MFC 版本生成的MFC 可执行文件(应用程序或规则DLL)才能使用MFC扩展DLL。 从前文可知,MFC规则DLL被MFC向导自动添加了一个CWinApp的对象,而MFC扩展DLL则不包含该对象,它只是被自动添加了DllMain 函数。对于MFC扩展DLL,开发人员必须在DLL的DllMain函数中添加初始化和结束代码。 从下表我们可以看出三种DLL对DllMain入口函数的不同处理方式:
对于MFC扩展DLL,系统会自动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA这样的宏,在DLL和应用程序中将具有不同的定义,这取决于_AFXEXT宏是否被定义。这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思。在DLL中,表示输出(因为_AFXEXT被定义,通常是在编译器的标识参数中指定/D_AFXEXT);在应用程序中,则表示输入(_AFXEXT没有定义)。
6.2 MFC扩展DLL导出MFC派生类 在这个例子中,我们将产生一个名为“ExtDll”的MFC扩展DLL工程,在这个DLL中导出一个对话框类,这个对话框类派生自MFC类CDialog。 使用MFC向导生成MFC扩展DLL时,系统会自动添加如下代码:
这一段代码含义晦涩,我们需要对其进行解读: (1)上述代码完成MFC扩展DLL的初始化和终止处理; (2)初始化期间所创建的 CDynLinkLibrary 对象使MFC扩展 DLL 可以将 DLL中的CRuntimeClass 对象或资源导出到应用程序; (3)AfxInitExtensionModule函数捕获模块的CRuntimeClass 结构和在创建 CDynLinkLibrary 对象时使用的对象工厂(COleObjectFactory 对象); (4)AfxTermExtensionModule函数使 MFC 得以在每个进程与扩展 DLL 分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展 DLL; (5)第一条语句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:
由AFX_EXTENSION_MODULE的定义我们可以更好的理解(2)、(3)、(4)点。 在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。
修改ExtDialog.h中CExtDialog类的声明为:
这其中最主要的改变是我们在class AFX_EXT_CLASS CExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。 |
图16 MFC扩展DLL调用工程中的对话框 |
// LoadExtDllDlg.cpp : implementation file // #include "..\ExtDialog.h" #pragma comment( lib, "ExtDll.lib" ) 而“调用DLL”按钮的单击事件的消息处理函数为: void CLoadExtDllDlg::OnDllcallButton() { CExtDialog extDialog; extDialog.DoModal(); } |
HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName ); |
BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib ); |
void CLoadExtDllDlg::OnDllcallButton() { HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" ); if(NULL == hDll) { AfxMessageBox( "MFC扩展DLL动态加载失败" ); return; } CExtDialog extDialog; extDialog.DoModal(); AfxFreeLibrary(hDll); } |
LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialog@@UAE@XZ) LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialog@@QAE@PAVCWnd@@@Z) |
error LNK2001: unresolved external symbol …....... |
//临时改变宏的含义“输出”为“输入” #undef AFX_EXT_CLASS #undef AFX_EXT_API #undef AFX_EXT_DATA #define AFX_EXT_CLASS AFX_CLASS_IMPORT #define AFX_EXT_API AFX_API_IMPORT #define AFX_EXT_DATA AFX_DATA_IMPORT //包含被调用MFC扩展DLL的头文件 #include "CalledDLL.h" //恢复宏的含义为输出 #undef AFX_EXT_CLASS #undef AFX_EXT_API #undef AFX_EXT_DATA #define AFX_EXT_CLASS AFX_CLASS_EXPORT #define AFX_EXT_API AFX_API_EXPORT #define AFX_EXT_DATA AFX_DATA_EXPORT |
//global.h:MFC扩展DLL导出变量和函数的声明 extern "C" { int AFX_EXT_DATA total; //导出变量 int AFX_EXT_API add( int x, int y ); //导出函数 } //global.cpp:MFC扩展DLL导出变量和函数定义 #include "StdAfx.h" #include "global.h" extern "C" int total; int add(int x,int y) { total = x + y; return total; } |
#include #include //AFX_EXT_DATA、AFX_EXT_API宏的定义在afxver_.h头文件中 #pragma comment ( lib, "ExtDll.lib" ) #include "..\global.h" int main(int argc, char* argv[]) { cout << add(2,3) << endl; cout << total; return 0; } |
class AFX_EXT_CLASS CMultiColorStatic : public CStatic { // Construction public: CMultiColorStatic(); virtual ~CMultiColorStatic(); // Attributes protected: CString m_strCaption; COLORREF m_BackColor; COLORREF m_TextColor; // Operations public: void SetTextColor( COLORREF TextColor ); void SetBackColor( COLORREF BackColor ); void SetCaption( CString strCaption ); // Generated message map functions protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; |
BEGIN_MESSAGE_MAP(CMultiColorStatic, CStatic) //{{AFX_MSG_MAP(CMultiColorStatic) ON_WM_PAINT() //为这个类定义WM_PAINT消息处理函数 //}}AFX_MSG_MAP END_MESSAGE_MAP() |
//为CMultiColorStatic类添加“设置文本颜色”接口 void CMultiColorStatic::SetTextColor( COLORREF TextColor ) { m_TextColor = TextColor; //设置文字颜色 } //为CMultiColorStatic类添加“设置背景颜色”接口 void CMultiColorStatic::SetBackColor( COLORREF BackColor ) { m_BackColor = BackColor; //设置背景颜色 } //为CMultiColorStatic类添加“设置标题”接口 void CMultiColorStatic::SetCaption( CString strCaption ) { m_strCaption = strCaption; } //重画Static,颜色和标题的设置都依赖于这个函数 void CMultiColorStatic::OnPaint() { CPaintDC dc(this); // device context for painting CRect rect; GetClientRect( &rect ); dc.SetBkColor( m_BackColor ); dc.SetBkMode( TRANSPARENT ); CFont *pFont = GetParent()->GetFont();//得到父窗体的字体 CFont *pOldFont; pOldFont = dc.SelectObject( pFont );//选用父窗体的字体 dc.SetTextColor( m_TextColor );//设置文本颜色 dc.DrawText( m_strCaption, &rect, DT_CENTER );//文本在Static中央 dc.SelectObject( pOldFont ); } |
图17 扩展的CStatic类调用演示 |
#include "..\MultiColorStatic.h" #pragma comment ( lib, "ColorStatic.lib" ) // CCallDllDlg dialog class CCallDllDlg : public CDialog { public: CCallDllDlg(CWnd* pParent = NULL); // standard constructor enum { IDD = IDD_CALLDLL_DIALOG }; CMultiColorStatic m_colorstatic; //包含一个CMultiColorStatic的实例 protected: virtual void DoDataExchange(CDataExchange* pDX);//DDX/DDV support HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CCallDllDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnRedButton(); afx_msg void OnBlueButton(); afx_msg void OnGreenButton(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
void CCallDllDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CCallDllDlg) DDX_Control(pDX, IDC_COLOR_STATIC, m_colorstatic); //使m_colorstatic与IDC_COLOR_STATIC控件关联 //}}AFX_DATA_MAP } BOOL CCallDllDlg::OnInitDialog() { … // TODO: Add extra initialization here // 初始static控件的显示 m_colorstatic.SetCaption("最开始为黑色"); m_colorstatic.SetTextColor(RGB(0,0,0)); return TRUE; // return TRUE unless you set the focus to a control } //设置static控件文本颜色为红色 void CCallDllDlg::OnRedButton() { m_colorstatic.SetCaption( "改变为红色" ); m_colorstatic.SetTextColor( RGB( 255, 0, 0 ) ); Invalidate( TRUE ); //导致发出WM_PAINT消息 } //设置static控件文本颜色为蓝色 void CCallDllDlg::OnBlueButton() { m_colorstatic.SetCaption( "改变为蓝色" ); m_colorstatic.SetTextColor( RGB( 0, 0, 255 ) ); Invalidate( TRUE ); //导致发出WM_PAINT消息 } //设置static控件文本颜色为绿色 void CCallDllDlg::OnGreenButton() { m_colorstatic.SetCaption( "改变为绿色" ); m_colorstatic.SetTextColor( RGB(0,255,0) ); Invalidate( TRUE ); //导致发出WM_PAINT消息 } |