Win32之菜单和库

 

    • 菜单

1.1 概念

  • 分类

1、静态菜单:在菜单资源编辑器预先编辑好

2、动态菜单:在程序运行的过程中通过代码生成

3、快捷菜单:是前两种菜单的组合,在菜单资源编辑器中预先编辑好,然后在程序运行的过程中动态显

  • 菜单项

菜单里面的每一个元素都是一个菜单项

菜单项包含两个最基本的要素:1、菜单项名字,2、该菜单项唯一的标识ID

每一个菜单项还可以是嵌套的子菜单数组

1.2 操作

c3926945b802ff2f82ea7070f9ae77d0.png

@1 点击资源文件中的”菜单和库.rc*"

c7914766a96791b623b8dd5edf31d004.png

@2 鼠标右击Menu->选择添加资源或者直接插入

92e95b78f580f3987d456b59be9d9120.png

然后就可以直接编辑了

 

01fcf349422dc0ddad462589735e40ed.png

这是vs的菜单栏 ,我们发现在菜单名后面有字母 起始这是快捷键

如果我们也要添加菜单快捷键 只要在编辑的时候在菜单名之后加上(&按键)即可 -----注意:括号为英文格式的括号,若要加载进窗口只需要在系统命令下 加入对应的条件就行了 先找到对应菜单项的ID

a22564fc95080a0c0a974614104ba311.png

 

2a109e25716dea3f29ab598dbf051b8a.png
bab4e86acf34879922eb881862fa4c90.png

在一个菜单项之后还可以编辑它的子菜单(但是实际开发中一般不超过三级菜单)

40453e461c68c3b3edb83d53a929e509.png
0e9cc6d3205c7dfd80facb64166187a2.png

我们发现在不同的菜单项中有分割线

一般是为了区分不同的功能类

我们想要实现只要在添加菜单项的时候输入‘-’(减号)即可

cf63911f3bc558970973817f35f86db8.png

 

 

 

在资源文件中编辑好了之后,我们还要把它加载到窗口中

有两种方式:

1、窗口类中加载菜单

8517ee53619ecccd13fa9ebec0a47ff6.png

 

在上图位置将默认的菜单名改为自己的菜单名

2、创建窗口时加载

在创建主窗口的函数中我们先获得一个菜单的句柄,然后再在CreateWindow函数的倒数第三的参数传入菜单句柄就行了

0c703eb7048e008e932cf2077bd0444a.png

不能把这条注释掉,为了验证第二种方法,把值改为nullptr就行了

a36e0101547d42bad68ed6600040ddc6.png

 

动态加载

学习怎么实现动态加载之前,我先给大家介绍几个函数

  1. CreateMenu()

参数

此函数无参数。

返回值

如果函数成功,则返回值是新建菜单的句柄。

如果函数失败,返回值为NULL。

  1. AppendMenu()

将一个新项目附加到指定的菜单栏,下拉菜单,子菜单或快捷菜单的末尾。您可以使用此函数指定菜单项的内容,外观和行为。

InsertMenuItem功能已被InsertMenuItem功能取代。但是,如果您不需要InsertMenuItem的任何扩展功能,您仍然可以使用AppendMenu.

BOOL AppendMenu(

HMENU 【HMENU】,

//处理要更改的菜单

UINT 【uFlags】,

//菜单项标志

UINT 【uIDNewItem】,

//菜单项标识符或下拉菜单或子菜单的句柄

LPCTSTR 【lpNewItem】

//菜单项内容即菜单项得名字

);

 

  1. InsertMenu()

将一个新的菜单项插入菜单,将其他项目向下移动到菜单中。

InsertMenuItem功能已被InsertMenuItem功能取代。但是,如果您不需要InsertMenuItem的任何扩展功能,您仍然可以使用InsertMenu.

BOOL InsertMenu(

HMENU 【HMENU】,

//菜单的句柄

UINT 【uPositionbs】,

//新菜单项前面的菜单项

UINT 【uFlags】,

//菜单项标志

UINT 【uIDNewItem】,

//菜单项标识符或下拉菜单或子菜单的句柄

LPCTSTR 【lpNewItem】

//菜单项内容

);

 

  1. EnableMenuItem

