ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

win32 - 对话框、静态动态库

2022-08-02 09:03:34  阅读:141  来源: 互联网

标签:函数 lib 对话框 静态 dll win32 int


对话框

与普通窗口相比,处理消息的方式不一样。

  • 普通窗口:自定义函数调用缺省函数
    WndProc(...){
        ...
        DefWindowProc(...); // 缺省函数
    }
  • 对话框窗口:缺省函数调用自定义函数
    缺省函数(...){
        ...
        自定义函数(...); // 调用用户写的函数
        ...
    }

1. 对话框原理

分类:

  1. 模式对话框:对话框显示后,禁止本进程其他窗口和用户交互
  2. 无模式对话框:对话框显示后,其他窗口仍然可以和用户交互

对话框基本使用:

  1. 对话框窗口处理函数
  2. 注册窗口类(不使用,系统已经注册好了,默认提供了一个处理消息的缺省函数)
    // 用户自定义函数 (INT)(HWND,UINT,WPARAM,LPARAM)
    // 返回 TRUE:缺省函数无需处理
    // 返回 FALSE:交给缺省函数处理
    INT CALLBACK DialogProc(
        HWND hWndDlg,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    );
  3. 创建对话框
  4. 对话框的关闭

2. 模式对话框

创建对话框

// 这是阻塞函数,只有当对话框关闭后才返回执行后续代码
// 返回值通过 EndDialog 设置
INT DialogBox(
    hInstance,    // 实例句柄
    lpTemplate,   // 对话框资源ID
    hWndParent,   // 对话框父窗口
    lpDialogFunc  // 自定义函数
);

 关闭对话框

// 关闭时只能使用这个函数,不能用 DestoryWindow 等函数
// => DestoryWindow可以销毁对话框,但是无法解除 DialogBox 的阻塞状态 <=
BOOL
EndDialog(
    _In_ HWND hDlg,       // 要关闭的对话框
    _In_ INT_PTR nResult  // 关闭的返回值
);

 对话框特殊消息:

  • WM_INITDIALOG对话框创建之后,显示之前。通知对话框窗口处理函数完成自己的初始化相关操作
    WM_CREATE 类似,其他消息都与普通窗口相同

例子

创建一个菜单资源 和 一个对话框资源:

// 当点击菜单栏中的 ID_MODEL 菜单项时创建对话框
	case WM_COMMAND:
	{
		switch (LOWORD(wparam))
		{
		case ID_MODEL:
		{
			// DialogBox 是一个阻塞函数,返回值 nRet 就是 EndDlg 设置的返回值100
			int nRet = DialogBox(g_hInstance, (CHAR*)IDD_DIALOG1, hwnd, DlgProc);
			break;
		}
		default:
			break;
		}
		break;
	}

// 自定义消息处理函数
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	switch (msg)
	{
	case WM_SYSCOMMAND:
	{
		if (wparam == SC_CLOSE)
		{
			EndDialog(hwnd, 100);
		}
		break;
	}
	default:
		break;
	}

	return FALSE; // 返回FALSE,让默认消息处理函数处理
}

 

 

3. 无模式对话框

非阻塞模式

// 创建成功返回窗口句柄,要使用 ShowWindow 函数显示对话框
// 关闭时需要使用 DestoryWindow 销毁,不能使用 EndDialog,后者用于销毁阻塞对话框
HWND
CreateDialogA(
    hInstance,   // 实例句柄
    lpName,      // 模板资源ID
    hWndParent,  // 父窗口
    lpDialogFunc // 自定义函数
    );

例子:

// 点击菜单项 ID_NOT_MODEL 创建非模式对话框
	case WM_COMMAND:
	{
		switch (LOWORD(wparam))
		{
		case ID_NOT_MODEL:
		{
			HWND hwndDlg = CreateDialog(g_hInstance, (CHAR*)IDD_DIALOG1, hwnd, DlgProc);
			ShowWindow(hwndDlg, SW_NORMAL);
			break;
		}
		default:
			break;
		}
		break;
	}

// 自定义消息处理函数
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	switch (msg)
	{
	case WM_SYSCOMMAND:
	{
		if (wparam == SC_CLOSE)
		{
			DestroyWindow(hwnd);
		}
		break;
	}
	default:
		break;
	}
	return FALSE; // 返回FALSE,让默认消息处理函数处理
}

 


 

 静态库

使用C语言使用静态库可以不用函数声明;

但是使用CPP时需要使用函数声明,否则保存。

1. 静态库特点

无法运行;静态库源码被链接到调用程序中;目标成像的归档。

2. C语言静态库

创建:

  1. 创建静态库文件
  2. 添加库程序,源文件用C文件
    int add(int x, int y)
    {
    	return x + y;
    }
    
    int sub(int x, int y)
    {
    	return x - y;
    }

向c文件添加函数

编译生成,生成的 .lib 文件在解决方案的 x64/DEBUG 目录下。

编译时出现问题:在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "pch.h"”?

解决方法:取消预编译头
项目属性 —— 配置属性 —— c/c++  —— 预编译头

 

使用:

库路径设置:可使用 pragma 关键字设置

