当前位置:数据分析 > VS2019中如何使用MFC库_VS2019 MFC DLL共享动态链接库(MFC正则库)封装例程非模态调用MFC正常...

VS2019中如何使用MFC库_VS2019 MFC DLL共享动态链接库(MFC正则库)封装例程非模态调用MFC正常...

  • 发布:2023-10-07 13:17

MFC连接数据库并添加数据

前言 1.在数据库中新建一个试库,并添加一个表student(随意命名,仅供参考) 1.新建一个试库 2.在库中添加表student

2。新建一个MFC工程==配置== 1.配置编译平台 2.配置目录和库文件 2.1包含目录 2.2引用目录和库目录 2.3附加包含目录 2.4附加库目录 2.5附加依赖 2.6字符集

3.添加文件

3。设计MFC界面并编写代码 1. MFC界面 2. 程序代码 2.1 首先包含头文件并创建数据库对象 2.2 初始化时禁用断开数据库按钮 2.3 连接数据库 2.4 插入数据 2.5 断开数据库

4。效果演示 1. 添加数据前 2. 添加数据中 3. 添加数据后

总结

前言

昨天下载了最新版本的MySQL,查询了很多资料,测试了各种例子,终于能够连接数据库并添加数据了。不过网上的例子要么不完整,要么运行结束时报各种错误。我准备把整个过程从头开始记录下来,方便自己以后复习,也方便其他新人朋友们。

1。在数据库中新建一个测试库,添加一个表student(随意命名,仅供参考)

1。创建一个新的测试库

我使用SQLyog工具添加测试库,ctrl+D弹出添加界面,如图,新库添加成功

2。在数据库中添加表student

通过命令提示符操作数据库。请务必在管理员模式下运行它。如图所示,表格添加成功

2。新建一个MFC项目并进行配置

新建工程省略,只需根据对话框选择即可

1。配置编译平台

右键单击解决方案 – 选择属性 – 配置属性 – 配置管理器 – 选择 x64 作为活动解决方案平台。我是64位系统

2。配置目录和库文件

2.1 包含目录

选择MySQL安装路径下的include文件夹

2.2 参考目录和库目录

选择MySQL安装路径下的lib文件夹

2.3 附加包含目录

选择MySQL安装路径下的include文件夹

2.4 附加库目录

选择MySQL安装路径下的lib文件夹

2.5 其他依赖项

添加libmysql.lib(手动输入)

2.6字符集

选择多字节字符集

3.添加文件

将数据库中lib文件夹中的libmysql.dll和libmysql.lib文件复制到项目所在文件

3。设计MFC界面并编写代码

1.MFC接口

在学号和姓名对应的Edit Control中添加变量,类型选择value,并分别在三个Button上添加三个事件处理函数

2.程序代码

2.1 首先包含头文件并创建数据库对象

#include //包含头文件

MYSQL m_sqlCon; //创建数据库对象

2.2 初始化期间禁用断开数据库按钮

BOOL CMySQLtestDlg::OnInitDialog()

{

CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。

ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

断言(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);

if (pSysMenu != nullptr)

{

BOOL bNameValid;

CString strAboutMenu;

bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);

ASSERT(bNameValid);

if (!strAboutMenu.IsEmpty())

{

pSysMenu->AppendMenu(MF_SEPARATOR);

pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

}

}

// 设置该对话框的图标。当应用程序主窗口不是对话框时,框架会自动

// 这样做

SetIcon(m_hIcon, TRUE); // 设置大图标

SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO:在此处添加额外的初始化代码

GetDlgItem(IDC_BUTTON3)->EnableWindow(FALSE);

//只要添加这句话,剩下的就会自动生成

//系统启动时禁用断开服务器按钮,防止误操作

返回 TRUE; // 返回 TRUE,除非焦点设置到控件

}

2.3 连接数据库

void CMySQLtestDlg::OnBnClickedConnect()

{

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

mysql_init(&m_sqlCon);

if (!mysql_real_connect(&m_sqlCon, "localhost", "填写你的用户名", "填写你的密码", "test", 3306, NULL, 0))

{

AfxMessageBox(_T("数据库连接失败!"));

返回;

}

其他

{

AfxMessageBox(_T("数据库连接成功!"));

GetDlgItem(IDC_BUTTON1)->启用窗口(FALSE);

GetDlgItem(IDC_BUTTON3)->启用窗口(TRUE);

返回;

}

}

2.4 插入数据

void CMySQLtestDlg::OnBnClickedInsert()

{

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

更新数据(true);

mysql_query(&m_sqlCon, "设置名称'gb2312'");

char* id = (char*)m_id.GetBuffer();

char* 名称 = (char*)m_name.GetBuffer();

字符插入[1000];

sprintf_s(insert, "插入学生(id,姓名)值(\'%s\',\'%s\')", id, name);

if (mysql_query(&m_sqlCon, insert) == 0)

{

AfxMessageBox(_T("插入数据成功!"));

}

其他

{

AfxMessageBox(_T("插入数据失败!"));

}

更新数据(假);

}

2.5 断开数据库连接

