zoukankan      html  css  js  c++  java
  • VC++2008 用空工程创建 DLL

    VC++2008 用空工程创建 DLL

    一、创建 DLL 工程项目:
     1)点击菜单[File] -> [New] -> [Project...] 弹出 “New Project” 对话框;
     2)在左侧 [Project types:] 树形框中展开 [Visual C++] 选择 [Win32];
     3)在右侧 [Templates:] 视图框中选择 [Win32 Project];
     4)在 [Name:] 对应的文本框中填写好项目名称;
     5)在 [Location:] 对应的文本框中选好项目位置;
     6)点击 [OK] 按钮,弹出 “Win32 Application Wizard - 你起的项目名称” 对话框;
     7)点击左侧的 [Application Settings] 或 点击 [Next >] 进入 “Application Settings” 选项页;
     8)在 [Application type:] 中选择 “DLL”;
     9)在 [Additional options:] 中选择 “Empty project”;
    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] -> "Module Definition 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)在弹出的对话框右侧栅栏框中修改 "Calling Convention" 项对应值。
       方法二:
       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 。
         如函数 int func(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, int b);
       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( HINSTANCE hInstance, 
             DWORD     dwReason, 
             LPVOID    lpReserved)
    {  
        switch( dwReason )
        {
            case DLL_PROCESS_ATTACH:
                return TRUE;
            case DLL_PROCESS_DETACH:
                return TRUE;
            case DLL_THREAD_ATTACH:
                return TRUE;
            case DLL_THREAD_DETACH:
                return TRUE;
            default:
                return TRUE;
        }
    }  

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

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

    四、编译生成 DLL 即可!
     
  • 相关阅读:
    GDI+ 实现透明水印和文字
    delphi调用LUA函数来处理一些逻辑
    Delphi 不使用自带模板创建服务
    Delphi在Listview中加入Edit控件
    中文转码器的工作原理_delphi教程
    使用钩子函数[6]
    简单全局HOOK拦截大部分键盘消息
    4个字节就相当于移动一位,原来指针是这样用的
    C#调用Delphi接口(ITest = interface)
    DELPHI 对象的本质 VMT
  • 原文地址:https://www.cnblogs.com/kinyer/p/3328358.html
Copyright © 2011-2022 走看看