#pragma comment(lib, "../lib/clib.lib")

注意:这里的相对路径是相对于项目的路径
项目为 D:/code/WinApplication ,静态库在 D:/code/WinApplication/clib.lib ,那么应该输入 .
如果在 D:/code/WinSolution/clib.lib ,那么应该输入 ../WinSolution/clib.lib

  1. 创建控制台程序调用 lib 库
  2. 在文件头部指定静态库位置
#include <stdio.h>

// 指出静态库位置,这里放在了项目文件夹下。让链接器链接
// 如果放在系统路径下可无需这句话
#pragma comment(lib, "./CStaticLib.lib")

int main()
{
    int x = 1;
    int y = 1;

    printf("add: %d\nsub: %d\n", add(x, y), sub(x, y));

    return 0;
}

3. C++静态库

创建一个静态库项目,使用cpp文件。使用同样的语句设置路径。

创建静态库工程,添加代码:

int add(int x, int y){
	return x + y;
}

int sub(int x, int y){
	return x - y;
}

创建C++工程:

#include <iostream>

// 向链接器指出静态库位置
#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CppStaticLib.lib")

// 下面两个函数声明给编译器看
// 正确做法是使用头文件包含
int add(int x, int y);
int sub(int x, int y);

int main()
{
    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    return 0;
}

 

 

4. 使用 c++ 调用c静态库

c++有换名机制,c语言没有。

使用C语言使用静态库可以不用函数声明;

但是使用CPP时需要使用函数声明,否则保存。

使用cpp调用c静态库会提示“找不到函数”因为c++由换名机制,将函数名换为一个其他的长字符串,在c静态库中没有,故cpp无法调用。cpp静态库中保存的函数名也是经过换名的。

在函数声明前加上 extern "C"表示使用C语言方式编译,不适用换名机制。这样cpp就可以使用c静态库了

Cpp需要函数声明就是为了知晓是否需要对函数进行换名。

#include <iostream>

// 向链接器指出静态库位置
//#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CppStaticLib.lib")
#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CStaticLib.lib")

// 下面两个函数声明给编译器看
// 正确做法是使用头文件包含
int add(int x, int y);
int sub(int x, int y);

int main()
{
    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    return 0;
}

报错:

正确方法:

#include <iostream>

// 向链接器指出静态库位置
//#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CppStaticLib.lib")
#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CStaticLib.lib")

// 下面两个函数声明给编译器看
// 正确做法是使用头文件包含
extern "C" int add(int x, int y);
extern "C" int sub(int x, int y);

int main()
{
    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    return 0;
}

 


 

动态库

1. 动态库特点

  1. 运行时独立存在
    依附其他程序启动,启动后是一个独立的进程
  2. 源码不会链接到程序
  3. 使用时加载

相比静态库:

  1. 静态库是镶嵌入程序中,每个程序都会复制一份库。动态库只要存在一份,其他程序通过函数地址使用,程序体积小
  2. 静态库变化后新代码需要重新链接嵌入。动态库变化后,如果库函数定义和地址未变化,其他程序无需重新链接

2. 动态库创建

  1. 创建动态库项目
  2. 添加库程序
  3. 库程序导出:提供使用者信息
    两种方法:
    1. 声明导出:使用 _declspec(dllexport) 导出函数
      注意:动态库编译链接后,也生成一个LIB文件,是作为动态库函数映射使用,与静态库不完全相同
    2. 模块定义文件 .def
      例如:
      LIBRARY DLLFunc   库
      EXPORTS                库导出表
      DLL_Mul      @1     导出的函数

创建项目,输入代码:

_declspec(dllexport)
int add(int x, int y)
{
    return x + y;
}

_declspec(dllexport)
int sub(int x, int y)
{
    return x - y;
}

编译生成文件:

  1. dll 文件:分为文件体和文件头
    文件体包含了函数的代码与具体实现
    文件头包含了 函数编号、函数名、函数入口地址(相对于动态库入口地址的相对地址)
  2. lib文件
    包含了配套dll文件的文件名,库函数名和dll中函数标号
    内部没有函数实现
  3. lib文件与dll文件中函数名、函数标号都是一致的

 

3. 动态库的使用

隐式链接

os负责使动态库执行

 

  1. 头文件和函数原型
    可以在函数原型的声明前增加 _declspec(dllimport) 
  2. 导入动态库的LIB文件
  3. 在程序中使用函数
  4. 隐式链接情况下,dll可存放目录:
    按照以下顺序查找,建议放在同一目录下
    1. 执行文件同一目录下
    2. 当前工作目录
    3. Window 目录
    4. Window/System32 目录
    5. Window/System 目录
    6. 环境变量PATH指定目录

创建dll项目:

_declspec(dllexport)
int add(int x, int y)
{
    return x + y;
}
_declspec(dllexport)
int sub(int x, int y)
{
    return x - y;
}

编译生成文件:位于 解决方案/x64/debug/ 目录下

创建使用dll的项目:

#include <iostream>

// 通知链接器从哪里找到 函数名和函数编号和dll文件名
#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CPPDLL.lib")