void CMySQLtestDlg::OnBnClickedDisConnect()

{

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

mysql_close(&m_sqlCon);

GetDlgItem(IDC_BUTTON1)->启用窗口(TRUE);

GetDlgItem(IDC_BUTTON3)->启用窗口(FALSE);

}

4。效果演示

1。添加数据之前

2。添加数据

3.添加数据后

总结

功能非常简单。它仅插入数据。可以在此基础上进一步发展

[前言]

?????????网上关于MFC共享DLL的介绍很多,这里就不一一介绍了。在实际应用中,基于MFC编写DLL程序时发现“规则DLL共享MFC?DLL”是编译后不包含MFC库的DLL库文件。这种方法比“带有静态链接MFC的常规DLL”编译稍大一些。因此,在发布“共享MFC?DLL规则DLL”dll时,如果对方的机器没有安装MFC库,则该dll将无法运行,除非你也给他MFC库。 “共享MFC?DLL常规DLL”与“静态链接MFC的常规DLL”最大的区别在于使用MFC的方法。正是因为“共享MFC DLL规则DLL”的这些特点,当系统加载这个如果DLL和应用程序中存在相同ID的资源,系统将无法正确区分程序员的意图。因此,使用“规则DLL共享MFC? DLL”我们需要通过模块切换找到正确的资源模块并执行相应的操作。“规则DLL共享MFC?的模块切换?” DLL》:在再次解释这个问题之前,我们先来了解一下DLL的内部运行机制:

应用程序进程本身及其调用的每个DLL模块都有一个全局唯一的HINSTANCE句柄,它代表DLL或EXE模块在进程虚拟空间中的起始地址。进程本身的模块句柄一般为0x400000,而DLL模块的默认句柄为0x10000000。如果程序同时加载多个DLL,则每个DLL模块都会有不同的HINSTANCE。 ? ? ? ?应用程序在加载 DLL 时重新定位该 DLL。共享MFC的规则DLL? DLL(或MFC扩展DLL)涉及HINSTANCE句柄的问题。 HINSTANCE 句柄对于加载资源尤其重要。 EXE和DLL都有自己的资源,这些资源的ID可能会重复。应用程序需要通过切换资源模块来找到正确的资源。

? ? ?如果应用程序需要来自DLL的资源,则应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,则应指定资源模块句柄作为EXE的模块句柄。 。

?????????为了完成模块切换,DLL 输出的所有函数都应以以下语句开头: AFX_MANAGE_STATE(AfxGetStaticModuleState(?)) ? ? ?这句话是用来正确切换MFC的。模块状态。说明:它的功能是在堆栈上创建 AFX_MODULE_STATE 类的实例(这意味着它的作用域是本地的)并返回其指针 pModuleState。 AFX_MODULE_STATE 类使用其构造函数和析构函数来存储和恢复模块状态场景。

该宏用于将pModuleState 设置为当前有效模块状态。当离开该宏的作用域(并因此离开 pModuleState 指向的堆栈上的对象的作用域)时,类 AFX_MODULE_STATE 的析构函数将恢复先前的模块状态。

(2) 带有静态链接MFC的常规DLL;

这个就不多说了,它是一个DLL类型,将MFC?dll编译到自己的内部。与“使用共享MFC的规则DLL?DLL”相比,也不难理解;

(3)常规DLL中的调用约定和名称修饰:调用约定是程序向函数传递参数和接收返回值的标准约定。它是为实现函数调用而建立的标准协议。该协议规定了该语言的函数中的参数传递方式、参数是否可变以及谁来处理堆栈。不同的语言定义了不同的调用约定。在C++中,为了允许运算符重载和函数重载,C++编译器经常按照一定的规则重写每个入口点的符号名称,以允许相同的名称(具有不同的参数类型或不同的作用域)有多种使用而不破坏现有的基于 C 的链接器。这种技术通常称为名称修改或名称装饰。许多 C++ 编译器供应商都选择使用自己的名称 mangling 解决方案。因此,为了让其他语言编写的模块(例如 Visual Basic 应用程序、Pascal 或 Fortran 应用程序等)调用 C/C++ 编写的 DLL 的函数,必须使用正确的调用约定导出函数,并且不要让编译器对要导出的函数应用任何名称修改。调用约定用于确定函数参数在传递时压入和弹出的顺序(无论是调用者还是被调用者从堆栈中弹出参数),编译器用于识别函数。名称修改约定和其他问题。 Microsoft VC++ 6.0 中定义了以下调用约定: 1. __cdecl__cdecl 是 C/C++ 和 MFC 程序使用的默认调用约定。您还可以在声明函数时添加 __cdecl 键。字符是手动指定的。使用__cdecl约定时,函数参数按从右到左的顺序压入堆栈,调用者将参数弹出堆栈以清空堆栈。因此,实现可变参数的函数只能使用这种调用约定。由于每个使用__cdecl约定的函数都必须包含清理堆栈的代码,因此生成的可执行文件的大小会相对较大。 __cdecl 可以写成 _cdecl。 2. __stdcall__stdcall调用约定用于调用Win32 API函数。使用 __stdcal 约定时,函数参数按从右到左的顺序压入堆栈。被调用函数在返回之前会清除用于传输参数的堆栈。函数参数的数量是固定的。由于函数体本身知道传入的参数个数,因此被调用的函数可以在返回前使用ret?n指令直接清除传入参数的堆栈。 __stdcall 可以写成 _stdcall 吗? 3. __fastcall __fastcall约定用于对性能要求非常高的情况。 __fastcall 约定从左侧打开函数前两个不超过4字节的参数(DWORD)分别放置在ECX和EDX寄存器中。剩余的参数仍然从右到左压入堆栈。被调用函数在返回之前会清除传输参数的堆栈。 __fastcall 可以写成 _fastcall。?????????最后说明:关键字 __cdecl、__stdcall 和 __fastcall 可以直接添加到要输出的函数之前,也可以在Setting...->C 中添加/ 编译环境。 C++->Code?生成项选择。它们对应的命令行参数是/Gd、/Gz和/Gr。默认状态为/Gd,即__cdecl。当在输出函数前添加关键字与编译环境选择不同时,直接在输出函数前添加关键字有效。

