精华内容
下载资源
问答
  • DLL封装

    2013-11-24 17:54:20
    今天看到一点DLL封装的东西,也有博友的博文。自己留下来。博文源址:http://blog.csdn.net/pbimage/article/details/9939113 [cpp] view plaincopy #ifdef WIN32 #ifdef DLL_EXPORTS #define ...

    今天看到一点DLL封装的东西,也有博友的博文。自己留下来。博文源址:http://blog.csdn.net/pbimage/article/details/9939113

    1. #ifdef WIN32  
    2. #ifdef DLL_EXPORTS  
    3. #define EXPORT_CLASS   __declspec(dllexport)  
    4. #define EXPORT_API  extern "C" __declspec(dllexport)  
    5. #else  
    6. #define EXPORT_CLASS   __declspec(dllimport )  
    7. #define EXPORT_API  extern "C" __declspec(dllimport )  
    8. #endif  
    9. #else  
    10. #define EXPORT_CLASS  
    11. #define EXPORT_API  
    12. #endif  

    关于DLL封装的几个注意点:

    1. extern "C"  
    1. __declspec(dllexport)  
    2. __declspec(dllimport)  
    1. #define STDCALL __stdcall  
    2. #define CDECL __cdecl 

    另外的分析:http://blog.csdn.net/pbimage/article/details/7282002

    C++中extern “C”含义深层探索

    分类: C/C++ 96人阅读 评论(0) 收藏 举报
    1.引言

      C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++保留了一部分过程 式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言,为了支 持函数的重载,C++对全局函数的处理方式与C有明显的不同。
    2.从标准头文件说起

      某企业曾经给出如下的一道面试题:

      面试题
    为什么标准头文件都有类似以下的结构?

       #ifndef __INCvxWorksh
    #define __INCvxWorksh
    #ifdef __cplusplus
    extern"C"{
    #endif
    /**//**/
    #ifdef __cplusplus
    }

    #endif
    #endif /* __INCvxWorksh */


    分析
    显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。

      那么

    #ifdef __cplusplus
    extern"C"{
    #endif
    #ifdef __cplusplus
    }

    #endif


    的作用又是什么呢?我们将在下文一一道来。

    3.深层揭密extern "C"

      extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。

      被extern "C"限定的函数或变量是extern类型的;

      extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:

      extern int a;


    仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

      通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变 量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从 模块A编译生成的目标代码中找到此函数。

      与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

      被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;

      未加extern “C”声明时的编译方式

      首先看看C++中对类似C的函数是怎样编译的。

      作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

    void foo( int x, int y );


    该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。

      _foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
    同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。 用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二 的名字,这个名字与用户程序中同名的全局变量名字不同。

      未加extern "C"声明时的连接方式

      假设在C++中,模块A的头文件如下:


    // 模块A头文件 moduleA.h
      #ifndef MODULE_A_H
    #define MODULE_A_H
    int foo(int x,int y );
    #endif



    在模块B中引用该函数:

    // 模块B实现文件 moduleB.cpp
    #include "moduleA.h"
    foo(2,3);


    实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

      加extern "C"声明后的编译和连接方式

      加extern "C"声明后,模块A的头文件变为:

    // 模块A头文件 moduleA.h
    #ifndef MODULE_A_H
    #define MODULE_A_H
    extern"C"int foo( int x, int y );
    #endif


    在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:

      (1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

      (2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

      如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。

      所以,可以用一句话概括extern“C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么 做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
    实现C++与C及其它语言的混合编程。
    明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧。
    4.extern "C"的惯用法

      (1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

    extern"C"
    {
    #include
    "cExample.h"
    }


    而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。

      笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:

       
    /**//* c语言头文件:cExample.h*/
    #ifndef C_EXAMPLE_H
    #define C_EXAMPLE_H
    externint add(int x,int y);
    #endif
    /**//* c语言实现文件:cExample.c */
    #include
    "cExample.h"
    int add(int x,int y )
    {
    return x+ y;
    }

    // c++实现文件,调用add:cppFile.cpp
    extern"C"
    {
    #include
    "cExample.h"
    }

    int main(int argc,char* argv[])
    {
    add(
    2,3);
    return0;
    }




    如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。

      (2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
    笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:

    //C++头文件 cppExample.h
    #ifndef CPP_EXAMPLE_H
    #define CPP_EXAMPLE_H
    extern"C"int add( int x, int y );
    #endif
    //C++实现文件 cppExample.cpp
    #include"cppExample.h"
    int add(int x,int y )
    {
    return x+ y;
    }

    /**//* C实现文件 cFile.c
    /* 这样会编译出错:#include "cExample.h"
    */

    externint add(int x, int y );
    int main(int argc,char* argv[] )
    {
    add(
    2,3 );
    return0;
    }

    如果深入理解了第3节中所阐述的extern "C"在编译和连接阶段发挥的作用,就能真正理解本节所阐述的从C++引用C函数和C引用C++函数的惯用法。对第4节给出的示例代码,需要特别留意各个细节。


    展开全文
  • dll封装

    2013-03-04 22:43:46
    视频: 孙鑫VC视频教程 动态链接库 ...VS2010中 C++创建DLL图解. 一、DLL的创建  创建项目: Win32->Win32项目,名称:MyDLL 选择DLL (D) ->完成. 1、新建头文件testdll.h testdll.h代码如下:

     

    视频: 孙鑫VC视频教程 动态链接库 http://v.youku.com/v_show/id_XMjE5MDMwODY0.html

    VS2010中 C++创建DLL图解.

    一、DLL的创建 
    创建项目: Win32->Win32项目,名称:MyDLL


    选择DLL (D) ->完成.

    1、新建头文件testdll.h
    testdll.h代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef TestDll_H_
    #define TestDll_H_
    #ifdef MYLIBDLL
    #define MYLIBDLL extern "C" _declspec(dllimport) 
    #else
    #define MYLIBDLL extern "C" _declspec(dllexport) 
    #endif
    MYLIBDLL int Add(int plus1, int plus2);
    //You can also write like this:
    //extern "C" {
    //_declspec(dllexport) int Add(int plus1, int plus2);
    //};
    #endif



    2、新建源文件testdll.cpp
    testdll.cpp代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "stdafx.h"
    #include "testdll.h"
    #include <iostream>
    using namespace std;
    int Add(int plus1, int plus2)
    {
    int add_result = plus1 + plus2;
    return add_result;
    }



    3、新建模块定义文件mydll.def
    mydll.def代码如下:

    1
    2
    3
    LIBRARY "MyDLL"
    EXPORTS
    Add @1




    4、vs2010自动创建dllmain.cpp文件,它定义了DLL 应用程序的入口点。

    dllmain.cpp代码如下:
    // dllmain.cpp : 定义 DLL 应用程序的入口点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include "stdafx.h"
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
          )
    {
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
    }


    最后,编译生成MyDLL.dll文件和MyDLL.lib文件。

    1>------ 已启动生成: 项目: MyDLL, 配置: Debug Win32 ------

    1>  dllmain.cpp

    ========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========

     

    1>------ 已启动生成: 项目: MyDLL, 配置: Debug Win32 ------

    1>  stdafx.cpp

    1>  testdll.cpp

    1>  MyDLL.cpp

    1>  正在生成代码...

    1>     正在创建库 D:\Visual C++\工程\Libaray\MyDLL\Debug\MyDLL.lib 和对象 D:\Visual C++\工程\Libaray\MyDLL\Debug



     

     

     

    今天教大家在VC中创建DLL文件的方法步骤,一起来学习一下,并不是很难的,相信聪明的你一看就会。

      一.Win32动态链接库

      1.制作的步骤:

      (1)新建WIN32 Dynamic-link Library工程,工程名为MyDll,选择A simple DLL project类型。

      (2)MyDll.h的内容如下:

     

    以下是引用片段:
      extern "C" _declspec(dllexport) int sum(int a,int b);//本文所有的例子只有一个sum即加法函数。

     

      (3)MyDll.cpp的内容如下:

     

    以下是引用片段:
      #include "stdafx.h"
      #include "windows.h"
      #include "MyDll.h"
      BOOL APIENTRY DllMain(
      HANDLE hModule,
      DWORD ul_reason_for_call,
      LPVOID lpReserved
      )
      {
      return TRUE;
      }
      extern "C" _declspec(dllexport)int sum(int a, int b)
      {
      return a+b;
      }

     

      (4)编译之后产生了MyDll.lib与MyDll.dll两个文件。

      2.使用方法:

      (1).隐式调用法: 将MyDll.lib和MyDll.h拷贝到需要应用该DLL的工程的目录下,将MyDll.dll拷贝到产生的应用程序的目录下,并在需要应用该DLL中的函数的CPP文件中添加如下几行:

     

    以下是引用片段:
      #include "MyDll.h"
      #pragma comment(lib,"MyDll");

     

      (2).显示调用法:将MyDll.lib和MyDll.h拷贝到需要应用该DLL的工程的目录下,将MyDll.dll拷贝到产生的应用程序的目录下,并在需要应用该DLL中的函数的CPP文件中包含头文件,如:

     

    以下是引用片段:
      #include "MyDll.h"

     

      同时还需要在Project->Setting->Link->Object/library modules的框中增加MyDll.lib这个库。

      二.MFC动态链接库

      1.制作的步骤:

      (1)新建MFC AppWizard(dll)工程,工程名为MFCDll,选择Regular DLL using shared MFC DLL类型。

      (2)在生成的MFCDll.cpp文件后面增加下面几行:

     

    以下是引用片段:
      int sum(int a, int b)
      {
      return a+b;
      }

     

      (3)在生成的MFCDll.def文件后面增加如下:

     

    以下是引用片段:
      sum @1 ;表示第一个函数是sum

     

      (4)编译后会产生两个文件MFCDll.lib,MFCDll.dll

      2.使用方法

      (1)隐式调用法: 将MFCDll.lib拷贝到需要应用该DLL的工程的目录下,将MyDll.dll拷贝到产生的应用程序的目录下,并在需要应用该DLL中的函数的CPP文件中添加如下几行:

      //注意这里没有在MFCDll.h中声明函数,所以不能直接包含MFCDll.h来声明函数。

     

    以下是引用片段:
      #pragma comment(lib,"MFCDll");
      int sum(int a, int b);

     

      //当然如果你的DLL中有很多函数,那可以另外写个MFCDll.h,包含所有的函数声明,然后直接将头文件包含进去

      (2)显示调用法:与Win32的调用方法一样,不需要#pragma comment(lib,"MFCDll");,但是需要在Project->Setting->Link->Object/library modules的框中增加MFCDll.lib这个库。


    DLL文件与exe文件的区别:

    动态链接库(Dynamic Link Library,缩写为DLL)
    是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件的扩展名一般是dll,也有可能是drv、sys和fon,它和可执行文件(exe)非常类似.

     

    区别
    DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。
    动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。
    而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。


    仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码

     

     

    初识dll,在VS2010平台上创建并使用dll(revised)            

            分类:            VC++            组件技术                 8069人阅读     评论(16)    收藏    举报    

    一、为什么需要dll

    代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,如ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。

    1. 暴露了源代码;
    2. 容易与程序员的“普通”代码发生命名冲突;
    3. 多份拷贝,造成存储浪费;
    4. 更新功能模块比较困难。

    实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。

    在Windows操作系统中有两种可执行文件,其后缀名分别为.exe和.dll。它们的区别在于,.exe文件可被独立的装载于内存中运行;.dll文件却不能,它只能被其它进程调用。然而无论什么格式,它们都是二进制文件。上面说到的“二进制级别”的代码复用,可以使用.dll来实现。

    与白盒复用相比,.dll很大程度上弥补了上述4大缺陷。.dll是二进制文件,因此隐藏了源代码;如果采用“显式调用”(后边将会提到),一般不会发生命名冲突;由于.dll是动态链接到应用程序中去的,它并不会在链接生成程序时被原原本本拷贝进去;.dll文件相对独立的存在,因此更新功能模块是可行的。

    说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。本文只对dll进行讨论。

    二、创建dll

    接下来用一个简单的例子来说明创建dll的方法。本例采用VS2010,使用C++编程语言,具体操作步骤如下。

    通过Start Page或者File菜单栏,新建一个Project,将会弹出新建项目对话框。选择Win32 Project向导,项目名为CreateDLL,解决方案名为DLLTEST(注意Create directories for solution是勾选上的),点击OK,接着点击Next,到Application Settings,选择应用程序类型为dll,并勾选“Export Symbols”,点击Finish。完成这一步之后,VS界面上左边的Solution Explorer中将会看到向导自动生成的文件列表,如图1所示。

    图1 wizard自动生成的文件列表

    在VS界面的编辑窗口中,展示了自动生成的CreateDLL.cpp的代码。

    1. // CreateDLL.cpp : Defines the exported functions for the DLL application.  
    2. //  
    3.   
    4. #include "stdafx.h"  
    5. #include "CreateDLL.h"  
    6.   
    7.   
    8. // This is an example of an exported variable  
    9. CREATEDLL_API int nCreateDLL = 0;  
    10.   
    11. // This is an example of an exported function.  
    12. CREATEDLL_API int fnCreateDLL(void)  
    13. {  
    14.     return 42;  
    15. }  
    16.   
    17. // This is the constructor of a class that has been exported.  
    18. // see CreateDLL.h for the class definition  
    19. CCreateDLL::CCreateDLL()  
    20. {  
    21.     return;  
    22. }  
    // CreateDLL.cpp : Defines the exported functions for the DLL application.
    //
    
    #include "stdafx.h"
    #include "CreateDLL.h"
    
    
    // This is an example of an exported variable
    CREATEDLL_API int nCreateDLL = 0;
    
    // This is an example of an exported function.
    CREATEDLL_API int fnCreateDLL(void)
    {
    	return 42;
    }
    
    // This is the constructor of a class that has been exported.
    // see CreateDLL.h for the class definition
    CCreateDLL::CCreateDLL()
    {
    	return;
    }
    

    这里有3种类型的example,分别为导出变量nCreateDLL、导出函数fnCreateDLL以及导出类CCreateDLL。为了简化起见,本例只考虑导出函数。修改CreateDLL.h文件为:

    1. #ifdef CREATEDLL_EXPORTS  
    2. #define CREATEDLL_API __declspec(dllexport)  
    3. #else  
    4. #define CREATEDLL_API __declspec(dllimport)  
    5. #endif  
    6.   
    7. CREATEDLL_API void printMax(int&,int&);  
    8. CREATEDLL_API void printMax(int&,int&,int&);  
    #ifdef CREATEDLL_EXPORTS
    #define CREATEDLL_API __declspec(dllexport)
    #else
    #define CREATEDLL_API __declspec(dllimport)
    #endif
    
    CREATEDLL_API void printMax(int&,int&);
    CREATEDLL_API void printMax(int&,int&,int&);

    修改CreateDLL.cpp文件为: 

    1. CREATEDLL_API void printMax(int& a,int& b)  
    2. {  
    3.     std::cout<<"Among ("<<a<<","<<b<<"), the Max Number is "<<(a>b?a:b)<<"\n";  
    4. }  
    5. CREATEDLL_API void printMax(int& a,int& b,int& c)  
    6. {  
    7.     std::cout<<"Among ("<<a<<","<<b<<","<<c<<"), the Max Number is "<<(((a>b?a:b)>c)?(a>b?a:b):c)<<"\n";  
    8. }  
    CREATEDLL_API void printMax(int& a,int& b)
    {
    	std::cout<<"Among ("<<a<<","<<b<<"), the Max Number is "<<(a>b?a:b)<<"\n";
    }
    CREATEDLL_API void printMax(int& a,int& b,int& c)
    {
    	std::cout<<"Among ("<<a<<","<<b<<","<<c<<"), the Max Number is "<<(((a>b?a:b)>c)?(a>b?a:b):c)<<"\n";
    }

    不难发现,printMax函数的作用就是打印出两个整数或三个整数中的最大值。需要说明的是,这里故意使用同名函数是为了引出导出函数的修饰名称,具体将在第四节中阐述。

    接下来,选择菜单Build->Build CreateDLL,Output窗口提示CreateDLL.dll文件生成成功,如图2所示。

     图2 CreateDLL.dll成功生成

    三、使用dll

    本例采用“显式调用”的方式使用CreateDLL.dll。显式调用方式相比于”隐式调用“有好有坏。显式调用只需要一个.dll文件就可以了,灵活性更好,更新模块方便;相对的,程序员需要做的事情更多,使用方法更为复杂。

    右键单击Solution Explorer中的Solution 'DLLTEST',在弹出的菜单中选择Add->New Project,选择Win32 Console Application,输入项目名为UseDLL,点击OK,接着点击Next,在Application Settings界面勾选EmptyProject并点击Finish。右键单击项目UseDLL,给它添加源文件UseDLL.cpp。这样操作之后,Solution Explorer的信息如图3所示。

    图3 向Solution'DLLTEST'添加项目UseDLL

    编写UseDLL.cpp的代码为:

    1. /*--UseDLL.cpp 
    2.  *Author: ume(李优米) 
    3.  *Use CreateDLL.dll explicitly 
    4.  */  
    5. #include<Windows.h>  
    6. #include<iostream>  
    7. typedef void(*FUNA)(int&,int&);  
    8. typedef void(*FUNB)(int&,int&,int&);  
    9. int main()  
    10. {  
    11.     const char* dllName = "CreateDLL.dll";  
    12.     const char* funName1 = "printMax";  
    13.     const char* funName2 = "printMax";  
    14.     int x(100), y(100), z(100);  
    15.     HMODULE hDLL = LoadLibrary(dllName);  
    16.     if(hDLL != NULL)  
    17.     {  
    18.         FUNA fp1 = FUNA(GetProcAddress(hDLL,funName1));  
    19.         if(fp1 != NULL)  
    20.         {  
    21.             std::cout<<"Input 2 Numbers:";  
    22.             std::cin>>x>>y;  
    23.             fp1(x,y);  
    24.         }  
    25.         else  
    26.         {  
    27.             std::cout<<"Cannot Find Function "<<funName1<<std::endl;  
    28.         }  
    29.         FUNB fp2 = FUNB(GetProcAddress(hDLL,funName2));  
    30.         if(fp2 != NULL)  
    31.         {  
    32.             std::cout<<"Input 3 Numbers:";  
    33.             std::cin>>x>>y>>z;  
    34.             fp2(x,y,z);  
    35.         }  
    36.         else  
    37.         {  
    38.             std::cout<<"Cannot Find Function "<<funName2<<std::endl;  
    39.         }  
    40.         FreeLibrary(hDLL);  
    41.     }  
    42.     else  
    43.     {  
    44.         std::cout<<"Cannot Find "<<dllName<<std::endl;  
    45.     }  
    46.     return 1;  
    47. }  
    /*--UseDLL.cpp
     *Author: ume(李优米)
     *Use CreateDLL.dll explicitly
     */
    #include<Windows.h>
    #include<iostream>
    typedef void(*FUNA)(int&,int&);
    typedef void(*FUNB)(int&,int&,int&);
    int main()
    {
    	const char* dllName = "CreateDLL.dll";
    	const char* funName1 = "printMax";
    	const char* funName2 = "printMax";
    	int x(100), y(100), z(100);
    	HMODULE hDLL = LoadLibrary(dllName);
    	if(hDLL != NULL)
    	{
    		FUNA fp1 = FUNA(GetProcAddress(hDLL,funName1));
    		if(fp1 != NULL)
    		{
    			std::cout<<"Input 2 Numbers:";
    			std::cin>>x>>y;
    			fp1(x,y);
    		}
    		else
    		{
    			std::cout<<"Cannot Find Function "<<funName1<<std::endl;
    		}
    		FUNB fp2 = FUNB(GetProcAddress(hDLL,funName2));
    		if(fp2 != NULL)
    		{
    			std::cout<<"Input 3 Numbers:";
    			std::cin>>x>>y>>z;
    			fp2(x,y,z);
    		}
    		else
    		{
    			std::cout<<"Cannot Find Function "<<funName2<<std::endl;
    		}
    		FreeLibrary(hDLL);
    	}
    	else
    	{
    		std::cout<<"Cannot Find "<<dllName<<std::endl;
    	}
    	return 1;
    }

    代码比较长,但是并不难理解,这里仅说明代码中的一些要点。

    • 包含头文件Windows.h,原因在于程序中用到了LoadLibrary、FreeLibrary、GetProcAddress等Win32 API函数。
    • FUNA和FUNB是函数指针类型的声明。
    • 当程序不再使用dll时,应该调用FreeLibrary及时释放它占用的内存空间。
    • 如果在const char* dllName和funName底部出现红色波浪线提示,说明采用的字符集不匹配,需要修改项目UseDLL的属性CharaterSet为Not Set。
    • 为方便项目的调试,建议修改解决方案的Startup Project属性为Single startup project并以UseDLL为首选。

    然而,这个程序还有错误。编译并运行,结果如图4所示。

                    图4 UseDLL的运行结果

    这并不是期望中的结果。实际上,正如第二节提到的那样,造成这种错误的原因正是导出函数的修饰名称。虽然在CreateDLL.cpp中两个printMax函数有相同的名称,但在dll二进制文件中,经过编译器的“加工”,它们实际上各自有不同的名称了。这也是函数重载机制得以实现的一个技术支持。

    使用VS2010附带工具dumpbin,查看CreateDLL.dll的导出函数名,结果如图5所示。

     图5 查看CreateDLL.dll的导出函数名

    观察图5可以发现,CreateDLL.dll导出函数名为?printMax@@YAXAAH00@Z和?printMax@@YAXAAH0@Z。它们分别对应着三个整数的printMax和两个整数的printMax。因此,Use.DLL中funName应当相应修改为:

    1. const char* funName1 = "?printMax@@YAXAAH0@Z";  
    2. const char* funName2 = “?printMax@@YAXAAH00@Z”;  
    const char* funName1 = "?printMax@@YAXAAH0@Z";
    const char* funName2 = “?printMax@@YAXAAH00@Z”;

    修改之后,再次编译运行,结果正确,如图6所示。

     图6 UseDLL正常运行

    四、dll导出函数名称规范化

    创建、使用dll并不复杂,走过前三节,相信读者肯定有这样的体会。然而,一个问题仍然值得思考:导出函数的修饰名称太“奇怪”,为dll的使用带来了不便,能不能让导出函数的修饰名称规范一些?

    答案是肯定的,而且方法至少有两种:一是运用extern "C"修饰printMax;二是运用模块定义文件.def。后者的效果更好,所以本节将使用.def来规范化导出函数的修饰名称。

    CreateDLL.dll导出的两个函数功能很简单,根据功能描述,理想的函数名称是pMaxA2和pMaxA3。在CreateDLL项目中添加CreateDLL.def文件:

    1. LIBRARY CreateDLL  
    2. EXPORTS  
    3. pMaxA2 = ?printMax@@YAXAAH0@Z  
    4. pMaxA3 = ?printMax@@YAXAAH00@Z  
    LIBRARY CreateDLL
    EXPORTS
    pMaxA2 = ?printMax@@YAXAAH0@Z
    pMaxA3 = ?printMax@@YAXAAH00@Z

    重新build项目CreateDLL,使用dumpbin再次查看CreateDLL.dll的导出函数名称,结果如图7所示。

    图7 规范化的函数名,奇怪的修饰名称还存在

    出现了期望的结果,但仍有小缺憾:奇怪的修饰名称仍然存在。能否去掉这些不太规范的修饰名称呢?当然是可以的。只需要将CreateDLL.h中#define CREATEDLL_API __declspec(dllexport) 修改为#define CREATEDLL_API即可。修改之后重新编译生成CreateDLL.dll,使用dumpbin查看导出函数名称,结果如图8所示。

    图8 规范化的函数名,去除了奇怪的修饰名称    

    回到UseDLL.cpp,修改funName:

    1. const char* funName1 = "pMaxA2";  
    2. const char* funName2 = "pMaxA3";  
    const char* funName1 = "pMaxA2";
    const char* funName2 = "pMaxA3";

    重新编译运行UseDLL,结果正确,与图6类似。
    五、dll的不足

    动态链接库虽然一定程度上实现了“黑盒复用”,但仍存在着诸多不足,笔者能够想到的有下面几点。

    1. dll节省了编译期的时间,但相应延长了运行期的时间,因为在使用dll的导出函数时,不但要加载dll,而且程序将会在模块间跳转,降低了cache的命中率。
    2. 若采用隐式调用,仍然需要.h、.lib、.dll文件(“三件套”),并不能有效支持模块的更新。
    3. 显式调用虽然很好地支持模块的更新,但却不能导出类和变量。
    4. dll不支持Template。

    二进制级别的代码复用相比源码级别的复用已经有了很大的进步,但在二进制级别的代码复用中,dll显得太古老。想真正完美实现跨平台、跨语言的黑盒复用,采用COM才是正确的选择。

     

    展开全文
  • C语言DLL封装DLL,嵌套封装DLL ctypes没能解决c_int类型会变成c_long类型这个问题。所以调库失败,有大手子可以教教我 x = c_int(0) x.value=1 print("type(x) is "+str(type(x))) type(x) is <class 'ctypes....

    C语言DLL封装DLL,嵌套封装DLL

    ctypes没能解决c_int类型会变成c_long类型这个问题。所以调库失败,有大手子可以教教我

        x = c_int(0)
        x.value=1
        print("type(x) is "+str(type(x)))
    
    type(x) is <class 'ctypes.c_long'>
    

    经过大佬的建议,重新封装原有的库,再嵌套一个DLL库,并再c环境里转换类型

    目前有的文件,还挺多

    SI_errors.h
    SI_sensor.h
    SI_types.h
    SpecSensor.dll
    SpecSensor.lib
    

    方法

    1. vs 20**版本,新建c/c++,新建控制台程序,选择DLL,后面不改,生成空文件后面自己添加比较麻烦,直接下一步
    2. 自己加的.c文件里添加如下,头文件我直接没改,把引用和输出都放在了.c文件了
    #include "stdafx.h"
    #include <string>
    #include <tchar.h>
    
    #include "SI_errors.h"
    #include "SI_sensor.h"
    #include "SI_types.h"
    
    extern "C" __declspec(dllexport)  int mine_SI_Open(long DeviceIndex, SI_H* Handle);
    extern "C" __declspec(dllexport)  void show(int input);
    
    
    void show(int input)
    {
    	printf("the input is%d", input);
    }
    int mine_SI_Open(long DeviceIndex, SI_H* Handle)
    {
    	typedef int (*FunDLL)(int, SI_H);  //采用指针指向dll里面的函数
    	HMODULE hDll = LoadLibrary(_T("SpecSensor.dll"));
    	int device = DeviceIndex;
    	if (hDll != NULL)
    	{
    		FunDLL SI_Open = (FunDLL)GetProcAddress(hDll, "SI_Open"); //获取函数入口地址
    		return SI_Open(device, &Handle);
    	}
    	FreeLibrary(hDll);
    	return 724;
    }
    

    _T 再头文件#include <tchar.h>
    SI_Open()这个函数是原来的SpecSensor.dll里
    的函数,就是函数类型换不过,所以才重新封装,这里就有嵌套了,其实和单层封装没有任何区别,只要最后把SpecSensor.dll等文件和第二次封装的dll文件一同放在你需要调用的地方就ok了
    3. 点生成解决方案,主要要看你后面用这个库是在64位环境还是32位,如果是64位,那就再工程里配置属性改成x64就好了
    4. 新工程文件调用,我这里是py

    #导入库,调用函数就行了
    FX = ctypes.cdll.LoadLibrary
    lib_ctype = FX("./driver/fx17/Ctype.dll")
    lib_ctype.show(77)
    
    
    ·······输出
    the input is77
    

    容易出现的问题

    未产生lib 文件,__declspec(dllexport)如下

    extern "C" __declspec(dllexport) int add(int a, int b)
    

    c语言的源文件生成库,没有加extern "C",不过调用仿佛不成功。
    可能原因是用的是C++的编译器编译的,还是加上extern "C"为好

    展开全文
  • c# dll封装以及调用

    2017-08-29 16:03:47
    c# dll封装以及调用
  • dll封装MFC对话框资源

    2015-12-11 16:41:44
    dll封装MFC对话框资源,博客中的源码工程。可以参看dll的资源封装以及使用。
  • DLL封装project

    2009-08-23 11:10:20
    dll封装project vba代码实例代码
  • DLL封装MDI窗体(DLL)

    2019-09-26 14:23:42
    DLL封装MDI窗体(DLL) 一:新建DLL View Code library TestMDIDll;{ Important note about DLL memory management: ShareMem must be thefirst uni...

    一:新建DLL

    View Code
    library TestMDIDll;

    { Important note about DLL memory management: ShareMem must be the
    first unit in your library's USES clause AND your project's (select
    Project-View Source) USES clause if your DLL exports any procedures or
    functions that pass strings as parameters or function results. This
    applies to all strings passed to and from your DLL--even those that
    are nested in records and classes. ShareMem is the interface unit to
    the BORLNDMM.DLL shared memory manager, which must be deployed along
    with your DLL. To avoid using BORLNDMM.DLL, pass string information
    using PChar or ShortString parameters.
    }

    uses
    SysUtils,
    Classes,
    Forms,
    Windows,
    UDllMain
    in'UDllMain.pas'{frmDLLMain};

    {$R *.res}
    var
    DllApplication: TApplication;
    DllScreen:TScreen;

    procedure ShowChild(ParentApplication: TApplication; ParentForm: TForm;ParentScreen:TScreen);export; stdcall;
    var
    frmDLLMain: TfrmDLLMain;
    begin
    Application:
    =ParentApplication;
    screen:
    =ParentScreen;
    frmDLLMain:
    =TfrmDLLMain.Create(ParentForm);
    frmDLLMain.Show;
    end;
    procedure DLLUnloadProc(Reason: Integer); register;
    begin
    if Reason = DLL_PROCESS_DETACH thenbegin
    Application:
    =DllApplication;
    screen:
    =DllScreen;
    end;
    end;
    exports
    ShowChild ;

    begin
    DllApplication:
    =Application;
    DllScreen:
    =screen;
    DLLProc :
    = @DLLUnloadProc;
    end.

    二:增加Form(UDllMain),修改Form.FormStyle=fsMDIChild

    View Code
    unit UDllMain;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs;

    type
    TfrmDLLMain
    =class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    frmDLLMain: TfrmDLLMain;

    implementation

    {$R *.dfm}

    procedure TfrmDLLMain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
    Action:
    =caFree;
    end;

    end.

    三:新建项目,增加Uint(ULoadDll),声明DLL

    View Code
    unit ULoadDll;

    interface

    uses Windows,SysUtils,Forms;
    type
    ShowMdiForm
    =procedure(ParentApplication: TApplication; ParentForm: TForm;ParentScreen:TScreen); stdcall;
    var
    RegisterDLLHandle:integer;
    DllShowMdiForm:ShowMdiForm;

    implementation

    end.

    四:主窗体Form.FormStyle=fsMDIForm

    View Code
    unit UTestMain;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, Menus;

    type
    TfrmMain
    =class(TForm)
    MainMenu1: TMainMenu;
    est1: TMenuItem;
    procedure est1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    frmMain: TfrmMain;

    implementation

    uses ULoadDll;

    {$R *.dfm}

    procedure TfrmMain.est1Click(Sender: TObject);
    begin
    RegisterDLLHandle:
    =loadLibrary('TestMDIDll.dll');
    if RegisterDLLHandle>0then @DllShowMdiForm:=getProcAddress(RegisterDLLHandle,'ShowChild');
    if (@DllShowMdiForm<>nil) then
    begin
    if frmMain.MDIChildren[0].Caption='frmDLLMain'then
    ShowMessage(
    '1')
    else
    DllShowMdiForm(Application,Self,screen);
    end;
    end;

    end.
    posted on 2011-07-12 17:30 龙七 阅读(...) 评论(...) 编辑 收藏

    转载于:https://www.cnblogs.com/Dragon7/archive/2011/07/12/2104485.html

    展开全文
  • Windows文件路径选择的dll封装,需要的可以下载看看。
  • dll封装成ocx说明文档, 将dll封装成ocx说明文档
  • 用VB生成DLL封装ASP连接数据库的代码
  • DLL封装窗体 ,然后通过调用DLL打开窗体!
  • DLL封装的gif动画控件

    2016-09-05 11:11:12
    DLL封装的VCL动画控件,采用多线程+GDI+处理GIF动画,CPU占用较低。仅能被delphi和C++builder调用
  • C#调用dll封装的对话框实例 C#调用dll封装的对话框实例 C#调用dll封装的对话框实例 C#调用dll封装的对话框实例 C#调用dll封装的对话框实例 C#调用dll封装的对话框实例
  • Delphi开发DLL插件用DLL封装应用程序资源
  • Delphi 7 mongodb driver DLL封装及源代码.rar
  • DLL封装框架视图经验总结
  • DLL封装 C++

    2012-04-23 21:41:46
    DLL封装 C++  (2008-05-23 09:02:41) 转载▼ 标签:  dll封装   c   it 分类: Windows+WPF开发 DLL,在windows和linux下都用,但是不太相同,因为微软的DLL的...
  • 学习封装dll源于对代码保密的需要,本文从最简单的开始,封装自定义函数为dll并注册使用。下面是我用思维导图做的简要笔记,会分几篇文章来介绍加载项的东西。加载宏暂时不太感兴趣,用到的时候在研究分享。VBA插件...
  • delphi dll封装窗体

    2008-09-05 11:38:43
    不错的啊!delphi dll封装窗体!看看不错的啊!
  • Delphi中DLL封装

    2009-12-31 10:04:13
    用接口实现了DLL封装对象,包括DLL文件本身与它的调用;在delphi7中调试通过;
  • wavelet的dll封装与c++调用matlab
  • IC卡dll封装成ocx供网页调用,供有需要的人参考。
  • 使用DLL封装业务逻辑

    2007-06-27 17:47:52
    使用DLL封装业务逻辑
  • Anyty C++DLL封装成NetDLL进行调用由于项目中要用到摄像头进行画面的抓拍,提供的C++动态链接库dll.但项目使用C#进行开发的,需要进行二次封装。主要涉及到两点: 1.C++中常用的变量在C#中的映射 2.C++方法在C#中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,437
精华内容 2,974
关键字:

dll封装