// 下面两个函数声明给编译器看
// 正确做法是使用头文件包含
_declspec(dllimport) int add(int x, int y);
_declspec(dllimport) int sub(int x, int y);

int main()
{
    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    return 0;
}

默认情况下生成的dll文件和exe文件都在同一解决方案的同一目录下。

 

 

显式链接

用户自己负责使动态库运行

  1. 定义函数指针类型 typedef
  2. 加载动态库
    HMODULE      // 返回DLL的实例句柄,就是动态库的 HINSTANCE
    LoadLibrary(
        _In_ LPCSTR lpLibFileName // 动态库文件名(与exe同一目录或在系统文件夹下)或全路径
        );
  3. 获取函数(真实/绝对)地址
    WINBASEAPI
    FARPROC   // 成功则返回函数地址
    WINAPI
    GetProcAddress(
        _In_ HMODULE hModule,   // DLL 句柄
        _In_ LPCSTR lpProcName  // 函数名称
        );
  4. 使用函数
  5. 卸载动态库
    WINBASEAPI
    BOOL
    WINAPI
    FreeLibrary(
        _In_ HMODULE hLibModule // DLL 的实例句柄
        );

创建DLL项目的步骤相同,调用dll的代码:

#include <iostream>
#include <Windows.h>


typedef int(*ADD)(int x, int y);
typedef int(*SUB)(int x, int y);

int main()
{
    HINSTANCE hDll =  LoadLibrary("CPPDLL.dll");
    std::cout << "hDll: " << hDll << std::endl;

    ADD add = (ADD)GetProcAddress(hDll, "?add@@YAHHH@Z"); // 注意此处要使用CPP中换名之后的函数名称
    std::cout << "add address: " << add << std::endl;

    SUB sub = (SUB)GetProcAddress(hDll, "sub");           // 使用原名称会报错,因为函数名被替换
    std::cout << "sub address: " << sub << std::endl;

    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    FreeLibrary(hDll);

    return 0;
}

如果导出函数名没有被替换则更加方便,这时就要使用 模块定义文件 .def 在其中书写要导出的函数信息。这样导出的函数没有被换名

LIBRARY DLL   // 关键字 + 无后缀的动态库文件名
EXPORTS       // 关键字,下面是库导出表
FunName1   @1 // 要导出的函数
FunName2   @2
// ...


CPPDLL 项目中:

dllmain.cpp:

// 使用def文件导出
int add(int x, int y)
{
    return x + y;
}

// 继续使用声明导出
_declspec(dllexport)
int sub(int x, int y)
{
    return x - y;
}

CPPDLL.def:

调用dll的文件:

#include <iostream>
#include <Windows.h>


typedef int(*ADD)(int x, int y);
typedef int(*SUB)(int x, int y);

int main()
{
    HINSTANCE hDll =  LoadLibrary("CPPDLL.dll");
    std::cout << "hDll: " << hDll << std::endl;

    ADD add = (ADD)GetProcAddress(hDll, "add");            // 此函数使用模块定义导出,可直接使用原文件名
    std::cout << "add address: " << add << std::endl;

    SUB sub = (SUB)GetProcAddress(hDll, "?sub@@YAHHH@Z");  // 使用声明导出,需要用这种替换后的函数名
    std::cout << "sub address: " << sub << std::endl;

    int x = 1;
    int y = 1;

    std::cout << "sum: " << add(x, y) << " sub: " << sub(x, y) << std::endl;

    FreeLibrary(hDll);

    return 0;
}

 

 

4. 动态库中封装类

对于类一般直接使用声明导出,不用模块定义文件。模块定义文件用于导出函数地址。

在类名前增加 _declspec(dllexport) 定义,例如:

class _declspec(dllexport) CMath {
    ...
};


// 使用预编译开关切换导入导出的定义
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) // DLL 中
#else
#define EXT_CLASS _declspec(import)    // 使用者中定义
#endif
class EXT_CLASS CMath{
    ...
};

创建一个DLL项目:

ClassDLL.h:

#pragma once
#ifndef _CLASSDLL_H
#define _CLASSDLL_H

#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) // DLL 中
#else
#define EXT_CLASS _declspec(dllimport)    // 使用者中定义
#endif

class 
EXT_CLASS // 导出这两个成员函数的相对地址
CMath
{
public:
	int Add(int, int);
	int Sub(int, int);
private:
};

#endif // !_CLASSDLL_H

ClassDll.cpp:

#include "ClassDll.h"

#define DLLCLASS_EXPORTS

int CMath::Add(int x, int y)
{
	return x + y;
}

int CMath::Sub(int x, int y)
{
	return x - y;
}

生成dll:

调用dll的项目,这个项目需要导入dll对应的头文件和lib文件:

#include <iostream>
#include <Windows.h>

#include "../CPPDLL/ClassDll.h"

#pragma comment(lib, "../WinConsoleApplication01/x64/DEBUG/CPPDLL.lib")

int main()
{
    CMath math;

    std::cout << math.Add(1, 2) << "\n" << math.Sub(1, 2) << std::endl;

    return 0;
}

结果:

标签:函数,lib,对话框,静态,dll,win32,int
来源: https://www.cnblogs.com/zhh567/p/16536626.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有