? ? ? ?从上面我们可以看出MFC共享DLL是一个非常复杂的工程。关于MFC的知识有很多吗? dll只能在实践中慢慢学习。最大的感受就是和“MFC扩展DLL”不同。虽然MFC可以在规则DLL内部使用,但是规则DLL的接口不应该基于MFC。 MFC扩展DLL和应用程序接口可以是MFC,并且可以从MFC扩展DLL导出MFC派生类。

?????????一般情况下,我们都会使用正则DLL,因为“正则DLL”可以为所有支持dll技术的语言提供调用接口。在常规DLL中,有一个继承自CWinApp的类,其dll入口函数是由MFC自动提供并由MFC封装的。此类DLL程序源自CWinApp,但没有消息循环。

??????动态链接到MFC的常规DLL可以与使用它的可执行文件同时动态链接到MFC DLL和任何MFC扩展DLL。使用MFC共享库时,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能无法获取正确的资源。因此,对于共享MFC DLL的常规DLL,我们必须进行模块切换,以便MFC能够找到正确的资源模板。

?????????我们可以在Visual C++中设置MFC规则DLL是静态链接到MFC DLL还是动态链接到MFC DLL。如图8所示,选择Visual C++项目->设置->常规菜单或选项以便在Microsoft Foundation Classes中进行设置。

【MFC规则DLL?包库示例】?

【第一步】打开VS 2019如下图所示。点击“创建新项目(N)”,进入项目创建窗口。

?

?【步骤2】在要创建的工程的属性中选择“MFC动态链接库”,点击“下一步”继续。

?

【步骤3】在工程名称中输入要创建的工程名称。本例以“MFC_Dll_Test”为例进行演示。 ?单击“创建”按钮创建项目。

?

? 【第4步】在DLL类型中选择“使用共享MFC DLL的常规DLL”,然后单击“确定”按钮。

?

??【步骤5】?新建完成后,会弹出如下窗口。如下图所示,可以看到工程的头文件、源文件和资源文件。

?

??【第六步】打开工程的资源文件,在资源文件中插入一个对话框,并将对话框的ID更改为:IDD_DIALOGTEST。将文本控件添加到对话框并将 ID 更改为 IDC_STATICTEST。使用相同的方法添加两个按钮 ID:IDC_BUTTON1 和 IDC_BUTTON2。如下图所示,为上述三个控件添加变量,变量分别为:m_TextTest、m_ButtonTestOne、m_ButtonTestTwo。

【第七步】将对话框中要用到的类和头文件复制到“MFC_Dll_Test”MFC DLL工程所在文件夹中,如下图所示。该例程使用两个类,一个是“CMyButton”,另一个是“OwnerDrawStatic”类和一个头文件“MacroDefinition”。 CMyButton 类包含 CMyButton.h 头文件和 CMyButton.CPP 源文件。 OwnerDrawStatic 类包含 OwnerDrawStatic.h 头文件和 OwnerDrawStatic.CPP 源文件。 “MacroDefinition”是宏定义头文件。

?

?[步骤8]?在项目的解决方案管理器中将头文件“MacroDefinition”包含到项目中。如下所示。

??[步骤9]?在类向导中将“CMyButton”类和“OwnerDrawStatic”添加到项目中。如下所示。

?

??【步骤10】?完成以上操作后,为MFC_Dll_Test工程中插入的对话框IDD_DIALOGTEST添加一个类文件。该类文件名为“CMfcDellDlg”,并将新的类文件包含在“CMfcDellDlg.h”头文件中。添加“CMyButton.h”和“OwnerDrawStatic.h”两个类的头文件。如下所示。并添加三个控制变量m_TextTest、m_ButtonTestOne、m_ButtonTestTwo。这些类型分别替换为 CMyStatic 和 CMyButton。值得说明的是,CMyStatic类是一个继承类,继承自CStatic类,是CStatic类的重写。 CMyButton 类是一个继承类,它继承自 CButton 类,并且是 CMyButton 类的重写。

