VS2013 动态链接库下,动态调用和静态调用的注意点事项

来源:互联网 发布:php获取数组第一个key 编辑:程序博客网 时间:2024/05/20 06:24

1.静态链接库

lib.h extern "C" int add(int x,int y);

lib.c int add(int x,int y){ ...}

main.cpp调用如下:

#include "..\lib.h"

#pragma comment(lib,"..\\debug\\libTest.lib") //指定与静态库一起连接

printf("2 + 3 = %d",add(2,3));


2、动态链接库下的动态调用

lib.h extern "C"  int __declspec(dllexport) add(int x,int y);//###这里是dllexport

lib.c int add(int x,int y){ ...}

main.cpp调用如下:

#include "windows.h"

typedef int ( * lpAddFun)(int,int);

int main(int argc, char* argv[])
{
    HINSTANCE hDll;   //DLL句柄
lpAddFun addFun;  //函数指针
    hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add");
if(addFun!=NULL)
{
   int result =  addFun(2,3);    
printf("%d",result);
}
FreeLibrary(hDll);
}
return 0;
}


3、动态链接库下的静态调用

lib.h extern "C" int add(int x,int y);

lib.c int add(int x,int y){ ...}

main.cpp调用如下:

#include "windows.h"


#pragma comment(lib,"dllTest.lib") 
//在link时,应链接dllTest.lib文件
//Lib库中仅仅是关于其对应DLL文件中函数的重定位信息
extern "C" _declspec(dllimport) add(int x,int y);  //###这里是dllimport


int main(int argc, char* argv[])
{
    int result =  add(2,3);    
printf("%d",result);

return 0;
}


4、动态链接库下,也有一个类似于main函数的函数,叫DllMain。(用户没有写的话,系统会自动给出一个隐式的DllMain)

lib.h extern "C" int add(int x,int y);

lib.cpp: start----------------------------------------------------------------------------------------------------------------------------------------------------------------

#include "windows.h"
#include "stdio.h"
// lib.cpp : DLLMain函数的例子
// 作者 : 宋宝华 21cnbao@21cn.com


//如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的
//缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。正如由dwReason参数所
//表明的那样。
BOOL APIENTRY DllMain( HANDLE hModule,    
/*
    进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识进程自己还有一个HINSTANCE句柄。
所有这些模块句柄都只有在特定的进程内部有效,它们代表了DLL或EXE模块在进程虚拟空间中的起始
地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用。进程模块句柄几乎
总是等于0x400000,而DLL模块的加载地址的缺省句柄是0x10000000。如果程序同时使用了几个DLL模
块,每一个都会有不同的HINSTANCE值。这是因为在创建DLL文件时指定了不同的基地址,或者是因为
加载程序对DLL代码进行了重定位。 
*/
DWORD  ul_reason_for_call, 
 LPVOID lpReserved
 )
{
    switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("\nprocess attach of dll");
break;
case DLL_THREAD_ATTACH:
printf("\nthread attach of dll");
break;
case DLL_THREAD_DETACH:
printf("\nthread detach of dll");
break;
case DLL_PROCESS_DETACH:
printf("\nprocess detach of dll");
break;
    }
    return TRUE;
}


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

lib.cpp: end----------------------------------------------------------------------------------------------------------------------------------------------------------------

main.cpp调用如下:

#include "windows.h"

typedef int ( * lpAddFun)(int,int);

int main(int argc, char* argv[])
{
    HINSTANCE hDll;   //DLL句柄
lpAddFun addFun;  //函数指针
    hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add");
if(addFun!=NULL)
{
    int result =  addFun(2,3);    
printf("%d",result);
}
FreeLibrary(hDll);
}
return 0;
}


运行输出如下;

process attach of dll
call add in dll:5
process detach of dll



5、__stdcall约定

如果通过 VC++编写的 DLL 欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall 方式, WINAPI 都采用这种方式,而
C/C++缺省的调用方式却为__cdecl。 

__stdcall 方式与__cdecl 对函数名最终生成符号的方式不同。若采用 C 编译方式(在 C++中需将函数声明为 extern "C"),

__stdcall 调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl
调用约定仅在输出函数名前面加下划线,形如_functionname。

Windows 编程中常见的几种函数类型声明宏都是与__stdcall 和__cdecl 有关的(节选自 windef.h):
#define CALLBACK __stdcall //这就是传说中的回调函数
#define WINAPI __stdcall //这就是传说中的 WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain 的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall


在 lib.h 中,应这样声明 add 函数:

int __stdcall add(int x, int y);

在应用工程中函数指针类型应定义为:

typedef int(__stdcall *lpAddFun)(int, int);


若在 lib.h 中将函数声明为__stdcall 调用,而应用工程中仍使用 typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,
在应用工程中仍然是缺省的__cdecl 调用),弹出如图 7 所示的对话框。



6、 DLL 导出变量

lib.h extern int dllGlobalVar;
extern "C" int GetGlobalVar();

-----------------------------------------------------------------------------------------

lib.cpp

#include "lib.h"
#include <windows.h>
 
int dllGlobalVar;
BOOL APIENTRY DllMain( HANDLE hModule, 
 DWORD  ul_reason_for_call, 
 LPVOID lpReserved
 )
{
    switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
dllGlobalVar = 100;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
    }
    return TRUE;
}


int GetGlobalVar()
{
   return dllGlobalVar;
}

--------------------------------------------------------------------------------

lib.def  //如果不加这个文件,生成Dll的时候,lib不会生成

;在DLL中导出变量
LIBRARY "dllTest"
EXPORTS
  dllGlobalVar CONSTANT  ;或dllGlobalVar DATA
  GetGlobalVar

--------------------------------------------------------------------------------

(1)main.cpp 中使用extern int dllGlobalVar;

#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern "C" int _declspec(dllimport) GetGlobalVar();
extern int dllGlobalVar;


int main(int argc, char* argv[])
{
printf("%d ",GetGlobalVar());


*(int *)dllGlobalVar = 1;
//特别要注意的是这种方法导出的并不是变量本身,而是DLL中导出变量的指针,应用程序必
//须通过强制指针转换来使用  
    
printf("%d ",GetGlobalVar());
return 0;
}


(2)main.cpp 中使用extern int dllGlobalVar;

#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern "C" int _declspec(dllimport) GetGlobalVar();
extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
//通过_declspec(dllimport)方式导入的就是 DLL 中全局变量本身而不再是其地址了


int main(int argc, char* argv[])
{
printf("%d ",GetGlobalVar());


dllGlobalVar = 1; 
//这里就可以直接使用, 无须进行强制指针转换
    
printf("%d ",GetGlobalVar());
return 0;
}



0 0
原创粉丝点击