打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
VC++2008用空工程创建DLL
一、创建 DLL 工程项目:
 1)点击菜单[File] -> [New]-> [Project...] 弹出 “New Project”对话框;
 2)在左侧 [Project types:] 树形框中展开 [Visual C++] 选择[Win32];
 3)在右侧 [Templates:] 视图框中选择 [Win32Project];
 4)在 [Name:]对应的文本框中填写好项目名称;
 5)在 [Location:]对应的文本框中选好项目位置;
 6)点击 [OK] 按钮,弹出 “Win32 Application Wizard -你起的项目名称” 对话框;
 7)点击左侧的 [Application Settings] 或 点击 [Next>] 进入 “Application Settings”选项页;
 8)在 [Application type:] 中选择“DLL”;
 9)在 [Additional options:] 中选择 “Emptyproject”;
10)最后点击 [Finish] 按钮。


二、编写 DLL 导出函数头文件(exampledll.h):
#ifdef DLL_EXPORTS
#define DLL_API_INT _declspec(dllexport) int__stdcall
#else
#define DLL_API_INT _declspec(dllimport) int__stdcall
#endif


#ifdef __cplusplus
// extern "C" 是 C++ 的关键字

extern "C"
{
#endif


DLL_API_INT sum( int a, int b );
DLL_API_INT sub( int a, int b );

#ifdef __cplusplus
}
#endif



注:
1. __declspec(dllexport) 声明函数为 DLL 的导出函数,
   它就是为了省掉在 *.def文件中手工定义导出哪些函数的一个方法,
   如果你的 DLL 里全是 C++ 的类的话,你无法在*.def 里指定导出的函数,
   只能用 __declspec(dllexport)导出类;

; 举例 exampledll.def 文件内容
LIBRARY exampledll
EXPORTS
sum @ 1
sub @ 2
var DATA

.def文件的规则为:
注:需要在工程属性中指定该*.def文件,位置:
[Configuration Properties] ->[Linker] -> [Input] -> "ModuleDefinition File"
不用 *.def文件导出函数的话,函数名将是按编译器的命名规则导出。

1)LIBRARY 关键字后跟该 def 文件对应的 DLL 工程名;
2)EXPORTS 关键字独占一行,下面每行列出要导出函数或变量的名称;
  可以在要导出的函数名后加 @数字,即为要导出函数排序,在动态加载导出函数时,此序号有用;
  若要导出某全局变量,要有如下格式:
  变量名 CONSTANT <-- 过时的方法,变量名后跟CONSTANT 关键字
 
  变量名 DATA   <--VC++提示的新方法,变量名后跟 DATA 关键字
  注意:
  用 extern 声明导入的并不是 DLL中全局变量本身,而是其地址,
  应用程序必须先通过类型强制转换指针,再间接取/赋值来使用 DLL中的全局变量,如:
  extern int var;
  *(int*)var = 1;
  通过 _declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,
  建议尽可能的情使用这种方式,如:
  extern int _declspec(dllimport)var;
  var = 1;
3)分号(;) 为行注释符且注释不能与语句共享一行,要独占一行,以分号开头。

2. __declspec(dllimport) 声明函数为 DLL 的导入函数,
   不使用 __declspec(dllimport)也能正确编译代码,
   但使用 __declspec(dllimport)使编译器可以生成更好的代码,
  编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL中,
  这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL边界的函数调用中。
   但是必须使用__declspec(dllimport) 才能导入 DLL 中使用的变量,
   原来 __declspec(dllimport)是为了更好的处理类中的静态成员变量的,
   如果没有静态成员变量,那么这个__declspec(dllimport) 无所谓;