??

??[步骤11]?重载对话框类CMfcDellDlg.CPP中的OnInitDialog()和OnClose()函数,并在OnInitDialog函数中添加以下代码。添加自定义消息#define WM_SHUNTDOWN ? ?在“CMfcDellDlg.h”中WM_USER+100,并在OnClose()中添加以下代码。

#define WM_SHUNTDOWN WM_USER+100

void CMfcDellDlg::OnClose()

{

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

m_Parent->PostMessage(WM_SHUNTDOWN);

CDialog::OnClose();

}

BOOL CMfcDellDlg::OnInitDialog()

{

CDialog::OnInitDialog();

m_TextTest.SetWindowText(_T("Hello Word 这是 MFC Dell 测试演示"));

m_TextTest.SetTextFont(18, FALSE, TRUE, L"Times New Roman");

m_TextTest.m_SetBkColor(RGB(0, 0, 255));

m_TextTest.m_SetTextClr(RGB(243, 249, 241));

this->SetWindowText(_T("MFC_DLL_TEST_DEMO"));

m_ButtonTestOne.SetTextFont(CONTROLBUTTON_FONT_HEIGHT, FALSE, FALSE, CONTROLBUTTON_FACENAME);

m_ButtonTestTwo.SetTextFont(CONTROLBUTTON_FONT_HEIGHT, FALSE, FALSE, CONTROLBUTTON_FACENAME);

m_ButtonTestOne.SetWindowText(_T("TestOne"));

m_ButtonTestTwo.SetWindowText(_T("TestTwo"));

返回 TRUE;

}

??[步骤12]?在MFC DLL工程的头文件“MFC_Dll_Test.h”中添加全局函数ShowMfcDellDlg(),如下图所示。

extern "C" _declspec(dllexport) CMfcDellDlg* ShowMfcDellDlg(CWnd * pParent);

// MFC_Dll_Test.h:MFC_Dll_Test DLL 的主头文件

//

#include“CMfcDellDlg.h”

#pragma Once

#ifndef __AFXWIN_H__

#error“在包含此文件以生成 PCH 之前包含‘pch.h’”

#endif

#include "resource.h" // 主符号

//CMFCDllTestApp

//有关此类实现的信息,请参阅MFC_Dll_Test.cpp

//

类 CMFCDllTestApp :公共 CWinApp

{

公众:

CMFCDllTestApp();

//重写

公众:

虚拟 BOOL InitInstance();

DECLARE_MESSAGE_MAP()

virtual int ExitInstance();

};

extern "C" _declspec(dllexport) CMfcDellDlg* ShowMfcDellDlg(CWnd * pParent);

???【步骤13】在MFC DLL项目的源文件“MFC_Dll_Test.CPP”中实现步骤12中添加的函数ShowMfcDellDlg()。如下图所示,该例程生成的DLL中包含的对话框是使用非模态调用来调用的。模态调用和非模态调用有很大的区别。函数中“pParent”变量的作用主要是封装。 DLL中的对话框在被其他工程调用时可以获取主窗口的基类,并通过参数使用基类库中所有窗口类的基本功能。注意函数中的AFX_MANAGE_STATE(AfxGetStaticModuleState());

AFX_MANAGE_STATE(AfxGetStaticModuleState())?

AFX_MANAGE_STATE(AfxGetStaticModuleState());//用于切换模块时的状态保护,

?????????1.AfxGetStaticModuleState()指向当前模块状态;

?????????2.当前函数调用完成后自动恢复原模块状态;

?????????3.用于调用 DLL 中的 MFC 函数、类和资源时的模块状态切换

AFX_MANAGE_STATE 切换到指定的模块状态,并在超出范围时将模块状态恢复为其原始值。它在不同模块状态之间切换有两个原因:

?????????1.在不同 MFC DLL 和 MFC EXE 的模块状态之间切换并保持正确的 AFX_MODULE_STATE。最常见的问题是DLL输出的函数中无法获取DLL本身。资源,这是由于没有正确维护Module State造成的,因为Module State中保存的是当前Resource DLL的句柄。

?????????2.切换激活上下文。不同的Module必须有不同的Activation Context,需要进行切换。一般用法如下:

void SomeMFCDllFunction()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

…注意这里使用的是AfxGetStaticModuleState,而不是AfxGetModuleState。原因是在DLL项目中,AfxGetStaticModuleState返回DLL本身的Module State,而AfxGetModuleState返回与当前线程相关的Module State。由于DLL输出的函数一般是被其他Module调用的,大多数情况下当前线程State的Module State是错误的,所以必须使用DLL本身的Module State。

【CSDN中AFX_MANAGE_STATE介绍】

?????????动态链接到 MFC 的规则 DLL 是内部使用 MFC 的 DLL。此类 DLL 中的导出函数可以由 MFC 或非 MFC 可执行文件调用。顾名思义,此类DLL是使用MFC动态链接库版本(也称为MFC共享版本)构建的。函数通常通过标准 C 接口从常规 DLL 导出。

