文章目录
一、基于对话框的MFC程序
1.创建工程
这里我用的是VS2015专业版,首先应该保证你的IDE安装了MFC相关组件。
接着创建一个MFC工程
vs会在你完成创建之前先提供大量的选项,包括创建的文件,窗口样式等等,应用程序类型这里我们选择“基于对话框”。
可以看到生成的类。
其他步骤都不做更改直接使用默认即可。
创建成功。
2.根据生成的代码进行分析
vs已经给我们生成了不少的代码,这些代码很详尽,但是也有一些冗余,好在它们都有详细的注释,让我可以看懂它们各个部分是起什么作用的。
打开readme.txt文件,可以得知各个文件的作用。
打开stdafx.h,查看包含的头文件,可以得知一个MFC程序用到了哪些库文件。
#include <afxwin.h> // MFC 核心组件和标准组件
#include <afxext.h> // MFC 扩展
#include <afxdisp.h> // MFC 自动化类
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT
#include <afxcontrolbars.h> // 功能区和控件条的 MFC 支持
这里我们可以看到#include <afxwin.h> // MFC 核心组件和标准组件,想要实现一个最基本的MFC,仅需要包含这一个头文件。
参考其他博文,我总结了MFC执行流程:
全局对象的定义
CMFCApplication1App theApp;
构造
CMFCApplication1App::CMFCApplication1App()
{
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
进入winmain
在VS中看到这个MFC程序连一个main函数都没有,这很反常识,其实它并非没有,只是隐藏在目录里,不用我们自己定义,但是为了搞清楚流程,先要把程序所依赖的源文件找到。程序继续执行进入main函数;MFC的main函数在appmodul.cpp文件当中,而这个文件与winmain.cpp都在
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc
这个目录下
找到main函数如下
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
程序会进入tWinMain,转而执行AfxWinMain。AfxWinMain函数在文件winmain.cpp当中。往下拉找到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;
}
初始化:注册窗口,产生窗口,更新窗口,消息循环
pThread->InitInstance() (由于InitInstance是虚函数,所以这次调用的是派生类的InitInstance()函数)
BOOL CMFCApplication1App::InitInstance()
对于MFC程序,MainFrame,View,ToolBar,Controlbar等都是窗口,所以下面的窗口注册与创建、显示等要反复调用多次,一次对应一个窗口
(1) 注册窗口类
AfxEndDeferRegisterClass()(相当于SDK里面的RegisterClass()函数)
(2)创建窗口
CMainFrame::PreCreateWindow()
CFrameWnd::Create()
(3)显示和更新窗口
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口
m_pMainWnd->UpdateWindow();//更新窗口
(4)消息循环
PumpMessage()
至此整个MFC程序运行流程结束
运行成功
二、win32工程写一个最简单的MFC程序
既然很多文件我们用不上,但是MFC工程都帮我们包含了,那么我们也可以建一个win32工程,调用MFC,实现一个简单的MFC程序。
1、工程创建
这里并没有什么好说的,工程名为win32project2
2、头文件
首先要把stdafx.h中的#include<windows.h>删除,因为我们需要#include<afxwin.h>,而后者已包含前者,保留前者后面会报错。
然后在win32project2.h头文件中写入以下内容
#define _AFXDLL
#include <afxwin.h>
class MyApp :public CWinApp
{
public:
//程序入口
virtual BOOL InitInstance();
};
class MyFrame :public CFrameWnd /
{
public:
MyFrame();
};
3、源文件
将win32project2.cpp中自动生成的代码除了include全部删除,然后写入以下内容
#include "stdafx.h"
#include "Win32Project2.h"
MyApp app; //全局应用程序对象,有且仅有一个
BOOL MyApp::InitInstance()
{
MyFrame *frame = new MyFrame;
//显示和更新
frame->ShowWindow(SW_SHOWDEFAULT);
frame->UpdateWindow();
m_pMainWnd = frame;
return TRUE;
}
MyFrame::MyFrame()
{
Create(NULL, TEXT("哈哈哈"));
}
4、在共享DLL中使用MFC
右键项目名->属性->配置属性->常规->项目默认值->MFC的使用->更改为“在共享DLL中使用MFC”
5、故障排除
在一切准备就绪后,报如下错误
#error: Building MFC application with /MD[d] (CRT dll version)
requires MFC…
经过多方查找和尝试,发现
项目->属性->C/C+±>代码生成->运行库,将“多线程调试DLL(/MDd)”改成“多线程(/MT)”或“多线程调试(/MTd)”,可以解决。
运行成功。
6、后记
在不选择MFC工程的情况下,按照运行流程,定义了全局变量,定义了theapp类和窗口框架类,然后在主文件写了窗口的创建、显示和更新流程,就完成了一个MFC程序,没有冗余的包含文件,是一次不错的尝试。
三、搭建可供命令行工具编译链接的环境
可能遇到的ERROR
用命令行工具进行编译即使用vs提供的cl.exe和Link.exe这两个工具进行编译和链接。在经过很多次试错和查找资料后发现,当报错为没有cl.exe这个命令,就要在环境变量里加上vs中cl.exe所在的那个目录。我这里是
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin;
记得在末尾加上英文分号。
当报错为
fatal error cxxx: xxxxxx.h:不包括路径
就要去找vs目录下包含这个程序要用到的头文件,然后将其所在目录包含到系统变量中。
当报错为
fatal error LINKxxxx: 无法打开文件“xxxx.lib”
就要去找vs目录下包含这个程序要用到的库文件,然后将其所在目录包含到系统变量中。
INCLUDE
如果你是win10,直接搜索“高级系统设置”,就可以打开如下界面
选择环境变量在系统变量中新建INCLUDE(注意大写)
因为我们这里着手编译一个win32程序和一个MFC程序,所以要把这两个程序所需头文件路径都包含进去。
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include;
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared;
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt;
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um;
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\winrt;
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include;
LIB
同样的新建LIB,包含以下路径
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib;
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\ucrt\x86;
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x86;
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\lib;
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\lib\amd64;
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\lib\arm;
至此环境算是搭建好了
四、命令行工具编译
1、windows API程序编译
程序源码见我之前发布的博客
win+R键->输入cmd,打开终端
cd命令,打开要编译的程序源文件所在的工作目录
cl.exe /c 文件名 ,只编译不连接
编译成功就可以在工作目录下找到main.obj文件
链接命令Link.exe main.obj 这里报了一堆错误
当时很疑惑,猜想还是库的问题,遂查找资料,发现
1、kernel32.dll
提供了线程、进程、内存管理等核心的API
2、user32.dll
提供了窗口、消息等API
3、gdi32.dll
提供了绘图的API
这三个vc常用的库,然后在链接命令后面加上user32.lib gdi32.lib。
没有问题
可想而知工作目录下生成了main.exe文件,我们直接运行
点击鼠标的效果
运行成功。
2、MFC程序的编译
这里编译的MFC程序就是本篇博客记录的
同样的步骤,同样的命令先编译不连接。
虽然有警告,但是编译成功了
同样生成了.obj文件
再进行链接
出错了,这个错误我一开始觉得匪夷所思,因为它并不是说打不开某某,无法定义某某,说明不是库文件的问题。没有定义入口点,就是说没有main函数,但是上文我探究过了,MFCC程序就是没有main函数啊,但是它的main在vs目录下,在IDE里,能够自动调用,所以之前在IDE里运行这个程序并没有出错。既然这样,将隐藏在目录中自动调用的函数写进MFCApplication1.cpp里。
即是我在上文“基于对话框的MFC程序”中摘录的appmodul.cpp和winmain.cpp中的函数入口重新定义在主文件的后面。
问题解决,在工作目录下输入MFCApplication1.exe,运行。
运行成功。
五、总结
这次工作完全是摸着石头过河,甚至一开始我不知道我的vs IDE上没有安装MFC模块,导致我因此摸索了好久。其中碰到不少问题,但是我也因此搞懂了MFC的运行流程,了解了VS目录下的那些头文件,动态库。通过用命令行工具cl.exe、Link.exe的使用,了解了VS帮我们做了哪些工作,搞懂了很多以前没有关注的问题。踩了很多坑,多亏了网上的教程还有一些博主的分享,文末会附上链接。