Win32编程day11 学习笔记

一 Windows的库
  由于项目的复杂程度,或者为了提高代码的重用率等等,所以才引入了库程序。
  库包含两种:
    1 静态库:扩展名为LIB的文件,是不能被加载的程序,可以理解为目标程序的归档。
    2 动态库:扩展名是DLL的文件,是可以被应用程序加载的程序。
       
二 静态库

  1 静态库的特点
    1.1 目标程序的归档
    1.2 静态库的代码会被嵌入到程序当中。
    1.3 程序执行时不需要静态库存在

  2 C语言静态库
    2.1 创建静态库 
        创建Win32静态库项目,使用*.C文件建立项目。
        在Setting里将library选项卡下的output路径改为 ../lib/*.lib
    2.2 添加静态库函数

View Code
//winclib.c

int C_Add( int nAdd1, int nAdd2 )
{
    return ( nAdd1 + nAdd2 );
}

int C_Sub( int nSub1, int nSub2 )
{
    return ( nSub1 - nSub2 );
}

    2.3 在程序中将静态库导入
      程序的设置里连接选项卡下的output路径也要修改。改为../bin/*.exe
       2.3.1 项目的Setting里设置
       2.3.2 使用关键字 pragma
        #pragma comment(lib, "../lib/winclib.lib")
    2.4 使用静态库提供的函数
       在C语言程序中,直接使用函数即可。

View Code
//useclib.c

//导入静态库
#pragma comment(lib, "../lib/winclib.lib")

int main( )
{ 
    int nAdd = 0;
    int nSub = 0;
    //使用C静态库的函数
    nAdd = C_Add( 100, 100 );
    nSub = C_Sub( 100, 100 );

    printf( "ADD: %d\n", nAdd );
    printf( "SUB: %d\n", nSub );

    return 0;
} 

  3 C++语言的静态库
    3.1 创建静态库(设置 library ..\lib\*.lib)
       创建Win32静态库项目,使用*.CPP文件建立项目。
    3.2 添加静态库的函数

View Code
//wincpplib.cpp

int CPP_Add( int nAdd1, int nAdd2 )
{
    return ( nAdd1 + nAdd2 );
}

int CPP_Sub( int nSub1, int nSub2 )
{
    return ( nSub1 - nSub2 );
}

    3.3 导入静态库(设置   link  ../bin/*.exe)
       3.3.1 项目的Setting里设置
       3.3.2 使用关键字 pragma
        #pragma comment(lib, "../lib/wincpplib.lib")
    3.4 定义库函数的原形
       int CPP_Add( int nAdd1, int nAdd2 );
    3.5 使用库函数
    3.6 注意:
       如果在CPP文件使用C语言静态库,定义的静态库函数原形,需要增加 extern "C".
       例如:
       extern "C" int C_Add( int nAdd1, int nAdd2 );

View Code
// usecpplib.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

//导入C++的静态库
#pragma comment( lib, "../lib/wincpplib.lib" )
#pragma comment( lib, "../lib/winclib.lib" )

//定义函数原形
int CPP_Add( int nAdd1, int nAdd2 );
int CPP_Sub( int nSub1, int nSub2 );

extern "C" {

int C_Add( int nAdd1, int nAdd2 );
int C_Sub( int nSub1, int nSub2 );

}

int main(int argc, char* argv[])
{    //使用C++库函数
    int nAdd = CPP_Add( 100, 100 );
    int nSub = CPP_Sub( 100, 100 );
    printf( "ADD: %d\n", nAdd );
    printf( "SUB: %d\n", nSub );
    //使用C库函数
    int nAdd2 = C_Add( 100, 100 );
    int nSub2 = C_Sub( 100, 100 );
    printf( "C_ADD: %d\n", nAdd );
    printf( "C_SUB: %d\n", nSub );

    return 0;
} 

三 动态库

  1 动态库的好处
    1.1 可以提供模块化的方式,方便协调开发。
    1.2 对源代码保护
    1.3 减小可执行文件的大小
    1.4 提供代码的重用率
   
  2 动态库的基本使用方法
    2.1 动态库的创建
    2.2 加载动态库
    2.3 获取并使用库函数、变量或类
    2.4 释放动态库
   
  3 动态库的函数
    3.1 创建
     3.1.1 创建DLL的项目
        使用Win32 DLL项目,创建DLL,添加相应的文件。
        设置--常规---输出 改为“../lib”
        设置--连接---输出 改为“../bin/*.dll”
     3.1.2 增加动态库函数
     3.1.3 导出动态库函数
       3.1.3.1 使用__declspec(dllexport)方式
          在函数前增加这个关键字,例如
          __declspec(dllexport) int Dll_Add()
        3.1.3.2 增加 extern "C" 方式,即
           extern "C" __declspec(dllexport)
           以C语言方式导出函数
        3.1.3.3 使用DEF文件导出
          增加扩展名为DEF的文件到项目中(新建文本类型的文件,文件名以.def结尾).
          在DEF文件中添加导出定义.
           LIBRARY dllfunc.dll //导出库
            EXPORTS             //导出表
               Dll_Mul @1      //导出函数
               Dll_Div @2

View Code
//动态库的代码文件dllfunc.cpp

//C++导出方式
__declspec(dllexport) int Dll_Add( int nAdd1, int nAdd2 )
{
    return ( nAdd1 + nAdd2 );
}
//C的导出方式
extern "C" __declspec(dllexport) int Dll_Sub( int nSub1, int nSub2 )
{
    return ( nSub1 - nSub2 );
}
//DEF的导出方式(不能出现同名函数)
int Dll_Mul( int nMul1, int nMul2 )
{
    return ( nMul1 * nMul2 );
}

用DEF导出方式时,新建一个文本文件 dllfunc.def, 接着到刚才新建def文件中:

View Code
LIBRARY dllfunc.dll
EXPORTS 
    Dll_Mul    @1

然后就可以使用了
   3.2 使用
   设置--连接---输出 改为“../bin/CallDllFunc.exe”
      3.2.1 隐式链接
        3.2.1.1 导入LIB
          项目的Setting或者使用#pragma导入,例如:
          #pragma comment( lib, "../lib/dllfunc.lib")
        3.2.1.2 定义函数原形
          声明一个和导出函数一致的函数定义.
          如果DLL采用extern "C"导出函数,
          需要定义extern "C"方式函数原形
        3.2.1.3 使用函数
          直接函数即可

View Code
// CallDllFunc.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

//导入DLL的Lib文件
#pragma comment( lib, "../lib/dllfunc.lib")

//定义函数原形
int Dll_Add( int nAdd1, int nAdd2 );
extern "C" int Dll_Sub( int nSub1, int nSub2 );
int Dll_Mul( int nMul1, int nMul2 );

int main(int argc, char* argv[])
{    // 使用函数
    int nAdd = Dll_Add( 100, 100 );
    int nSub = Dll_Sub( 100, 100 );
    int nMul = Dll_Mul( 100, 100 );
    printf("Dll_Add: %d\n", nAdd );
    printf("Dll_Sub: %d\n", nSub );
    printf("Dll_Mul: %d\n", nMul );
    return 0;
}

        3.2.1.4 应用程序查找DLL的路径
          1) 查找当前应用程序的目录.
          2) 当前的工作目录
          3) 查找Windows System32的目录
          4) 查找Windows System的目录
          5) 查找Windows目录
          6) 查找环境变量PATH指定路径

      3.2.2 显式链接  (程序要修改设置里面的link      ../bin/*.exe) 
    3.2.2.1 加载动态库
      HINSTANCE LoadLibrary(
      LPCTSTR lpLibFileName );//DLL的路径
          返回加载好DLL的句柄
    3.2.2.2 定义函数原形对应的函数指针
    3.2.2.3 获取函数地址
       FARPROC GetProcAddress(
       HMODULE hModule,//DLL的句柄
       LPCSTR lpProcName );//函数的名称
     返回对应函数地址
     注意:
     1 对于__declspec(dllexport)导出的函数,由于函数名称发生变化,所以无法使用函数名称获取对应的函数地址,所以尽量采用隐式链接的方式.
     2 extern "C"或DEF方式导出的函数,
       可以正常的使用函数名称获取函数地址.
    3.2.2.4 使用函数
    3.2.2.5 释放动态库
       FreeLibrary

下面还是以调用dllfunc.dll为例:
View Code
// InvokeDllFunc.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

typedef int ( * DLL_ADD )( int nAdd1, int nAdd2 );
typedef int ( * DLL_SUB )( int nSub1, int nSub2 );
typedef int ( * DLL_MUL )( int nMul1, int nMul2 );

void UseDll( )
{    //加载动态库
    HMODULE hDll = (HMODULE)
        LoadLibrary( "dllfunc.dll" );
    if( hDll == NULL )
    {
        printf( "Load Failed\n");
        return;
    }
    printf( "DLL Handle: %p\n", hDll );

    //定义函数指针
    DLL_ADD Dll_Add = NULL;
    DLL_SUB Dll_Sub = NULL;
    DLL_MUL Dll_Mul = NULL;
    //获取函数地址
    Dll_Add = ( DLL_ADD )
        GetProcAddress( hDll, "Dll_Add" );
    if( NULL == Dll_Add )   
    {
        printf( "Get Dll_Add Failed\n");   //打印。因为找不到Dll_Add函数。C++会重命名函数
    }
    printf( "Dll_Add: %p\n", Dll_Add );

    Dll_Sub = ( DLL_SUB )
        GetProcAddress( hDll, "Dll_Sub" );
    if( NULL == Dll_Sub )
    {
        printf( "Get Dll_Sub Failed\n");
    }
    printf( "Dll_Sub: %p\n", Dll_Sub );

    Dll_Mul = ( DLL_MUL )
        GetProcAddress( hDll, "Dll_Mul" );
    if( NULL == Dll_Mul )
    {
        printf( "Get Dll_Mul Failed\n");
    }
    printf( "Dll_Mul: %p\n", Dll_Mul );
    //使用函数
    int nSub = Dll_Sub( 100, 100 );
    int nMul = Dll_Mul( 100, 100 );
    printf( "Dll_Sub: %d\n", nSub );
    printf( "Dll_Mul: %d\n", nMul );
    //释放动态库
    FreeLibrary( hDll );
}

int main(int argc, char* argv[])
{
    UseDll( );
    return 0;
}

 4 动态库的变量
 
   4.1 定义全局变量(设置  常规   ../lib   link   ../bin/*.dll)
   4.2 导出全局变量
      4.2.1 __declspec(dllexport)导出
     __declspec(dllexport) int g_nValue1 = 100;
      4.2.2 DEF文件导出
         int g_nValue1 = 100;
         在DEF文件的导出列表中增加
           g_nValue1 @1 DATA

View Code
//ddlvalue.cpp
//导出DLL的全局变量
__declspec(dllexport) int g_nValue1 = 100;

//DEF导出的全局变量
int g_nValue2 = 200;
View Code
//dllvalue.def

LIBRARY dllvalue
EXPORTS
    g_nValue2    @1   DATA

   4.3 导入LIB文件(设置  link  ../bin/*.exe)
   4.4 定义导入变量
      需要使用__declspec(dllimport)定义变量.
      extern __declspec(dllimport) int g_nValue1;
   4.5 使用变量
  
 5 动态库的类
 
   5.1 创建动态库并定义类(设置  常规  ../lib   link   ../bin/*.dll)
   5.2 从DLL中导出类
      在类名称前增加__declspec(dllexport)定义.
      class __declspec(dllexport) CMath
    { ... };

View Code
//math.h
#ifndef _MATH_H_
#define _MATH_H_

//定义类导入导出宏
#ifdef _DLLCLASS_DLL_    //(在设置   C/C++  Category--Preprocessor 下  增加_DLLCLASS_DLL_)
#define DLLCLASS_EXT __declspec(dllexport)
#else
#define DLLCLASS_EXT __declspec(dllimport)
#endif //_DLLCLASS_DLL_

//增加类的导入导出宏
class DLLCLASS_EXT CMath 
{
public:
    int Add( int nAdd1, int nAdd2 );
    int Sub( int nSub1, int nSub2 );
};

#endif //_MATH_H_
View Code
//math.cpp
#include "math.h"

int CMath::Add( int nAdd1, int nAdd2 )
{
    return ( nAdd1 + nAdd2 );
}

int CMath::Sub( int nSub1, int nSub2 )
{
    return ( nSub1 - nSub2 );
}

   5.3 使用时导入LIB文件
   5.4 导入类
      在类名称前增加__declspec(dllimport)定义.
      class __declspec(dllimport) CMath
    { ... };
   5.5 使用类

View Code
// useclass.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
//导入DLL的LIB文件
#pragma comment( lib, "../lib/dllclass.lib")
//需要类的导入方式的声明
#include "../DllClass/math.h"

int main(int argc, char* argv[])
{    //使用DLL中的类
    CMath math;
    int nAdd = math.Add( 100, 100 );
    int nSub = math.Sub( 100, 100 );
    printf("math.Add = %d\n", nAdd );
    printf("math.Sub = %d\n", nSub );
    return 0;
}

   5.6 关于类的导入和导出
     1) 定义一个宏,例如:
       #ifdef _DLLCLASS_DLL_
    #define DLLCLASS_EXT __declspec(dllexport)
    #else
    #define DLLCLASS_EXT __declspec(dllimport)
    #endif //_DLLCLASS_DLL_
    2) 根据编译项目,修改_DLLCLASS_DLL_宏声明对于导出类,需要定义_DLLCLASS_DLL_,否则不需要定义 _DLLCLASS_DLL_ 宏
      3) 类的定义为
        class DLLCLASS_EXT CMath
        { ... };
       
  6 DllMain 函数
    是DLL文件入口函数.当程序加载或者释放动态库的时候,会自动调用这个函数.
     BOOL WINAPI DllMain(
       HINSTANCE hinstDLL,//DLL的句柄
       DWORD fdwReason,//DLL被调用的原因
       LPVOID lpvReserved ); //保留值
     fdwReason -
       DLL_PROCESS_ATTACH 进程加载
       DLL_THREAD_ATTACH  线程加载
       DLL_THREAD_DETACH  线程卸载
       DLL_THREAD_DETACH  进程卸载
     返回值表示是否加载成功.

View Code
//dllfunc.cpp
#include "windows.h"
#include "stdio.h"

BOOL WINAPI DllMain( HINSTANCE hinstDLL,
    DWORD fdwReason, LPVOID lpvReserved )
{
    printf( "DLL=%p, Reason=", hinstDLL );
    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH:
        printf( "DLL_PROCESS_ATTACH\n" );
        break;
    case DLL_THREAD_ATTACH:
        printf( "DLL_THREAD_ATTACH\n" );
        break;
    case DLL_THREAD_DETACH:
        printf( "DLL_THREAD_DETACH\n" );
        break;
    case DLL_PROCESS_DETACH:
        printf( "DLL_PROCESS_DETACH\n" );
        break;
    }
    return TRUE;
}

//C++导出方式
__declspec(dllexport) int Dll_Add( int nAdd1, int nAdd2 )
{
    return ( nAdd1 + nAdd2 );
}
//C的导出方式
extern "C" __declspec(dllexport) int Dll_Sub( int nSub1, int nSub2 )
{
    return ( nSub1 - nSub2 );
}
//DEF的导出方式
int Dll_Mul( int nMul1, int nMul2 )
{
    return ( nMul1 * nMul2 );
} 
原文地址:https://www.cnblogs.com/tangzhengyue/p/2646786.html