目录
(如有错误,请大佬指出、更正)
一、实验目的
1.完成一个简单的MFC程序,通过此来了解MFC的对象类和程序执行流程。
2.运用VC6++或者Virtual Studio的cl.exe和link.exe来编译和连接Windows API程序和MFC程序。
二、实验配置
Virtual Studio 2015和Windows10操作系统
三、MFC程序创建和解析
(一)、创建MFC程序
打开VS2015,点击新建工程,然后如下图操作。
我选择了基于对话框的程序。
选择生成类(对本实验基本没影响)
其余的都点击下一步。
创建完成后,不做任何修改,点击就可已运行。
VS提供了一个基于对话窗的MFC程序,比较方便。
(二)、分析MFC程序
MFC的所有类都继承自CObject,下图是MFC类结构层次。
MFC大致执行是按照:注册窗口-》产生窗口-》更新窗口-》消息循环。从注册窗口函数开始直至消息循环函数结束。MFC具体执行流程
主函数代码为:
其中,我添加了两个部分。它们分别来自appmodul.cpp和winmain.cpp。这两个文件在:安装目录\VC\atlmfc\src\mfc。
VS2015将这两个文件和其它文件封装在一起,对外部是隐藏的。在VS内执行程序时,会自动将这两个文件中的函数进行调用。
注:不加入这些函数,后面使用控制台编译链接MFC程序会出现找不到程序入口的错误
// MFC_test2.cpp : 定义应用程序的类行为。
//
#include "stdafx.h"
#include "MFC_test2.h"
#include "MFC_test2Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//来自appmodul.cpp
extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow);
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
//来自winmain.cpp
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
// CMFC_test2App,自己创建的工程cpp
BEGIN_MESSAGE_MAP(CMFC_test2App, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CMFC_test2App 构造
CMFC_test2App::CMFC_test2App()
{
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CMFC_test2App 对象
CMFC_test2App theApp;
// CMFC_test2App 初始化
BOOL CMFC_test2App::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
CShellManager *pShellManager = new CShellManager;
// 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
CMFC_test2Dlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
}
// 删除上面创建的 shell 管理器。
if (pShellManager != NULL)
{
delete pShellManager;
}
#ifndef _AFXDLL
ControlBarCleanUp();
#endif
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
VS还有每个函数的使用解析。在ReadMe.txt文件里面。
下面展示部分内容。
================================================================================
MICROSOFT 基础类库 : MFC_test2 项目概述
===============================================================================
应用程序向导已为您创建了此 MFC_test2 应用程序。此应用程序不仅演示 Microsoft 基础类的基本使用方法,还可作为您编写应用程序的起点。
本文件概要介绍组成 MFC_test2 应用程序的每个文件的内容。
MFC_test2.vcxproj
这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
MFC_test2.vcxproj.filters
这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。
MFC_test2.h
这是应用程序的主头文件。
其中包括其他项目特定的标头(包括 Resource.h),并声明 CMFC_test2App 应用程序类。
四、使用cl.exe和link.exe
cl是编译工具,而link是链接工具。程序在编译完之后,需要链接各种文件才能形成最终的可执行文件。
(一)、Windows API
1.实验代码
2.实验步骤
(1)环境配置
右键点击此电脑——》左键点击属性——》
——》
——》新建时,变量值用 ;(英文分号)隔开。
我的INCLUDE环境,每台电脑环境会不一样。
//这里包含了后面要用的环境
E:\VS2015\VC\include
E:\VS2015\VC\atlmfc\include
C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\shared
C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\ucrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\um
C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt
LIB环境
//前3个Windows API需要
E:\VS2015\VC\lib
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\um\x86
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\ucrt\x86
//后3个MFC需要,编译或者链接API出错,可以先将后3个删除再试
E:\VS2015\VC\atlmfc\lib
E:\VS2015\VC\atlmfc\lib\amd64
E:\VS2015\VC\atlmfc\lib\arm
按下win+R——》输入cmd——》进入控制台输入cl。如下所示,则环境配置完成。
(2)编译
用cl命令编译main.cpp
会生成.obj文件
(3)链接
使用link命令链接main.obj
为防止出现下面错误,需要添加另外的库user32.lib gdi32.lib
成功后会出现main.exe
(二)、MFC
1.实验代码
在上文分析MFC程序的主函数。
2.实验步骤
(1)环境配置
在上文Windows API环境配置。
(2)编译
还是用cl命令分别编译MFC_test2.cpp和MFC_test2Dlg.cpp
生成.obj文件
(3)链接
使用link命令链接MFC_test2.obj和MFC_test2Dlg
生成MFC_test2.exe
五、总结和部分错误解决
我尝试将appmodul.cpp和winmain.cpp编译后加入到原MFC_test2.cpp(代码中未加入appmodul.cpp和winmain.cpp的函数)中,但是会缺少文件。由此可见,VS是将一些文件一起封装的,运行出来的总的程序会比使用cl和link的大。
最容易出错的是环境配置部分。
这种错误需要检查环境中的类型是否一致(或与计算机一致)。
如果是找不到或者打不开文件,就要在环境变量里面添加此文件。
- fatal error cxxx: xxxxxx.h:不包括路径
- fatal error LINKxxxx: 无法打开文件“xxxx.lib”
六、参考资料
1.如何使用cl.exe和linker.exe编译链接Windows API程序和MFC程序 (记得点赞)
2.完成一个MFC程序并进行分析;用VS命令行工具编译链接 (这个贼详细)