在动态链接到MFC的常规DLL中,必须在所有导出函数的开头添加AFX_MANAGE_STATE宏,以将当前模块的状态设置为DLL的状态。为此,请将以下代码行添加到从 DLL 导出的函数的开头:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

动态链接到 MFC 的规则 DLL 具有以下功能:

它是Visual C++ 4.0引入的新DLL类型。客户端可执行文件可以用任何支持使用 DLL 的语言(C、C++、Pascal、Visual Basic 等)编写;它不一定是 MFC 应用程序。与静态链接的常规 DLL 不同,这种类型的 DLL 动态链接到 MFC DLL(也称为共享 MFC DLL)。链接到此类 DLL 的 MFC 导入库与用于扩展 DLL 或使用 MFC DLL 的应用程序的 MFC 导入库相同:MFCxx(D).lib。

动态链接到 MFC 的规则 DLL 具有以下要求:

与动态链接到 MFC DLL 的可执行文件一样,这些 DLL 也是使用定义的 _AFXDLL 进行编译的。但它也像静态链接到 MFC 的常规 DLL 一样定义 _USRDLL。此类 DLL 必须实例化 CWinApp 派生类。这种类型的DLL使用MFC提供的DllMain。与在标准 MFC 应用程序中一样,将所有特定于 DLL 的初始化代码放在 InitInstance 成员函数中,并将终止代码放在 ExitInstance 中。

由于此类DLL使用MFC动态链接库版本,因此必须将当前模块的状态显式设置为DLL的状态。为此,请在从 DLL 导出的每个函数的开头使用 AFX_MANAGE_STATE 宏。

与 MFC 应用程序一样,规则 DLL 必须具有 CWinApp 派生类和该应用程序类的单个对象。但是,与应用程序的 CWinApp 对象不同,DLL 的 CWinApp 对象没有主消息泵。

请注意,CWinApp::Run 机制不适用于 DLL,因为应用程序拥有主消息泵。如果 DLL 生成无模式对话框或具有自己的主框架窗口,则应用程序的主消息泵必须调用从 DLL 导出的例程来调用 CWinApp::PreTranslateMessage。

与标准 MFC 应用程序中一样,将所有特定于 DLL 的初始化放入 CWinApp::InitInstance 成员函数中。在卸载 DLL 之前,从 MFC 提供的 DllMain 函数调用 CWinApp 派生类的 CWinApp::ExitInstance 成员函数。

共享 DLL 必须随应用程序一起分发:MFCx0.dll 和 Msvcr*0.dll(或类似文件)。

动态链接到 MFC 的 DLL 也不能静态链接到 MFC。与任何其他 DLL 一样,应用程序链接到动态链接到 MFC 的常规 DLL。

符号通常通过标准 C 接口从常规 DLL 导出。从常规 DLL 导出的函数的声明如下所示:

extern "C" __declspec(dllexport) MyExportedFunction( );

规则 DLL 内的所有内存分配都应在 DLL 内进行; DLL 不应向调用可执行文件传递或接收以下任何指针:

指向 MFC 对象的指针 指向 MFC 分配的内存的指针

如果需要执行上述任何操作,或者需要在调用可执行文件和 DLL 之间传递 MFC 派生对象,则必须生成扩展 DLL。

仅在创建数据副本后,才能安全地将指针传递到应用程序和 DLL 之间由 C 运行时库分配的内存。请确保不要删除或调整这些指针的大小,或者在不创建内存副本的情况下使用这些指针。

生成动态链接到MFC的规则DLL时,需要使用AFX_MANAGE_STATE宏来正确切换MFC模块状态。为此,请将以下代码行添加到从 DLL 导出的函数的开头:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

AFX_MANAGE_STATE 宏不应当用于静态链接到 MFC 的规则 DLL 中,也不应当用于扩展 DLL 中。有关更多信息,请参见管理 MFC 模块的状态数据。

extern "C" _declspec(dllexport) CMfcDellDlg * ShowMfcDellDlg(CWnd * pParent)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CMfcDellDlg* Dlg;

Dlg = new CMfcDellDlg(pParent);

Dlg->Create(IDD_DIALOGTEST, NULL);

Dlg->ShowWindow(SW_SHOWNORMAL);

Dlg->CenterWindow();

return Dlg;

}

????[第十四步]完成上述工作后编译“MFC_Dll_Test”项目,编译成功后在项目文件夹Release文件下会出现已经编辑完成的MFC_Dll_Test.DLL文件和MFC_Dll_Test.LIB文件.如下图所示。

?

? ?[第十五步]新建一个MFC对话框工程,本文已上一讲中建立好的项目“MFCApplication1Dlg”为例子进行说明。新建MFC对话框类工程(MFCApplication1)用于测试封装完成的类是否正确,能否正常使用。在工程文件下新建“Cbinclude”文件夹用于存放DLL库文件的MFC_Dll_Test.h头文件,新建“Cblib”文件夹用于存放MFC_Dll_Test.Lib库文件。将“MFC_Dll_Test.dll”文件分别放入项目文件夹下的"Debug"和"Release"文件夹下。如下图所示。

[第十六]在项目属性中打开“MFCApplication1”项目属性对话框如下图所示。