3. 函数的调用方式更改:
   方法一:
   1)右击 "Solution Explorer"中的项目名;
   2)选择"Properties";
   3)在弹出的对话框左侧树形框中选择[Configuration Properties] -> [C/C++]-> [Advanced];
   4)在弹出的对话框右侧栅栏框中修改 "CallingConvention" 项对应值。
   方法二:
   microsoft 的 VC 默认的是 __cdecl方式,而 windows API 则是 __stdcall ,
   如果用 VC 开发 dll 给其他语言用,则应该指定__stdcall 方式。
   如果是 __cdecl 方式的函数,则函数本身则不需要关心保存参数的堆栈的清除,
   如果是 __stdcall方式的函数,则一定要在函数退出前恢复堆栈。

   1)__cdecl 所谓的 C调用规则。按从右至左的顺序压参数入栈,由调用者把参数弹出栈。
    切记:对于传送参数的内存栈是由调用者来维护的。
        返回值在EAX 中因此,对于象 printf 这样变参数的函数必须用这种规则。
       编译器在编译的时候对这种调用规则的函数生成修饰名的时候,
       仅在输出函数名前加上一个下划线前缀,格式为 _functionname。 

   2)__stdcall按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。
    __stdcall 是 Pascal程序的缺省调用方式,
     通常用于Win32 Api 中,切记:函数自己在退出时清空堆栈,返回值在EAX中。
    __stdcall 调用约定在输出函数名前加上一个下划线前缀,后面加上一个 @符号和其参数的字节数,
     格式为_functionname@number 。
     如函数 intfunc(int a, double b) 的修饰名是 _func@12 。

   3)__fastcall调用的主要特点就是快,
    因为它是通过寄存器来传送参数的,
     实际上,它用ECX 和 EDX 传送前两个双字(DWORD)或更小的参数,
    剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。
    __fastcall 调用约定在输出函数名前加上一个 @ 符号,后面也是一个 @符号和其参数的字节数,
     格式为@functionname@number 。
     这个和__stdcall 很象,唯一差别就是头两个参数通过寄存器传送。
    注意通过寄存器传送的两个参数是从左向右的,即第一个参数进ECX,第2个进EDX,
    其他参数是从右向左的入栈。返回仍然通过EAX。

   4)__pascal 这种规则从左向右传递参数,通过EAX 返回,堆栈由被调用者清除。

   5)__thiscall 仅仅应用于 C++成员函数。
     this指针存放于 CX 寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

  修饰符的书写顺序如下:
   extern "C"_declspec(dllexport) int __stdcall sum(int a, intb);
   typedef int (__cdecl*FunPointer)(int a, int b);

4.如果要对编译器提示使用 C 的方式来处理函数的话,那么就要使用 extern "C"来说明。

三、编写 DLL 导出函数实现文件(exampledll.c):
#ifndef DLL_EXPORTS
#define DLL_EXPORTS
#endif

#include "windows.h"  
#include "exampledll.h"

 
#pragma comment(linker,"/section:shared,rws") 
#pragma data_seg("shared")
int var = 0;  
#pragma data_seg()


extern "C" BOOL __stdcall 
DllMain( HINSTANCEhInstance, 
        DWORD    dwReason, 
        LPVOID   lpReserved)
{  
    switch( dwReason)
    {
       case DLL_PROCESS_ATTACH:
           returnTRUE;
       case DLL_PROCESS_DETACH:
           returnTRUE;
       case DLL_THREAD_ATTACH:
           returnTRUE;
       case DLL_THREAD_DETACH:
           returnTRUE;
       default:
           returnTRUE;
    }
}  

DLL_API_INT sum( int a, int b )
{
    return a +b;
}

DLL_API_INT sub( int a, int b )
{
    return a -b;
}

四、编译生成 DLL 即可!
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
VC 动态链接库(DLL)编程深入浅出
__cdecl __fastcall与 __stdcall
C/C++:函数的编译方式与调用约定以及extern “C”的使用
C#程序调用非托管C++ DLL文件的方法 - Chase的技术博客 - 博客园
总结一下DLL函数声明的一些问题
C#调用C/C++ DLL方式
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服