win32day11-windows的库/静态库/动态库

Posted 吴英强

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了win32day11-windows的库/静态库/动态库相关的知识,希望对你有一定的参考价值。

Windows的库

  由于项目的复杂程度,或者为了提高代码的

  重用率等等,所以才引入了库程序。

  库包含两种:

    1 静态库:扩展名为LIB的文件,是不能被

        加载的程序,可以理解为目标程序的

        归档。

    2 动态库:扩展名是DLL的文件,是可以被

        应用程序加载的程序。

静态库

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


2 C语言静态库
2.1 创建静态库
创建Win32静态库项目,使用*.C文件建立项目。

int C_Add( int nAdd1, int nAdd2 )

	return ( nAdd1 + nAdd2 );


int C_Sub( int nSub1, int nSub2 )

	return ( nSub1 - nSub2 );
2.2 添加静态库函数
2.3 在程序中将静态库导入
   2.3.1 项目的Setting里设置, link选项
   2.3.2 使用关键字 pragma
#pragma comment(lib, "../lib/winclib.lib")
2.4 使用静态库提供的函数
   在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 创建静态库

   创建Win32静态库项目,使用*.CPP文件建立项目。

int CPP_Add( int nAdd1, int nAdd2 )

	return ( nAdd1 + nAdd2 );


int CPP_Sub( int nSub1, int nSub2 )

	return ( nSub1 - nSub2 );

3.2 添加静态库的函数

3.3 导入静态库

   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 );

// 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,添加相应的文件。 
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语言方式导出函数

#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 );

3.1.3.3 使用DEF文件导出

 增加扩展名为DEF的文件到项目中.

 DEF文件中添加导出定义.

  LIBRARY dllfunc.dll //导出库

EXPORTS             //导出表

  Dll_Mul @1      //导出函数

  Dll_Div @2

LIBRARY dllfunc.dll
EXPORTS 
	Dll_Mul	@1

3.2 使用

  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 使用函数

  直接函数即可

3.2.1.4 应用程序查找DLL的路径

  1) 查找当前应用程序的目录.

  2) 当前的工作目录

  3) 查找Windows System32的目录

  4) 查找Windows System的目录

  5) 查找Windows目录

  6) 查找环境变量PATH指定路径

// 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.2 显示链接    

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

// 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");
	
	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 定义全局变量

  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

4.3 导入LIB文件

  4.4 定义导入变量

 需要使用__declspec(dllimport)定义变量.

 extern __declspec(dllimport) int g_nValue1;

  4.5 使用变量

// usevalue.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

//导入LIB
#pragma comment( lib, "../lib/dllvalue.lib")
//定义DLL导入变量
extern __declspec(dllimport) int g_nValue1;
extern __declspec(dllimport) int g_nValue2;

int main(int argc, char* argv[])

	printf("g_nValue1 = %d\\n", g_nValue1 );
	printf("g_nValue2 = %d\\n", g_nValue2 );
	return 0;

5 动态库的类

 

  5.1 创建动态库并定义类

  5.2 DLL中导出类

 在类名称前增加__declspec(dllexport)定义.

 class __declspec(dllexport) CMath

  ... ;

  5.3 使用时导入LIB文件

  5.4 导入类

 在类名称前增加__declspec(dllimport)定义.

 class __declspec(dllimport) CMath

  ... ;

  5.5 使用类

  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

... ;

#ifndef _MATH_H_
#define _MATH_H_

//定义类导入导出宏
#ifdef _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_

#include "math.h"

int CMath::Add( int nAdd1, int nAdd2 )

	return ( nAdd1 + nAdd2 );


int CMath::Sub( int nSub1, int nSub2 )

	return ( nSub1 - nSub2 );

// 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;

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  进程卸载

 返回值表示是否加载成功.











以上是关于win32day11-windows的库/静态库/动态库的主要内容,如果未能解决你的问题,请参考以下文章

win32day09-对话框/子控件/静态块/按钮/文本编辑框

用 AutoHotkey 做为 win32 程序的库

Win32编程之静态库编写与使用.动态链接库的编写与使用

包含在自制静态库头文件中使用的库

仅使用所需的库构建可执行文件?

windows中静态库lib和动态dll的区别及使用方法