?

?[第十七步]在项目属性中打开“MFCApplication1”项目属性对话框如下图所示。选中“C/C++”属性,在右边的“附加包含目录”中选择“Cbinlude”文件夹所在的目录,并在"连接器"选项中配置"Cblib"库文件所在的目录,具体配置方式可以参考上一讲的类容这里不在描述。

?

?[第十八步]配置完成后将“MFC_Dll_Test”MFC DLL项目中生成的“MFC_Dll_Test.lib”文件复制到“MFCApplication1”项目文件夹下的“Cblib”文件夹中。将“MFC_Dll_Test.dll”文件复制到“MFCApplication1”项目文件夹下的“Debug”和“Release”文件夹中。将所有要用到的头文件复制到“MFCApplication1”项目文件夹下的“Cbinlude”文件夹中。如下图所示。

?

??[第十九步]在项目“MFCApplication1Dlg.h”头文件中包含"CMyStatic.h"头文件和 "MFC_Dll_Test.h"。在类向导中为对话框添加自定义消息处理函数。如下图所示。并在消息处理函数中增加如下代码。并重载"OnClose()"函数,并增加响应代码。

?

afx_msg LRESULT CMFCApplication1Dlg::OnShuntdown(WPARAM wParam, LPARAM lParam)

{

if (NULL!= m_Dlg)

{

delete m_Dlg;

m_Dlg = NULL;

}

if (NULL != hDll)

{

FreeLibrary(hDll);

hDll = NULL;

}

return 0;

}

oid CMFCApplication1Dlg::OnClose()

{

if (NULL!= m_Dlg)

{

delete m_Dlg;

m_Dlg = NULL;

}

if (NULL!= hDll)

{

FreeLibrary(hDll);

hDll = NULL;

}

CDialogEx::OnClose();

}

[第二十步]在项目对话框中添加一个按钮。将按钮的ID改为“IDC_BUTTONTESTMFCDELL”

并为按钮增加处理函数。

?

[第二十一步]在"MFCApplication1Dlg.h"头文件中包含“MFC_Dll_Test.h”并在其中定义两个变量CMfcDellDlg* m_Dlg;HINSTANCE hDll;??

// MFCApplication1Dlg.h: 头文件

#include"CMyStatic.h"

#include"MFC_Dll_Test.h"

#pragma once

// CMFCApplication1Dlg 对话框

class CMFCApplication1Dlg : public CDialogEx

{

// 构造

public:

CMFCApplication1Dlg(CWnd* pParent = nullptr); // 标准构造函数

// 对话框数据

#ifdef AFX_DESIGN_TIME

enum { IDD = IDD_MFCAPPLICATION1_DIALOG };

#endif

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

// 实现

protected:

HICON m_hIcon;

// 生成的消息映射函数

virtual BOOL OnInitDialog();

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnBnClickedOk();

CMfcDellDlg* m_Dlg;

HINSTANCE hDll; //DLL句柄

//CMyStatic m_statictext;

CMyStatic m_idcttt;

afx_msg void OnBnClickedButtontestmfcdell();

afx_msg void OnClose();

protected:

afx_msg LRESULT OnShuntdown(WPARAM wParam, LPARAM lParam);

};

?[第二十二步]在"MFCApplication1Dlg.CPP"中按钮事件函数中增加调用MFC DLL库的功能函数。

void CMFCApplication1Dlg::OnBnClickedButtontestmfcdell()

{

hDll = LoadLibrary(_T("MFC_Dll_Test.dll")); //加载dll

if (NULL == hDll)

{

MessageBox(_T("MFC_Dll_Test动态链接库加载失败!"));

return;

}

typedef CMfcDellDlg* (*lpCallDell)(CWnd*);

lpCallDell MfcDlg = (lpCallDell)GetProcAddress(hDll,"ShowMfcDellDlg");

if (MfcDlg==NULL)

{

FreeLibrary(hDll);

MessageBox(_T("ShowMfcDellDlg函数加载失败!"));

return;

}

if (NULL==m_Dlg)

{

m_Dlg = MfcDlg(this);

}

else if (NULL!= m_Dlg)

{

MessageBox(_T("对话框已成功加载,请关闭后再试!"),_T("信息提示"),MB_ICONINFORMATION|MB_OKCANCEL);

return;

}

}

?如果不对hDll进行判断的话每点击一次按钮就会调用一次MFC DLL导致出现多个窗口。如下图所示。

?进行判断后对话框关闭后会向调用窗口发送“WM_SHUNTDOWN”以便主窗口进行资源释放。更改之后更加灵活。运行效果如下图所示。

?

?????????详细例程已上传到资源,可供参考。DLL是一个相当复杂的系统工程只能在碎片中慢慢学习和积累,下一篇将会对win32 dll封装的步骤进行详细的研究。

https://www.sychzs.cn/download/lzc881012/86400621https://www.sychzs.cn/download/lzc881012/86400621

在上一节中讲的是VS2010的菜单资源,本节主要讲菜单及CMenu类的使用。

在内容开始前为大家介绍一款MFC界面开发控件:

Xtreme Toolkit Pro:是屡获殊荣的VC界面库,是MFC开发中最全面界面控件套包,它提供了Windows开发所需要的11种主流的Visual C++ MFC控件,包括Command Bars、Controls、Chart Pro、Calendar、Docking Pane、Property Grid、Report Control、Shortcut Bar、Syntax Edit、Skin Framework 和Task Panel。

点击下载Xtreme Toolkit Pro最新试用版

CMenu类的主要成员函数

MFC为菜单的操作提供了CMenu类,下面就常用的几个成员函数进行简单的介绍。

BOOL LoadMenu(UINT nIDResource);

加载菜单资源,并将其附加到CMenu对象上。参数nIDResource指定了要加载的菜单资源的ID。如果菜单加载成功则返回TRUE,否则返回FALSE。

BOOL DeleteMenu(UINT nPosition,UINT nFlags);

在菜单中删除一个菜单项。参数nPosition指定要删除的菜单项。参数nFlags就用来解释nPosition的意义,为MF_BYCOMMAND时说明nPosition表示菜单项的ID,为MF_BYPOSITION时说明nPosition表示菜单项的位置,第一个菜单项的位置为0。如果删除菜单项成功则返回TRUE,否则返回FALSE。

BOOL TrackPopupMenu(UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect = 0);

用来在指定位置显示一个浮动的弹出式菜单。参数nFlags指定屏幕坐标和鼠标位置的标志,可以是以下取值:

TPM_CENTERALIGN:菜单在水平方向上相对于参数x指定的坐标值居中显示 TPM_LEFTALIGN:菜单的左侧与参数x指定的坐标值对齐 TPM_RIGHTALIGN:菜单的右侧与参数x指定的坐标值对齐 TPM_BOTTOMALIGN:菜单的底部与参数y指定的坐标值对齐 TPM_TOPALIGN:菜单项的顶部与参数y指定的坐标值对齐 TPM_VCENTERALIGN:菜单在垂直方向上相对于参数y指定的坐标值居中显示

这里先介绍这几个比较常用的,其他可参见MSDN。参数x指定弹出式菜单的水平方向的屏幕坐标,参数y指定菜单顶部垂直方向上的屏幕坐标,参数pWnd指明哪个窗口拥有此弹出式菜单,不能为NULL,参数lpRect忽略。

UINT CheckMenuItem(UINT nIDCheckItem,UINT nCheck);

在弹出菜单中为菜单项增加选中标记或移除选中标记。参数nIDCheckItem指定要选中或取消选中的菜单项。参数nCheck指定菜单项的选中状态和如何根据nIDCheckItem确定菜单项的位置,可以是MF_CHECKED或MF_UNCHECKED与MF_BYPOSITION或MF_BYCOMMAND的组合,这几个标志的含义如下:

MF_BYCOMMAND:为默认值。说明参数nIDCheckItem表示菜单项的ID MF_BYPOSITION:说明参数nIDCheckItem表示菜单项的位置,第一个菜单项的位置是0 MF_CHECKED:为菜单项添加选中标记 MF_UNCHECKED:为菜单项移除选中标记

该函数返回菜单项之前的状态:MF_CHECKED或MF_UNCHECKED, 如果菜单项不存在则返回0xFFFFFFFF。

UINT EnableMenuItem(UINT nIDEnableItem,UINT nEnable);

激活、禁用菜单项或使其变灰。参数nIDEnableItem指定要激活、禁用或变灰的菜单项。参数nEnable指定操作的类型,可以是MF_DISABLED、MF_ENABLED或MF_GRAYED与MF_BYCOMMAND或MF_BYPOSITION的组合,这些值的含义如下:

MF_BYCOMMAND:同CheckMenuItem MF_BYPOSITION:同CheckMenuItem MF_DISABLED:禁用菜单项,使其不能被选择但不变灰 MF_ENABLED:激活菜单项,使其能够被选择并由变灰状态恢复 MF_GRAYED:禁用菜单项,使其不能被选择并变灰

该函数返回菜单项之前的状态:MF_DISABLED、MF_ENABLED或MF_GRAYED

CMenu* GetSubMenu(int nPos) const;

获取弹出菜单的CMenu对象。参数nPos指定弹出菜单在菜单中的位置,不能使用ID。返回值是CMenu对象的指针,该CMenu对象的m_hMenu成员为由nPos指定的弹出菜单的句柄,如果不存在这样的CMenu对象则返回NULL,然后创建一个临时弹出菜单。

CMenu类的成员函数先讲这些,如果大家需要用其他的函数可以到MSDN中查看,解释的很清楚。

菜单消息

菜单主要能发送两种消息:COMMAND消息和UPDATE_COMMAND_UI消息。下面分别讲解:

COMMAND消息:在菜单项被点击时发送该消息。

UPDATE_COMMAND_UI消息:用来维护菜单项的各项状态,包括激活、禁用、变灰、选中、未选中等。在下拉菜单每次打开的时候,所有菜单项的此消息都会被发送出去。如果所属类中为菜单项的该消息添加了处理函数,则执行相应函数更新菜单状态,如果菜单项没有此消息处理函数,也没有COMMAND消息的处理函数,那么它就会变灰。