禁用或灰化指定的菜单项。

BOOL EnableMenuItem(

HMENU 【HMENU】,

//处理菜单的句柄

UINT 【uIDEnableItem】,

//菜单项启用,禁用或灰色

UINT 【uEnable】

//菜单项标志

);

 


//动态菜单:
HMENU hMenu1 = nullptr;
hMenu1 = CreateMenu();//创建了一个新菜单(空菜单)
HMENU h_1 = CreateMenu();
AppendMenu(h_1, //菜单句柄,指明在哪一个菜单的尾部进行追加
0, //菜单风格
10087, //菜单项id
_T("新建(&N)\t Ctrl+N"));//菜单项名
AppendMenu(h_1, MF_SEPARATOR, 0, _T("-"));
AppendMenu(h_1, 0, 10088, _T("打开(&O)\t Ctrl+O"));
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)h_1, _T("更多(&F)"));
HMENU h_2 = CreateMenu();
//第2个参数为坐标,该参数会受到第3个参数(MF_BYCOMMAND or MF_BYPOSITION)的影响
InsertMenu(h_2, 0, MF_BYPOSITION, 10089, _T("a"));
InsertMenu(h_2, 0, MF_BYPOSITION, 10090, _T("b"));
InsertMenu(h_2, 10089, MF_BYCOMMAND, 10091,_T("c"));
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)h_2, _T("abc"));
//使菜单项是否变灰或是否激活
EnableMenuItem(h_1, 10087, MF_GRAYED);
EnableMenuItem(hMenu, 10087, MF_ENABLED);
EnableMenuItem(h_1, 0, MF_DISABLED | MF_BYPOSITION);
SetMenu(hWnd, hMenu1);//更新菜单,在窗口显示更新后修改要加
a6c19c72ccbd14857353f1ffbd7aee28.png

当我把上面这段代码加入之前的代码中之后效果如上图

注意到:多了两个菜单项 即h_1 h_2, abc菜单项项有三个子项 顺序为bca a是最先插入的但是在最下面 所以说明了使用InsertMenu()时上面的后插入,下面的先插入(当然这是默认的插入,没有指定下面的菜单项是谁,比如上面的c就指定了下面是a)

但是使用AppendMenu()时默认是先插入的在上面

 

运行效果如下

d90558db8ae5342f07344bdc83fbe355.png

 

 

 

右键菜单

1、在资源编辑器中编辑好新的右键菜单

2、初始化加载,可以在WM_CREATE内或者在主消息循环之前加载。(下面的演示代码是在窗口创建的时候加载的)

  1. 在WM_RBUTTONDOWN内可以响应



case WM_CREATE:
         g_hRMemu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU2));
         break;
    case WM_RBUTTONUP://鼠标右键抬起
    {
POINT pt;
        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);
        //把客户区坐标转为屏幕坐标
        ClientToScreen(hWnd, &pt);
        //从菜单中获取要弹出的分级菜单
        HMENU tempMenu = GetSubMenu(g_hRMemu, 0);//把g_hRMenu里面的0列表示的菜单给到tempMenu
        //弹出右键菜单
        TrackPopupMenu(tempMenu,//哪一个菜单
            TPM_LEFTALIGN | TPM_TOPALIGN,//菜单风格,对齐方式
            pt.x, pt.y,//鼠标坐标,菜单在哪个坐标开始显示
            0,
            hWnd,
            nullptr);
    }
        break;

客户区坐标:就是相对于我们打开的窗口的左上角的坐标

屏幕坐标:相对于电脑屏幕左上角的坐标

我们直接用lParam获取的是客户区坐标 但是TrackPopupMenu这个弹出菜单的函数里的参数是屏幕坐标所以我们要用 ClientToScreen转换一下

a967e0b33e1163c309e45f944cdd34fd.png

 

以上源文件的所有代码如下:


// 菜单和库.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "菜单和库.h"

#define MAX_LOADSTRING 100

