01 对话框
普通窗口和对话框窗口:
- 对话框的分类
模式对话框 - 当对话框显示时,会禁止其他窗口和用户交互操作。
无模式对话框 -在对画框显示后,其他窗口仍然可以和用户交互操作。 - 对话框基本使用
1.对话框窗口处理函数
2.注册窗口类(不使用)
3.创建对话框
4.对话框的关闭 - 对话框窗口处理函数(并非真正的对话框窗口处理函数)
INT CALLBACK DialogProc(
HWND hwndDlg, //窗口句柄
UINT uMsg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam, //消息参数
);
返回TRUE -缺省处理函数不需要处理。
返回FALSE -交给缺省处理函数处理。
不需要调用缺省对话框处理函数。(系统自带)
1.模式对话框
-
创建对话框
INT DialogBox(
HINSTANCE hInstance, //应用程序实例句柄
LPCTSTR lpTemplate, //对话框资源ID
HWND hWndParent, //对话框父窗口
DLGPROC lpDialogFunc //自定义函数
);
DialogBox是一个阻塞函数,只有当对话框关闭后,才会返回,继续执行后续代码。
返回值是通过EndDialog设置。 -
对话框的关闭
BOOL EndDialog(
HWND hDlg, //关闭的对话框窗口句柄
INT_PTR nResult //关闭的返回值
);
关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow等函数(只完成了EndDialog函数第一步关闭,第二部解除阻塞没做)。nResult是DialogBox函数退出时的返回值。 -
对话框的消息
WM_INITDIALOG -对话框创建之后显示之前,通过对户口窗口处理函数,可以完成自己的初始化相关的操作。
步骤1:
创建menu
步骤2:
创建对话框
#include<Windows.h>
#include<stdio.h>
#include"resource.h"
HINSTANCE g_hIns = 0;
INT CALLBACK DlgProc(HWND hhWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_INITDIALOG:
MessageBox(hhWnd, "WM_INITDIALOG", "Info", MB_OK);
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE)
{
//销毁模式对话框
EndDialog(hhWnd, 100);
}
}
return FALSE;//将消息交给真正的对话框窗口处理函数的后续代码帮我们处理
}
void OnCommand(HWND hWnd, WPARAM wParam)
{
switch (LOWORD(wParam))
{
case ID_MODEL:
DialogBox(g_hIns, (char*)IDD_DIALOG1, hWnd, DlgProc);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_COMMAND:
OnCommand(hWnd, wParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使用GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
g_hIns = hIns;
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = (char*)IDR_MENU1;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
//在内存创建窗口
HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
2.无模式对话框
-
创建对话框
HWND CreateDialog(
HINSTANCE hInstance, //应用程序实例句柄
LPCTSTR lpTemplate, //模板资源ID
HWND hWndParent, //父窗口
DLGPROC lpDialogFunc //自定义函数
);
非阻塞函数,创建成功返回窗口句柄,需要使用ShowWindow函数显示对话框。 -
对话框的关闭
关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框。
02 静态库
1.静态库特点
- 运行不存在。
- 静态库源码被链接到调用程序中。
- 目标程序的归档。
2.C语言静态库
- C静态库的使用
1)创建一个静态库项目。
2)添加库程序,源文件使用C文件。
步骤1:
步骤2:
步骤3:
Clib.c:
int Clib_add(int add1, int add2)
{
return add1 + add2;
}
int Clib_sub(int sub1, int sub2)
{
return sub1 - sub2;
}
步骤4:
编译:右键->生成
- C静态库的使用
库路径设置:可以使用pragma关键字设置
#pragma comment(lib,“/lib/clib.lib”)
步骤5:
新建主函数
UseClib.c:
#include"stdio.h"
#pragma comment(lib,"../x64/Debug/Clib.lib") //通知链接器到哪抓源码
int main()
{
int sum, sub;
sum = Clib_add(5, 3);
sub = Clib_sub(5, 3);
printf("sum=%d,sub=%d\n", sum, sub);
return 0;
}
3.C++语言静态库
- C++静态库的创建
1)创建一个静态库项目
2)添加库程序,源文件使用CPP文件。
CPPlib.cpp:
int CPPlib_add(int add1, int add2)
{
return add1 + add2;
}
int CPPlib_sub(int sub1, int sub2)
{
return sub1 - sub2;
}
- C++静态库的使用
库路径设置:可以使用pragma关键字设置
#pragma comment(lib,“…/lib/cpplib.lib”)
UseCPPlib.cpp:
#include<iostream>
using namespace std;
int CPPlib_add(int add1, int add2);
int CPPlib_sub(int sub1, int sub2);
//以上代码给编译器看的
#pragma comment(lib,"../x64/Debug/CPPClib.lib")//?Clib_add@@YAHHH@Z
//给链接器看的
extern "C" int Clib_add(int add1, int add2);
extern "C" int Clib_sub(int sub1, int sub2);
#pragma comment(lib,"../x64/Debug/Clib.lib")//Clib_add /Clib_sub
int main()
{
int sum = CPPlib_add(5, 4);//?Clib_add@@YAHHH@Z
int stub = CPPlib_sub(5, 4);//?Clib_sub@@YAHHH@Z
cout << "sum=" << sum << ",sub=" << stub << endl;
sum = Clib_add(5, 3);//Clib_add
stub = Clib_sub(5, 3);//Clib_sub
cout << "sum=" << sum << ",sub=" << stub << endl;
}
03 动态库
1.动态库特点
- 动态库特点
1)运行时独立存在
2)源码不会链接到执行程序
3)使用时加载(使用动态库必须使动态库执行) - 与静态库的比较
1)由于静态库是将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所以代码体积会增大。动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小。
2)静态库发生变化后,新的代码需要重新链接嵌入到执行程序成。动态库发生变化后,如果库中函数的定义(或地址)未变化,其他使用DLL的程序不需重新链接。
2.动态库创建
- 创建动态库项目
- 添加库程序
- 库程序导出 -提供给使用者库中的函数等信息。
1)声明导出:使用_declspec(dllexport)导出函数。(会更改函数名称)
注意:动态库编译链接后,也会有LIB文件,是作为动态库函数映射使用,与静态库不完全相同。
2)模块定义文件.def (不会更改函数名称)
例如:LIBRARY DLLFunc //库
EXPORTS //库导出表
DLL_MUL @1 //导出的函数
步骤1:
步骤2:
步骤2:
CPPdll.cpp
int CPPdll_add(int add1, int add2)
{
return add1 + add2;
}
int CPPdll_sub(int sub1, int sub2)
{
return sub1 - sub2;
}
int CPPdll_mul(int mul1, int mul2)
{
return mul1 * mul2;
}
步骤3:
声明导出:
_declspec(dllexport)int CPPdll_add(int add1, int add2)
{
return add1 + add2;
}
_declspec(dllexport)int CPPdll_sub(int sub1, int sub2)
{
return sub1 - sub2;
}
_declspec(dllexport)int CPPdll_mul(int mul1, int mul2)
{
return mul1 * mul2;
}
同步会生成lib
3.动态库的使用
1.隐式链接(操作系统负责使动态库执行)
1)头文件和函数原型
可以在函数原型的声明前,增加_declspec(dllimport)
2)导入动态库的LIB文件
3)在程序中使用函数
4)隐式链接的情况,dll文件可以存放的路径:
(1)与执行文件中同一个目录下
(2)当前工作目录
(3)Windows目录
(4)Windows/System32目录
(5)Windows/System
(6) 环境变量PATH指定目录
UseCPPdll.cpp:
#include<iostream>
using namespace std;
_declspec(dllimport)int CPPdll_add(int add1, int add2);
_declspec(dllimport)int CPPdll_sub(int sub1, int sub2);
_declspec(dllimport)int CPPdll_mul(int mul1, int mul2);
#pragma comment(lib,"../x64/Debug/CPPdll.lib")
//通知链接器到哪抓编号和dll文件名("CPPdll.lib")
int main()
{
int sum = CPPdll_add(5, 4);//0 "CPPdll.lib"
int sub = CPPdll_sub(5, 4);//1 "CPPdll.lib"
int mul = CPPdll_mul(5, 4);//2 "CPPdll.lib"
cout << "sum=" << sum << ",sub=" << sub << ",mul" << mul << endl;
}
2.显式链接(程序员自己负责使动态库执行)
1)定义函数指针类型 typedef
2)加载动态库
HMODULE LoadLibrary(
LPCTSTR lpFileName //动态库文件名或全路径
);返回DLL的实例句柄(HINSTANCE)
3)获取函数地址
FARPROC GetProcAddress(
HMODULE hModule, //DLL句柄
LPCSTR lpProcName //函数名称
);成功返回函数地址
4)使用函数
5)卸载动态库
BOOL FreeLibrary(
HMODULE hModule //DLL的实例句柄
);
CPPdll.cpp:
int CPPdll_add(int add1, int add2)
{
return add1 + add2;
}
int CPPdll_sub(int sub1, int sub2)
{
return sub1 - sub2;
}
_declspec(dllexport)int CPPdll_mul(int mul1, int mul2)
{
return mul1 * mul2;
}
CPPdll.def:
LIBRARY CPPDll
EXPORTS
CPPdll_add @1
CPPdll_sub @2
UseShow.cpp:
#include<Windows.h>
#include<iostream>
using namespace std;
typedef int(*ADD)(int m, int n);
typedef int(*SUB)(int m, int n);
typedef int(*MUL)(int m, int n);
int main()
{
HINSTANCE hDll = LoadLibrary(L"CPPDll.dll");
cout << "hDll" << hDll << endl;
//hDLL:获取首地址,第二个参数:相对地址
ADD myAdd = (ADD)GetProcAddress(hDll, "CPPdll_add");
cout << "myAdd:" << myAdd << endl;
int sum = myAdd(5, 4);
cout << "sum=" << sum << endl;
SUB mySub = (SUB)GetProcAddress(hDll, "CPPdll_sub");
cout << "mySub:" << mySub << endl;
int sub = mySub(5, 4);
cout << "sub=" << sub << endl;
MUL myMul = (MUL)GetProcAddress(hDll, "?CPPlib_a@@YAHHH@Z");
cout << "myMul:" << myMul << endl;
int mul = myMul(5, 4);
cout << "mul=" << mul << endl;
return 0;
}
4.动态库中封装类
- 在类名称前增加_declspec(dllexport)定义,例如:
class _declspec(dllexport) CMath(
…
}; - 通常使用编译器开关切换类的导入导出定义,例如:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) //DLL
#else
#define EXT_CLASS _declspec(dllimport) //使用者
#endif
class EXT_CLASS CMath{
…
};
注意:DLL中使用导入dllexport,使用者使用导入dllimport,使用此开关,便于在导入时导入,使用时导出。
ClassDll.h:
#ifndef _DLLCLASS_H
#define _DLLCLASS_H
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport)
#else
#define EXT_CLASS _declspec(dllimport)
#endif // !_DLLCLASS_H
class EXT_CLASS CMath {
public:
int Add(int add1, int add2);
int Sub(int sub1, int sub2);
};
#endif
ClassDll.cpp:
#define DLLCLASS_EXPORTS
#include"ClassDll.h"
int CMath::Add(int add1, int add2)
{
return add1 + add2;
}
int CMath::Sub(int sub1, int sub2)
{
return sub1 - sub2;
}
UseClassDll.cpp:
#include<iostream>
using namespace std;
#include "../ClassDll/ClassDll.h"
#pragma comment(lib,"../x64/Debug/ClassDll.lib")
int main()
{
CMath math;
int sum = math.Add(5, 6);
int sub = math.Sub(5, 6);
cout << "sum=" << sum << ",sub=" << sub << endl;
return 0;
}