菜单的应用实例

鸡啄米先讲一下本实例要实现的功能,此实例是在上一节创建的单文档工程Example34的基础上完成的,上一节中为主菜单栏添加了“Tools”菜单项,并设置它的第一个子菜单项为“Draw”,另外我们还要为主菜单栏添加“Settings”项,然后为其添加一个子菜单项“Draw Enable”,我们通过“Draw Enable”菜单项的选中状态控制菜单项“Draw”的激活状态,如果“Draw Enable”菜单项选中,则“Draw”菜单项激活,点击它弹出一个MessageBox,否则“Draw”菜单项禁用。程序中已经在Example34View类中自动生成了OnRButtonUp(UINT /* nFlags */, CPoint point)函数,并在其中实现了弹出右键菜单的功能,这里鸡啄米用CMenu类的TrackPopupMenu成员函数重新做一遍。

注意:Example34的CMainFrame类中定义的菜单并没有使用常用的CMenu类,而是用的CMFCMenuBar类(自VS2008起提供),但菜单的消息处理函数的添加是相同的。

下面是具体步骤:

1. 打开Example34工程的IDR_MAINFRAME菜单资源,在“Help”菜单项前通过“Insert New”操作插入一个菜单项,Caption设为“Settings”,在新菜单项的子菜单中再添加一个菜单项,Caption设为“Draw Enable”,ID默认为ID_SETTINGS_DRAWENABLE。

2. 因为此菜单为CMainFrame所拥有,所以我们在CMainFrame类中对菜单进行操作。在“MainFrm.h”中为CMainFrame类添加成员变量bool m_bDraw,以标识当前是否可以点击Tools->Draw菜单项,并在CMainFrame类的构造函数中为m_bDraw初始化:m_bDraw = TRUE。

3. 为菜单项Tools->Draw的COMMAND消息和UPDATE_COMMAND_UI消息分别添加处理函数CMainFrame::OnToolsDraw()和OnUpdateToolsDraw(CCmdUI *pCmdUI),这里要注意,添加处理函数时class list中应选择CMainFrame,修改两个函数的实现为:

?

C++代码

void CMainFrame::OnToolsDraw()

{

// TODO: Add your command handler code here

// 弹出提示框

MessageBox(_T("Draw"));

}

void CMainFrame::OnUpdateToolsDraw(CCmdUI *pCmdUI)

{

// TODO: Add your command update UI handler code here

// 根据m_bDraw的值设置是否激活

pCmdUI->Enable(m_bDraw);

}

4. 为菜单项Settings->Draw Enable的COMMAND消息和UPDATE_COMMAND_UI消息分别添加处理函数CMainFrame::OnSettingsDrawenable()和OnUpdateSettingsDrawenable(CCmdUI *pCmdUI),并将它们的实现修改为:

?

C++代码

void CMainFrame::OnSettingsDrawenable()

{

// TODO: Add your command handler code here

// 绘图使能标识取反

m_bDraw = !m_bDraw;

}

void CMainFrame::OnUpdateSettingsDrawenable(CCmdUI *pCmdUI)

{

// TODO: Add your command update UI handler code here

// 根据m_bDraw的值设置是否选中

pCmdUI->SetCheck(m_bDraw);

}

5. 运行程序,效果图如下:

6. 接下来我们要重新实现右键菜单。大家以后可以仿照VS2010自动生成的代码实现右键菜单,也可以用鸡啄米下面讲到的方法。首先将CExample34View::OnRButtonUp(UINT /* nFlags */, CPoint point)函数内的代码都注释掉,保证原来的弹出方法失效。

7. 自动生成代码是在鼠标弹起时实现的右键菜单,我们这里改为在鼠标按下时就弹出右键菜单。在class view类视图中点击CExample34View,然后在属性页的messages列表中找到WM_RBUTTONDOWN,添加其消息响应函数CExample34View::OnRButtonDown(UINT nFlags,CPoint point),修改其实现为:

C++代码

void?CExample34View::OnRButtonDown(UINT?nFlags,?CPoint?point)

{

//?TODO:?Add?your?message?handler?code?here?and/or?call?default

CMenu?menu;???????//?菜单(包含主菜单栏和子菜单)

CMenu?*pSubMenu;??//?右键菜单

//?加载菜单资源到menu对象

menu.LoadMenu(IDR_POPUP_EDIT);

//?因为右键菜单是弹出式菜单,不包含主菜单栏,所以取子菜单

pSubMenu?=?menu.GetSubMenu(0);

//?将坐标值由客户坐标转换为屏幕坐标

ClientToScreen(&point);

//?弹出右键菜单,菜单左侧与point.x坐标值对齐

pSubMenu->TrackPopupMenu(TPM_LEFTALIGN,?point.x,?point.y,?this);

CView::OnRButtonDown(nFlags,?point);

}

8. 最终的右键菜单效果:

本节内容不少,大家可以慢慢消化。菜单的讲解就到这里了。

本文转载自:鸡啄米

?

相关文章