// 全局变量: 
HINSTANCE hInst;                                // 当前实例
TCHAR szTitle[MAX_LOADSTRING];                    // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
HMENU g_hRMemu = nullptr;                       //右键菜单
// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

     // TODO:  在此放置代码。
    MSG msg;
    HACCEL hAccelTable;

    // 初始化全局字符串
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_MY, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY));

    // 主消息循环: 
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数:  MyRegisterClass()
//
//  目的:  注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName    = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

//
//   函数:  InitInstance(HINSTANCE, int)
//
//   目的:  保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中
   HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);
   //动态菜单:
   HMENU hMenu1 = nullptr;
   hMenu1 = CreateMenu();//创建了一个新菜单(空菜单)
   HMENU h_1 = CreateMenu();
   AppendMenu(h_1, //菜单句柄,指明在哪一个菜单的尾部进行追加
       0, //菜单风格
       10087, //菜单项id
       _T("新建(&N)\t Ctrl+N"));//菜单项名
   AppendMenu(h_1, MF_SEPARATOR, 0, _T("-"));
   AppendMenu(h_1, 0, 10088, _T("打开(&O)\t Ctrl+O"));
   AppendMenu(hMenu, MF_POPUP, (UINT_PTR)h_1, _T("更多(&F)"));
   HMENU h_2 = CreateMenu();
   //第2个参数为坐标,该参数会受到第3个参数(MF_BYCOMMAND or MF_BYPOSITION)的影响
   InsertMenu(h_2, 0, MF_BYPOSITION, 10089, _T("a"));
   InsertMenu(h_2, 0, MF_BYPOSITION, 10090, _T("b"));
   InsertMenu(h_2, 10089, MF_BYCOMMAND, 10091, _T("c"));
   AppendMenu(hMenu, MF_POPUP, (UINT_PTR)h_2, _T("abc"));
   HMENU h_3 = CreateMenu();
    HMENU h_4 = CreateMenu();
   AppendMenu(h_3, MF_POPUP, (UINT)h_4, _T("菜单选项一"));
   AppendMenu(h_3, 0, 1001, _T("菜单选项二"));
   AppendMenu(h_3, 0, 1002, _T("菜单选项三"));
   AppendMenu(h_3, 0, 1003, _T("菜单选项四"));
   AppendMenu(hMenu, MF_POPUP, (UINT)h_3, _T("选项"));//    MF_POPUP指定菜单项打开下拉菜单或子菜单   第三个参数就是子菜单项的菜单句柄
   AppendMenu(h_4, 0, 2000, _T("小黑子"));
   AppendMenu(h_4, 0, 2001, _T("IKUN"));
   //使菜单项是否变灰或是否激活
   EnableMenuItem(h_1, 10087, MF_GRAYED);
  // EnableMenuItem(hMenu, 10087, MF_ENABLED);
 //  EnableMenuItem(h_1, 0, MF_DISABLED | MF_BYPOSITION);
  // SetMenu(hWnd, hMenu1);//更新菜单,在窗口显示更新后修改要加

   
   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数:  WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND    - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY    - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
         g_hRMemu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU2));
         break;
    case WM_RBUTTONUP:
    {
POINT pt;
        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);
        //把客户区坐标转为屏幕坐标
        ClientToScreen(hWnd, &pt);
        //从菜单中获取要弹出的分级菜单
        HMENU tempMenu = GetSubMenu(g_hRMemu, 0);//把g_hRMenu里面的0列表示的菜单给到tempMenu
        //弹出右键菜单
        TrackPopupMenu(tempMenu,//哪一个菜单
            TPM_LEFTALIGN | TPM_TOPALIGN,//菜单风格,对齐方式
            pt.x, pt.y,//鼠标坐标,菜单在哪个坐标开始显示
            0,
            hWnd,
            nullptr);
    }
        break;
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // 分析菜单选择: 
        switch (wmId)
        {
        case 1234:
            MessageBox(hWnd,_T("你干嘛~"), _T("帮助"), MB_OK);
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO:  在此添加任意绘图代码...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

2.库

 

静态库

静态库是指我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”,理解为“仓库”,

在“仓库”中存储了很多东西,每次再要使用时只需要去“仓库”拿就可以了。

程序有“预处理”->“编译”->“链接”这几个步骤。静态库就不需要再次编译,只需要在“链接”步骤中,链接器

将从“仓库”的文件中取得所需的代码,复制到生成的可执行文件中即可。

动态库

动态库又称动态链接库。英文“dll”,是“Dynamic Link Library”的缩写形式。

DLL是一个包含可由多个程序同时使用的代码和数据的库。

DLL不是可执行文件。

动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。

函数的可执行代码位于一个DLL中,该DLL包含一个或多个已被编译、链接并与使用它们的进程分开存储

的函数。

DLL还有助于共享数据和资源。多个应用程序可同时访问内存中单 个DLL副本的内容。

windows下动态库为.dll后缀。

linux下为.so后缀。

静态库和动态库的区别

1、静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系。

2、动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。

优点

使用静态库的优点:

1、代码装载速度快,执行速度略比动态链接库快

2、发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题

使用动态库的优点:

1、更加节省内存并减少页面交换

2、dll文件和exe文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换

DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性

3、不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数

4、适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和

测试。不足之处

1、使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费

2、使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,

程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终

止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果

新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常

注意:不管是静态库还是动态库,都能实现共享代码。只不过是有没有被包含到exe文件中而已。

静态库、动态库文件关系

静态库和动态库中会需要 一些文件。主要用到以下3类中的文件。

1、.h文件。编译时必须的,声明函数接口用

2、.lib文件。链接时需要的,当我们在程序中引用了一个.h文件里面的函数,链接器怎么知道要调用哪

个dll文件呢,这就是lib文件的作用。它告诉链接器调用的函数在哪个dll中,函数执行代码在dll中的什么

位置。

注意:如果是生成静态库文件,没有dll文件,这个时候函数的可执行代码部分也在lib文件中。所以在链

接时就能直接链接进exe文件。

3、.dll文件。运行时需要的,函数的可执行代码

静态库只用到前两种,动态库用到了所有的三种。

注意:附加依赖项添加的是.lib而不是.dll,若生成了dll,则肯定也生成了lib文件 。

静态库示例

1、创建一个”WIN32“项目,”win32控制台“或”win32项目“。

2、选择”静态库“。

3、新建一个.h文件。

然后在这个.h文件中进行如下代码编写:

4、添加cpp文件。

#ifndef __MYTOOLS_H__ //如果没有定义宏名

#define __MYTOOLS_H__ //定义宏名

extern "C" int MyAdd(int num1, int num2);//实现两个数相加

extern "C" int MySub(int num1, int num2);//实现两个数相减

#endif //结束预处理5、编译生成解决方案。

导入lib文件

动态库示例

1、创建”WIN32“项目,”win32控制台“或”win32项目“。

2、选择”DLL“。

3、添加头文件”MyDll.h“:

4、添加源文件”MyDll.cpp“:

  1. 编译生成解决方案。


#include "stdafx.h"
#include "MyTools.h"
int MyAdd(int num1, int num2)
{
return num1 + num2;
}
int MySub(int num1, int num2)
{
return num1 - num2;
}



#pragma comment(lib,"某某.lib")//第二个参数为拷贝到这个新工程中的.lib文件的全称
#ifndef __MYDLL_H__
#define __MYDLL_H__
extern "C" _declspec(dllexport) void MyFun(int op1,int op2);
#endif
#include "stdafx.h"
#include <stdio.h>
#include "MyDll.h"
void MyFun(int op1, int op2)
{
for (int i = 1; i <= op1; ++i)
{
for (int j = i; j <= op2; ++j)
{
printf("%d * %d = %d\t",j,i,j * i);
}
printf("\n");
}
}

6、在”dll项目“中有它的入口函数的。有一个dllmain.cpp。这里面有一个动态库的入口函数。DllMain函

数,一般这个函数不用。它只是一个可选的入口函数。里面第二个参数的switch分支用来表示”系统何时

调用了DllMain

 

静态库

静态库是指我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”,理解为“仓库”,

在“仓库”中存储了很多东西,每次再要使用时只需要去“仓库”拿就可以了。

程序有“预处理”->“编译”->“链接”这几个步骤。静态库就不需要再次编译,只需要在“链接”步骤中,链接器

将从“仓库”的文件中取得所需的代码,复制到生成的可执行文件中即可。

动态库

动态库又称动态链接库。英文“dll”,是“Dynamic Link Library”的缩写形式。

DLL是一个包含可由多个程序同时使用的代码和数据的库。

DLL不是可执行文件。

动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。

函数的可执行代码位于一个DLL中,该DLL包含一个或多个已被编译、链接并与使用它们的进程分开存储

的函数。

DLL还有助于共享数据和资源。多个应用程序可同时访问内存中单 个DLL副本的内容。

windows下动态库为.dll后缀。

linux下为.so后缀。

静态库和动态库的区别

1、静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系。

2、动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。

优点

使用静态库的优点:

1、代码装载速度快,执行速度略比动态链接库快

2、发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题

使用动态库的优点:

1、更加节省内存并减少页面交换

2、dll文件和exe文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换

DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性

3、不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数

4、适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和

测试。不足之处

1、使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费

2、使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,

程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终

止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果

新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常

注意:不管是静态库还是动态库,都能实现共享代码。只不过是有没有被包含到exe文件中而已。

静态库、动态库文件关系

静态库和动态库中会需要 一些文件。主要用到以下3类中的文件。

1、.h文件。编译时必须的,声明函数接口用

2、.lib文件。链接时需要的,当我们在程序中引用了一个.h文件里面的函数,链接器怎么知道要调用哪

个dll文件呢,这就是lib文件的作用。它告诉链接器调用的函数在哪个dll中,函数执行代码在dll中的什么

位置。

注意:如果是生成静态库文件,没有dll文件,这个时候函数的可执行代码部分也在lib文件中。所以在链

接时就能直接链接进exe文件。

3、.dll文件。运行时需要的,函数的可执行代码

静态库只用到前两种,动态库用到了所有的三种。

注意:附加依赖项添加的是.lib而不是.dll,若生成了dll,则肯定也生成了lib文件 。

静态库示例

1、创建一个”WIN32“项目,”win32控制台“或”win32项目“。

2、选择”静态库“。

3、新建一个.h文件。

然后在这个.h文件中进行如下代码编写:

  1. 添加cpp文件。


#ifndef __MYTOOLS_H__ //如果没有定义宏名
#define __MYTOOLS_H__ //定义宏名
extern "C" int MyAdd(int num1, int num2);//实现两个数相加
extern "C" int MySub(int num1, int num2);//实现两个数相减
#endif //结束预处理5、编译生成解决方案。
导入lib文件

动态库示例

1、创建”WIN32“项目,”win32控制台“或”win32项目“。

2、选择”DLL“。

3、添加头文件”MyDll.h“:

4、添加源文件”MyDll.cpp“:

  1. 编译生成解决方案。


#include "stdafx.h"
#include "MyTools.h"
int MyAdd(int num1, int num2)
{
return num1 + num2;
}
int MySub(int num1, int num2)
{
return num1 - num2;
}


#pragma comment(lib,"某某.lib")//第二个参数为拷贝到这个新工程中的.lib文件的全称
#ifndef __MYDLL_H__
#define __MYDLL_H__
extern "C" _declspec(dllexport) void MyFun(int op1,int op2);
#endif
#include "stdafx.h"
#include <stdio.h>
#include "MyDll.h"
void MyFun(int op1, int op2)
{
for (int i = 1; i <= op1; ++i)
{
for (int j = i; j <= op2; ++j)
{
printf("%d * %d = %d\t",j,i,j * i);
}
printf("\n");
}
}

6、在”dll项目“中有它的入口函数的。有一个dllmain.cpp。这里面有一个动态库的入口函数。DllMain函

数,一般这个函数不用。它只是一个可选的入口函数。里面第二个参数的switch分支用来表示”系统何时

调用了DllMain

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pp不会算法^v^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值