| 网站首页 | 文章中心 | 电子书下载 | 矢量图库 | 视频教程 | 素材下载 | 程序代码下载 | JS代码 | 论坛 | 
常用软件类:
|杀毒安全 |联络聊天 |网络软件 |多媒体类 |系统工具 |图形图像 |系统工具 |应用软件 |行业软件
开发设计类:
|动画制作 |图像处理 |3D设计 |操作系统 |站长学院 |网络相关 |WEB设计 |数据库类 |程序开发
VC++动态链接库编程之非MFC DLL
作者:未知    文章来源:网络    点击数:    更新时间:2006-8-29
 



  执行下列代码:

hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
 addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
 //MAKEINTRESOURCE直接使用导出文件中的序号
 if (addFun != NULL)
 {
  int result = addFun(2, 3);
  printf("\ncall add in dll:%d", result);
 }
 FreeLibrary(hDll);
}


  我们看到输出顺序为:

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


  这一输出顺序验证了DllMain被调用的时机。

  代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):

#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA


  4.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所示的对话框。


图7 调用约定不匹配时的运行错误


  图8中的那段话实际上已经给出了错误的原因,即“This is usually a result of …”。

4.6 DLL导出变量

  DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据,我们来看看在应用工程中引用DLL中变量的例子(单击此处下载本工程)。

/* 文件名:lib.h */

#ifndef LIB_H
#define LIB_H
extern int dllGlobalVar;
#endif

/* 文件名: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; //在dll被加载时,赋全局变量为100
   break;
  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:
  case DLL_PROCESS_DETACH:
   break;
 }
 return TRUE;
}


  ;文件名:lib.def

  ;在DLL中导出变量

LIBRARY "dllTest"

EXPORTS

dllGlobalVar CONSTANT

;或dllGlobalVar DATA

GetGlobalVar


  从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:

  变量名 CONSTANT   //过时的方法

  或

  变量名 DATA     //VC++提示的新方法

  在主函数中引用DLL中定义的全局变量:

#include <stdio.h>
#pragma comment(lib,"dllTest.lib")

extern int dllGlobalVar;

int main(int argc, char *argv[])
{
 printf("%d ", *(int*)dllGlobalVar);
 *(int*)dllGlobalVar = 1;
 printf("%d ", *(int*)dllGlobalVar);
 return 0;
}


  特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:

dllGlobalVar = 1;


  其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。

  在应用工程中引用DLL中全局变量的一个更好方法是:

#include <stdio.h>
#pragma comment(lib,"dllTest.lib")

extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
int main(int argc, char *argv[])
{
 printf("%d ", dllGlobalVar);
 dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
 printf("%d ", dllGlobalVar);
 return 0;
}


  通过_declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。

上一页  [1] [2] [3] 下一页


相关文章