精华内容
下载资源
问答
  • Java通过jna-api实现最简单的跨语言调用函数,在Windows上调用DLL动态库

    前述:

    • 本帖为lz原创,经过实际运行,欢迎转载注明出处。
    • 此帖相对比较长,但是十分详细。lz花了将近两天的时候研究JNA调用dll的过程,写这篇帖子花了将近6个小时,所以如果你花了一天的时间让程序成功运行那说明你赚了。在给各位提供方便的同时也请尊重lz的劳动,转发注明出处,谢谢!(楼主基本每天都会在线,有不足之处欢迎指出,提的问题看到后马上回复)
    • DLL动态库是在Windows环境运行的,后期会加上调用Linux系统中的SO

    大概步骤:

    • 安装MingW,为c++提供运行环境
      MinGW 就是 GCC 的 Windows 版本 ,而gcc呢是GNU编译器套件(GNU Compiler Collection),它包括了C、C++等等的前端也包括了这些语言的库,我们需要使用c++所以需要安装,不清楚c++的可以进此帖查看:查看

    • 安装Microsoft Visual Studio 2017 将c++文件编译生成DLL文件
      实际上安装这款软件我也不是很情愿的,将近有4个G的大小,因此我一开始尝试使用Idea生成DLL文件(我上一篇帖子就是如此,是通过JNI生成),没有成功,步骤很繁琐,而且很多过程不可控,所以选择了专业的软件。不使用工具其实就相当于使用用记事本编写web项目,所以还是要安装

    • 通过Java调用dll中的对应方法
      我们只需通过引入JNA的jar包然后通过简单的配置,就可以实现调用dll中的方法


    接下来我们开始入坑之旅吧
    ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓


    关于JNA,与JNI的比较:

    有过跨语言、跨平台开发的程序员都知道,跨平台、语言调用的难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。关于这一点,不论何种语言,何种技术方案,都无法解决这个问题。JNA也不例外。

    上面说到接口中使用的函数必须与链接库中的函数原型保持一致,这是JNA甚至所有跨平台调用的难点,因为C/C++的类型与Java的类型是不一样的,你必须转换类型让它们保持一致,比如printf函数在C中的原型为:

    void printf(const char *format, [argument]);

    你不可能在Java中也这么写,Java中是没有char *指针类型的,因此const char *转到Java下就是String类型了。

    这就是类型映射(Type Mappings),JNA官方给出的默认类型映射表如下:
    在这里插入图片描述

    还有很多其它的类型映射,需要的请到JNA官网查看。

    另外,JNA还支持类型映射定制,比如有的Java中可能找不到对应的类型(在Windows API中可能会有很多类型,在Java中找不到其对应的类型),JNA中TypeMapper类和相关的接口就提供了这样的功能。

    JNA能否替代JNI
    这可能是大家比较关心的问题,但是遗憾的是,JNA是不能完全替代JNI的,因为有些需求还是必须求助于JNI。

    使用JNI技术,不仅可以实现Java访问C函数,也可以实现C语言调用Java代码。
    
    而JNA只能实现Java访问C函数,作为一个Java框架,自然不能实现C语言调用Java代码。此时,你还是需要使用JNI技术。
    
    JNI是JNA的基础,是Java和C互操作的技术基础。有时候,你必须回归到基础上来。
    

    引用自:查看


    安装MingW


    • 首先我们需要去官网下载压缩包,下载Windows版本32位和64位都适用的文件,下载查看
      在这里插入图片描述
    • 再安装文件,基本不用操作,点击下一步就行,安装步骤推荐链接查看
      备注:lz在安装的时候遇到这么样一个问题:在为mingw配置环境变量MinGW_HOME时,将值设置为mingw的安装目录下(bin目录的上一层)时会出现在其他路径运行gcc指令失效的问题,但也有不是每次都无效。最后lz直接将MinGW_HOME的值配置到bin目录下,在path中添加对应%MinGW_HOME%就没出现此问题。跟大家分享一下,希望大家不会出现这样的问题。在这里插入图片描述
      在这里插入图片描述最后在任意路径下运行gcc - v指令不报错就代表安装完成,如下图:
      在这里插入图片描述

    安装Microsoft Visual Studio 2017 将c++文件编译生成DLL文件


    • lz建议安装较高版本的Microsoft Visual Studio,我使用的是2017版本的,低版本存在一些问题,会导致你安装失败后需要重新安装,而且新版本支持模块化安装

    • 下载Microsoft Visual Studio 2017地址https://docs.microsoft.com/zh-cn/visualstudio/productinfo/vs2017-system-requirements-vs
      在这里插入图片描述

    • 下载完成对Microsoft Visual Studio 2017 进行解压安装,安装步骤推荐链接https://www.sohu.com/a/258291421_468739

      注意当安装到选择功能模块这个界面时,由于我们是用来制作dll,所以只需勾选最前面三个Windows和单独的WINDOWS 8.1 SDK(一定要勾选一定一定!)
      其余按照安装步骤安装即可。
      在这里插入图片描述

    • 提示安装完成之后去发现桌面没有快捷方式,可以这样查找:WIN+Q快捷操作出现搜索框,输入deven差不多就可以出来了,如下图;(若快捷键不生效可在安装目录\Common7\IDE\devenv.exe,例如楼主的就是D:\Visual Studio 2017\Common7\IDE路径,如下图)
      在这里插入图片描述
      运行程序的exe程序位置
      在这里插入图片描述
      ===========================================================================================

    使用Visual Studio 2017生成DLL过程

    • 操作过程根据哔哩哔哩网站中搜索:VC《VC++动态库DLL与静态库LIB编程》视频下的第二节部分入口点函数机器导出函数的讲解操作,可以直接观看此视频(视频使用的是2008版本操作过程有些许不同)链接:查看视频
      步骤:

    1.打开Visual Studio 2017软件
    2.点击左上角 文件》新建》项目后进入选择页面(无法截图)
    3.桌面平台》动态链接库
    在这里插入图片描述生成的项目层级关系
    在这里插入图片描述
    4.添加导出def文件 源文件》添加》新建项》模块定义文件
    在这里插入图片描述
    5.对def文件进行编辑,如图片所示
    代码:

    LIBRARY "WindowsProject2"
    EXPORTS
     add @1
    

    在这里插入图片描述
    6.实现第五步中的add函数,源文件》添加》新项目》C++文件
    在这里插入图片描述
    7.编辑cpp文件,简单实现add方法,解释如图:
    代码:

    #include "stdafx.h"
    int add(int a, int b) {
    	return a + b;
    }
    

    在这里插入图片描述
    8.为生成dll做准备,统一jdk和生成的dll位数,仔细看一定要设置:不然调用的时候会因为生成的dll为32位而jdk64位报错

    • 选中项目》右键》属性 将“配置(c)”设置为Release ,平台(p)设置为×64,如图

    在这里插入图片描述

    • 正常情况刚安装的软件没有×64的选项,可跟着我一起添加:添加×64平台
      点击前面打开的平台页面右侧的配置管理器,选择平台出,点击新建,再点击下拉框中的×64即可

      在这里插入图片描述

      • 添加×64

      在这里插入图片描述9.上面操作完成后将第八步的配置设置为Release,平台设置为×64
      10.生成dll文件,选中项目,右键出现的重新生成,即可生成对应的dll文件,如下如所示则代表生成成功
      在这里插入图片描述
      备注:如果生成的时候出现了error MSB803找不到8.1的SDK包(如下图)的错误,则说明你在安装软件的时候没有按照我的提示添加sdk8.1包,解决方案按照此链接操作再次运行即可解决问题,链接:查看
      在这里插入图片描述
      11.查找生成的dll文件,64位生成的dll文件位置如下图,会在控制台打印出来
      在这里插入图片描述

    32位的可直接选中项目右键倒数第二个在资源文件中打开文件夹,然后上一层目录,进入debug中就有对应生成的dll文件


    通过java代码调用方法

    • 引入jna的jar包,可以在pom文件中引入,也可以直接导入jar文件
      在这里插入图片描述
    • 创建一个interface 继承Library接口,在接口中声明方法,并且加载对应的dll文件,如图
      代码:
    public interface DLL extends Library {
    //JNA加载
        DLL instance = (DLL) Native.loadLibrary("DLLTest", DLL.class);
    
        int add(int a, int b);
    }
    

    说明:实际上有多种方式加载dll文件,**JNI中(本文为JNA调用)**也可以通过System.load()和System.loadLibrary()加载dll文件,前者需要写dll的绝对路径,后者只需要写dll的名称即可,具体区别可以查看此链接:查看
    在这里插入图片描述dll路径查找设置
    -Djna.library.path=*** 后面的路径值通过点击项目中dll文件copy path获得,就是dll文件在系统中的路径,我将DLLTest.dll文件放在owntest项目的lib目录下,所以看到的路径就如图
    在这里插入图片描述

    • 设置完成之后创建一个main方法调用add方法,执行成功,如下图(由于楼主在创建项目的时候选为exe程序,故没有成功生成dll文件,在创建项目的时候必须选择动态链接库dll,否则生成的就是一个exe的程序)
      在这里插入图片描述



    最后总结下在执行时可能出现的问题

    1.java.lang.UnsatisfiedLinkError异常,如下,一堆乱七八糟的文字,如下

    java.lang.UnsatisfiedLinkError: Unable to load library 'Dll1': ÕҲ»µ½ָ¶¨
    

    此异常为lz操作时已经着重讲过的问题,就是在生成dll文件时没有统一jdk和vc 2017工具打包的位数,需要相同,要不全是32位要不全是64位,解决此异常需要将生成dll的第8步开始重新生成一遍dll

    2.如果按照上面步骤后依旧出现此问题,则可这样操作:

    -Djna.library.path=D:\AppClass\owntest\lib
    

    在这里插入图片描述
    在这里插入图片描述
    目前只出现了这个问题
    至此结束,感谢观看

    展开全文
  • 注:.dll文件是Windows平台下的动态链接库文件,在Linux平台,有响应功能的文件是.so文件,.so文件接口封装也可以参考此文的思路 目录 一、DLL项目结构介绍 二、DLL项目示例 1.文件 My_Dll_Project.h 2.文件...

    注:.dll文件是Windows平台下的动态链接库文件,在Linux平台,有响应功能的文件是.so文件,.so文件接口的封装也可以参考此文的思路

    目录

     一、DLL项目结构介绍

    二、DLL项目示例

    1.文件 My_Dll_Project.h

    2.文件 My_Dll_Project.cpp

    3.修改后的文件 My_Dll_Project.h

    三、封装方法介绍

    1.再包装法

    1.1 新接口头文件 My_Dll_Project_Interface.h(给用户的头文件)

    1.2 文件 My_Dll_Project_Interface.cpp (给用户头文件的实现,不公开)

    1.3 封装后的原头文件 My_Dll_Project.h (不公开)

    1.4 再包装法面临的难题

    2.抽象类法

    2.1 抽象类接口头文件 My_Dll_Project_Interface_2.h(给用户的头文件)

    2.2 文件 My_Dll_Project_Interface_2.cpp (给用户头文件的实现,不公开)

    1.3 封装后的原头文件 My_Dll_Project.h (不公开)

    3.大小欺骗法(原创,未深度检验)

    3.1 原来给用户的接口头文件(不公开)

    3.2 占位+删减处理后的头文件(公开给用户)


    一、DLL项目结构介绍

    一个C++的DLL项目的基本结构是:

    1. 头文件(.h),写好这个库准备给人家用的函数、类、变量等的声明。
    2. 源文件(.cpp),写上面那个头文件中声明给人家用的函数等的具体实现。
    3. 库文件(.lib),用你DLL的程序员编译他的程序时会用到。
    4. 程序数据库文件(.pdb),保存这个DLL库的调试信息,只有调试这个库时才必须用到。
    5. 动态链接库文件(.dll),用户编写的程序运行时才会用到这个文件。

    其中,源文件(.cpp)里写接口函数的具体实现,编译完成后一般不公开给用户,原因有很多,如,保留产权,或者为了彻底封装,甚至可能只是因为代码写得太烂不好意思公开(手动滑稽)。

    头文件(.h),库文件(.lib),动态链接库文件(.dll)必须给用户,程序数据库文件(.pdb)给不给看心情,如果给了,用户就能在调试的多看几步。

    头文件(.h)就是其他程序员(用户)使用你的库的接口了,也就是说,头文件告诉了用户如何使用你的库,原理参考C++教科书关于声明和定义的章节。


    二、DLL项目示例

    下面是用VS创建项目自动生成的代码,项目名:My_Dll_Project


    1.文件 My_Dll_Project.h

    // 下列 ifdef 块是创建使从 DLL 导出更简单的
    // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYDLLPROJECT_EXPORTS
    // 符号编译的。在使用此 DLL 的
    // 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
    // MYDLLPROJECT_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
    // 符号视为是被导出的。
    #ifdef MYDLLPROJECT_EXPORTS
    #define MYDLLPROJECT_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT_API __declspec(dllimport)
    #endif
    
    // 此类是从 dll 导出的
    class MYDLLPROJECT_API CMyDllProject {
    public:
    	CMyDllProject(void);
    	// TODO: 在此处添加方法。
    };
    
    extern MYDLLPROJECT_API int nMyDllProject;
    
    MYDLLPROJECT_API int fnMyDllProject(void);
    

    2.文件 My_Dll_Project.cpp

    // My_Dll_Project.cpp : 定义 DLL 的导出函数。
    //
    
    #include "framework.h"
    #include "My_Dll_Project.h"
    
    
    // 这是导出变量的一个示例
    MYDLLPROJECT_API int nMyDllProject=0;
    
    // 这是导出函数的一个示例。
    MYDLLPROJECT_API int fnMyDllProject(void)
    {
        return 0;
    }
    
    // 这是已导出类的构造函数。
    CMyDllProject::CMyDllProject()
    {
        return;
    }
    

    这样的头文件给别人似乎看不出来什么问题,如果你感觉注释太多,删掉也可以,但是如果接口是下面这样,再直接给用户,就很成问题了。

    3.修改后的文件 My_Dll_Project.h

    #pragma once
    #ifdef MYDLLPROJECT_EXPORTS
    #define MYDLLPROJECT_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT_API __declspec(dllimport)
    #endif
    
    #include <Windows.h>
    
    // 此类是从 dll 导出的
    class MYDLLPROJECT_API CMyDllProject
    {
    public:
    	CMyDllProject(void);
    	void this_dll_user_is_good();
    
    private:
    	int user;
    	char is;
    	void* bad;
    	HANDLE yeah;
    
    private:
    	HANDLE user_is_bad();
    	void user_is_stupid();
    	void user_is_dirty();
    	void user_is_nasty();
    	void user_is_ugly();
    	void user_is_wtf_i_cant_say_more();
    
    };
    
    extern MYDLLPROJECT_API int nMyDllProject;
    
    MYDLLPROJECT_API int fnMyDllProject(void);
    

    当然这里的说用户坏话的函数名只是开个玩笑,认真地说,这个头文件有以下两个问题:

    1. private 标签标识这个这个函数是私有的,也就是说这个类的用户被阻止使用这些函数(原因不解释),也就是说这些函数、成员变量是用户绝对用不到的,不需要暴露给用户。而私有成员部分暴露了类的实现方式,削弱了库的封装性和保密性,用户可以获得内部实现的线索,同时,如果后续修改私有成员,那么用户必须连头文件都一起替换。
    2. 这个库的实现用到了<Windows.h>头文件,但是这个头文件并不是接口所必须的,用户#include这个头文件时,会把<Windows.h>一并包含进来,会污染用户的命名空间。

    函数和变量是独立的,可以分别决定每个函数和变量是否导出,但是类的定义一定是所有成员都写在一起的,这才导致了问题,下面就来介绍常用的类的两种深度隔离的封装方法。

    三、封装方法介绍

    1.再包装法

    就是不导出原本要导出的类,重写一个新类,新类中数据成员只有一根指向原先类的指针,新类的所有普通函数都是通过指针对原先类的调用,就好像在商品外面重新包装了一样,再包装类可以完全隔绝原有类的副影响,而且可以给原先不支持移动语义(C++11)的类附加移动语义支持。


    1.1 新接口头文件 My_Dll_Project_Interface.h(给用户的头文件)

    #pragma once
    #ifdef MYDLLPROJECT_EXPORTS
    #define MYDLLPROJECT_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT_API __declspec(dllimport)
    #endif
    
    class CMyDllProject;	// 这里写声明
    
    class MYDLLPROJECT_API CMyDllProjectInterface
    {
    public:
    	CMyDllProjectInterface(void);
    	CMyDllProjectInterface(CMyDllProjectInterface&& mov) noexcept;	// 添加移动语义
    	~CMyDllProjectInterface();
    	
    public:
    	CMyDllProjectInterface& operator=(CMyDllProjectInterface&& mov) noexcept;	// 添加移动语义
    
    public:
    	void this_dll_user_is_good();
    
    private:
    	CMyDllProject* _self;	// 只暴露一根指针,甚至不暴露,直接声明为void*,然后实现代码使用强制类型转换也可以
    };
    

    1.2 文件 My_Dll_Project_Interface.cpp (给用户头文件的实现,不公开)

    #include "My_Dll_Project_Interface.h"
    #include "My_Dll_Project.h"
    
    CMyDllProjectInterface::CMyDllProjectInterface(void) :
    	_self(new CMyDllProject)
    {}
    
    CMyDllProjectInterface::CMyDllProjectInterface(CMyDllProjectInterface&& mov) noexcept:
    	_self(mov._self)
    {
    	mov._self = nullptr;
    }
    
    CMyDllProjectInterface::~CMyDllProjectInterface()
    {
    	if (_self) delete _self;
    }
    
    CMyDllProjectInterface& CMyDllProjectInterface::operator=(CMyDllProjectInterface&& mov) noexcept
    {
    	this->~CMyDllProjectInterface();
    	_self = mov._self;
    	mov._self = nullptr;
    	return *this;
    }
    
    void CMyDllProjectInterface::this_dll_user_is_good()
    {
    	return _self->this_dll_user_is_good();	// 这个类的函数其实就是对原类的简单调用
    }
    

    1.3 封装后的原头文件 My_Dll_Project.h (不公开)

    #include <Windows.h>	// 这个头文件就不用再暴露给用户了
    
    class CMyDllProject
    {
    public:
    	CMyDllProject(void);
    	void this_dll_user_is_good();
    
    private:
    	int user;
    	char is;
    	void* bad;
    	HANDLE yeah;
    
    private:
    	HANDLE user_is_bad();
    	void user_is_stupid();
    	void user_is_dirty();
    	void user_is_nasty();
    	void user_is_ugly();
    	void user_is_wtf_i_cant_say_more();
    
    };
    

    完美封装+添加移动语义

    这种封装的优点:

    1. 直接封装成类的形式,用户可以直接按类的方式使用
    2. 可以自动管理资源,无需智能指针
    3. 可以添加额外的移动语义,同时对象的移动速度非常快。

    缺点:

    1. 从理论上来说,每次调用原函数都要进栈出栈,可能会显著降低性能(测试结果:Debug模式下,方法一比下面的方法二慢了77%,Release版本下,完全优化,方法一比方法二反而快了2%)
    2. 不容易反映原接口头文件中类的继承关系
    3. 写包装接口类的工作量有点大

    反映类的继承关系就是说下面这样:

    1.4 再包装法面临的难题

    class Interface // 这是要直接公开的抽象类接口
    {
    public:
    	virtual int help() = 0;
    };
    
    class A :public Interface // 这是要包装的导出类
    {
    public:
    	virtual int help();
    	void yes();
    };
    
    class B :public A // 这是要包装的导出类
    {
    public:
    	void yes();
    };

    这三个类想导出,封装类可不好写,读者可以自行试试如何解决这个棘手的问题,先告诉大家这个问题并不是无解。

    2.抽象类法

    这个方法的基本原理就是利用抽象类作接口,给用户提供工厂函数(Factory Method),或者把所有工厂函数打包在一起做个工具箱类,让用户使用工厂函数创建实例。


    2.1 抽象类接口头文件 My_Dll_Project_Interface_2.h(给用户的头文件)

    #ifdef MYDLLPROJECT2_EXPORTS
    #define MYDLLPROJECT2_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT2_API __declspec(dllimport)
    #endif
    
    class MYDLLPROJECT2_API CMyDllProjectInterface
    {
    public:
    	virtual void this_dll_user_is_good() = 0;
    };
    
    MYDLLPROJECT2_API CMyDllProjectInterface* factory_function();	// 导出工厂函数
    

    2.2 文件 My_Dll_Project_Interface_2.cpp (给用户头文件的实现,不公开)

    #include "My_Dll_Project.h"
    #include "My_Dll_Project_Interface_2.h"
    
    CMyDllProjectInterface* factory_function()
    {
    	return new CMyDllProject();
    }
    

    1.3 封装后的原头文件 My_Dll_Project.h (不公开)

    #include <Windows.h>
    #include "My_Dll_Project_Interface_2.h"
    
    class CMyDllProject : public CMyDllProjectInterface
    {
    public:
    	CMyDllProject(void);
    	void this_dll_user_is_good();
    
    private:
    	int user;
    	char is;
    	void* bad;
    	HANDLE yeah;
    
    private:
    	HANDLE user_is_bad();
    	void user_is_stupid();
    	void user_is_dirty();
    	void user_is_nasty();
    	void user_is_ugly();
    	void user_is_wtf_i_cant_say_more();
    
    };
    

    这种封装的优点:

    1. 理论上,直接使用指针可以比方法一更快
    2. 不必编写封装类,工作量少

    缺点:

    1. 要求用户管理资源
    2. 不能直接套用一般类的形式(构造函数、拷贝函数、移动、复制)

    这种方法多适用于接口先于代码确定好的情况


    3.大小欺骗法(原创,未深度检验)

    没有点创新怎好意思投原创,这种方法的原理非常简单,就是原来接口头文件在编译好后、交给用户前时,把类中的私有成员尽管删除,然后用char数组填充类的大小,数组长度为sizeof(类)的大小,说白了就是占位的作用。

    3.1 原来给用户的接口头文件(不公开)

    #pragma once
    #ifdef MYDLLPROJECT_EXPORTS
    #define MYDLLPROJECT_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT_API __declspec(dllimport)
    #endif
    
    #include <Windows.h>
    
    class MYDLLPROJECT_API CMyDllProject
    {
    public:
    	CMyDllProject(void);
    	void this_dll_user_is_good();
    
    private:
    	int user;
    	char is;
    	void* bad;
    	HANDLE yeah;
    
    private:
    	HANDLE user_is_bad();
    	void user_is_stupid();
    	void user_is_dirty();
    	void user_is_nasty();
    	void user_is_ugly();
    	void user_is_wtf_i_cant_say_more();
    
    };
    

    3.2 占位+删减处理后的头文件(公开给用户)

    #pragma once
    #ifdef MYDLLPROJECT_EXPORTS
    #define MYDLLPROJECT_API __declspec(dllexport)
    #else
    #define MYDLLPROJECT_API __declspec(dllimport)
    #endif
    
    // 使用占位可以不包含<Windows.h>了
    
    class MYDLLPROJECT_API CMyDllProject
    {
    public:
    	CMyDllProject(void);
    	void this_dll_user_is_good();
    private:
            // 函数尽管删除,数据成员反应在占位数组的大小里
    	char _place_hoder[20];    // 占位20
    };

    包不包含<Windows.h>的问题:如果类没有返回别的头文件中的数据类型的公共函数,就可以实现不包含,其他头文件中的数据类型的数据成员,可以直接用占位数组占位就行了。

    这种封装的好处:

    1. 简单粗暴,而且不会带来额外的性能开销
    2. 直接反应导出类间的继承等关系
    3. woc,还列个啥,最简单的就是最好的,能直接用了难道不爽吗?

    缺点:

    1. 简直就是野路子方法(没错就是野路子,原创)
    2. 兼容性可能有所欠缺,可能会出错(虽然我自己用了好几次没有出任何错误)
    3. 私有函数其实并没有真正意义上的封装在内部(使用dumpbin仍能在dll中看到导出的私有函数)

    之所以想出这种方法,是基于我的一个假设:类的定义体在编译过程中只起到内存大小指示的作用,内存本身并不划分为不同类型。我认为基于这种方法并不能完全封装,我觉得它的用处可能更多在于团队内部合作用到。

    另外我认为,完全封装并不是不行,只是需要编译器支持,让编译器不导出私有函数就行了。

    展开全文
  • DLL动态链接封装VCL的MDI子窗体 这里是工程文件的部分:  在DLL中封装MDI子窗体需要重写DLL入口函数,具体代码如下: var  DllApp: TApplication;//定义保存原DLL的TApplication对象...

    在DLL动态链接库中封装VCL的MDI子窗体

    这里是工程文件的部分:

      在DLL中封装MDI子窗体需要重写DLL入口函数,具体代码如下:

    var
      DllApp: TApplication;//定义保存原DLL的TApplication对象
      DllScr: TScreen;//定义保存原DLL的TScreen对象

    procedure UnProcDll(Reason: Integer); reGISter;
    //重新定义DLL入口函数
    begin
      if Reason = DLL_PROCESS_DETACH then
      begin
        Application := DllApp;
        Screen := DllScr;
      end;
    end;

    //初始化区
    begin
      DllApp := Application;//备份原DLL的TApplication对象
      DllScr := Screen;//备份原DLL的TScreen对象
      DllProc := @UnProcDll;//将重写后的入口函数地址付给DLLProc
    end.

     这里是需要导出的函数,写在工程文件或者一个单元的接口部分:

    function ShowForm(App: TApplication; Scr: TScreen; Owner: TForm):TForm; export; stdcall;
    begin
      //下面的这两个变量是必须的
      Application := App;//这个就是调用的时候对Application对象重新赋值
      Screen := Scr;//对Screen对象赋值
      //函数自定义代码
      if not Assigned(Form1) then
      begin
        //Application.CreateForm(TForm1, Form1);
        Form1 := TForm1.Create(Owner);
        Result := Form1;
      end
    else
        Result := Form1;
      //函数自定义代码结束
    end;

      最后在工程文件把这个函数导出来就OK了。

    //最后导出这个函数
    exports
       ShowForm;

    展开全文
  • .h头文件 .lib库文件 .dll动态库文件之间的关系

    万次阅读 多人点赞 2013-10-12 11:56:52
    .h头文件是编译时必须的,lib是链接时需要的...如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。 .h .lib .dll三者的关系是: H文件作用是:声明函数接口 DLL文件作用是: 函数可执行代码

    .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

    附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。

    .h .lib .dll三者的关系是:

    H文件作用是:声明函数接口

    DLL文件作用是: 函数可执行代码

    当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

    一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

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

    静态链接库(Lib)与动态链接库(DLL)的区别 

         静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。


    “每一个lib文件就是若干函数(假设只有函数)的定义” 
    lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。 

    “通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”

    还要指定编译器链接相应的库文件。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。 

    “那他和直接给出那个函数定义的文件,比如.cpp文件,和头文件有什么区别,静态链接库有什么用” 
    cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。 

    “还有不明白的是,静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?” 
    是在链接的时候将lib链接到目标代码中。

    静态链接库(Lib)
    在VC++6.0中new一个名称为libTest的static library工程,

    并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:

    //文件:lib.h
    #ifndef LIB_H
    #define LIB_H
    extern "C" int add(int x,int y);   //声明为C编译、连接方式的外部函数
    #endif

    //文件:lib.cpp
    #include "lib.h"
    int add(int x,int y)
    {
    return x + y;
    }


      编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。

      标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。

    下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:

    #include <stdio.h>
    #include "..\lib.h"//不可丢失
    #pragma comment( lib, "..\\debug\\libTest.lib" )  //指定与静态库一起连接
    int main(int argc, char* argv[])
    {
         printf( "2 + 3 = %d", add( 2, 3 ) );
    }
      静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接

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

    用VC++生成静态库文件

    今天闲着没事做,自己写了一点小笔记,不知道对于新手有没用,高手就不用看了,作为新手的我斗胆来发表一个笔记,就是静态库文件的封装过程,使用VC++6.0编写,下面是正文,也许我的用语并不专业

    以前我们写C/C++源文件的时候,都是先将各个写好的源文件编译,编译生成的是目标文件机器码,即.obj文件.(目标文件的扩展名不一定是.obj文件).

    我们调用的标准C/C++函数机器码实际被封装于标准C/C++静态库文件中的.即那些扩展名为.lib的文件中.

    最后链接器将我们编译的各个目标文件里的机器码和静态库(标准C/C++库)中的函数机器码链接到一起形成一个扩展名为.exe的可执行文件模块.

    在这里我们叙述将C/C++源文件编译链接成一个静态库文件,但它不是可执行模块,它体内含有可执行机器码

    静态库文件就像一个仓库或者容器,里面封装了一些可执行机器码.这些机器码是我们用程序设计语言,比如C/C++源文件编译后生成的机器码.

    一.下面将讨论将C/C++源文件编译并链接成一个静态库文件的过程,

    在VC++6.0中选择File-New-Win32 Static Library,写好工程名创建好工作空间后再选择菜单中New-File来为工程添加C或者C++ 源文件.

    假如我们为该工程添加了一个名为lib_c.c和一个名为lib_cpp.cpp的源文件

    //lib_c.c中的内容

    extern int Add(int x,int y) //该函数是一个外部函数,任何文件都可以访问它

    {
        return x+y;

    }

    extern int data_c
    //这是一个外部全局变量,任何文件可以访问它

    //lib_cpp.cpp中的内容

    extern “C” int
            reduce(int x,int y)//这里加了个”C”表示允许C源文件访问这个C++函数代码

    {
        return x-y;

    }

    extern “C” int data_cpp=2;

    注意以下几点

    (1)当“extern”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量任何文件可以访问,“extern”关键字可以省略不写,缺省下就是”extern”

      当“extern”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“extern”关键字修饰定义的函数或全局变量.

    (2)当”static”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量只能由本文件中加了”static”关键字修饰的函数声明或全局变量声明来引用.

      当”static”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“static”关键字修饰定义的函数或全局变量.

    (3)在CPP源文件的函数和全局变量定义中加了个”C”表示允许C源文件访问该函数和全局变量.如果是C++源文件访它们的话则可加可不加.注意这”C”要大写.

    接下来就要将写好的C/C++源文件进行编译和链接,最后会生成一个扩展名为.lib的文件.该文件就是静态库文件了,该静态库文件是不能直接运行的,我们所编译的C/C++源文件的机器码就已经被封装进这个用VC++6.0创建的静态库文件里面去了.

    二.如何将编写好的静态库文件像使用C/C++标准库那样使用,下面将继续讨论

    1.用VC++6.0新建一个工程名为TEST,添加一个名为TEST.c的源文件到该工程,因为我们将测试一下,将我们编写的库文件里的函数或者全局变量的机器码链接到我们这个TEST.c源文件中去,假设我们生成的库文件名为TEST.lib,先拷贝如下范例代码到TEST.c中

    //TEST.c

    #include <stdio.h>

    extern int 
    Add(int x,int y); //当前文件只能访问“extern”关键字修饰定义的Add函数

    extern int
    reduce(int x,int y);// //当前文件只能访问“extern”关键字修饰定义的reduce函数

    #pragma comment(lib,"TEST.lib") //指示链接器到字符串所表示的文件路径中去找库文件

    int main()

    {
        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    这里我们要声明静态库中已知的函数或全局变量的声明

    #pragma comment(lib,"TEST.lib")这条指令告诉链接器到字符串所表示的路径下去找库文件,这里我将库文件放到了当前工程目录下.也可以不写这句.

    还有一种方法,可以直接在VC++6.0中设置依次选择toolsoptionsdirectorieslibrary files菜单或选项,填入库文件路径(只键入库文件所在目录路径而不能输入库文件名),这只是告诉链接器库文件所在目录的路径,还没告诉链接器库文件名,方法是VC++6.0中设置依次选择project-settings-link 在object/library modules: 这栏输入库文件名字然后就OK了

    2.当用C++源文件的目标文件和库文件的代码链接时有一点小改变,这里就不浪费口舌了,假设我们新建了一个工程并添加了一个名为TEST.CPP的源文件,拷贝如下范例代码到TEST.CPP中

    //TEST.cpp

    #include <stdio.h>

    extern “C” int 
           Add(int x,int y); //表示引用的是C函数代码

    extern int
          reduce(int x,int y);

    #pragma comment(lib,"TEST.lib")

    int main()

    {
        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    在这个C++源文件里引用C函数代码同样要加个”C”,但是在C源文件引用C++函数代码不能加”C++”,编译会报错,只能在C++文件函数定义中加”C”.

    只有C++才支持这种引用方式,也许因为只有C++兼容C而没有C兼容C++这一原则.




    .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

    附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。

    .h .lib .dll三者的关系是:

    H文件作用是:声明函数接口

    DLL文件作用是: 函数可执行代码

    当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

    一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

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

    静态链接库(Lib)与动态链接库(DLL)的区别 

         静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。


    “每一个lib文件就是若干函数(假设只有函数)的定义” 
    lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。 

    “通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”

    还要指定编译器链接相应的库文件。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。 

    “那他和直接给出那个函数定义的文件,比如.cpp文件,和头文件有什么区别,静态链接库有什么用” 
    cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。 

    “还有不明白的是,静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?” 
    是在链接的时候将lib链接到目标代码中。


    展开全文
  • ...附加依赖项的是.lib不是.dll,若生成了dll,则肯定也生成lib文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果要使需要动态连接的程序运行起来,有dll就够了。在开发和调试阶
  •  将一个函数声明为导出函数,就是说这个函数要被其他程序调用,即作为DLL的一个对外函数接口。  通常它和extern "C" 合用,形式如下: extern "C" { __declspec(dllexport) RETURN_TYPE ...
  • 本文将整理动态链接库dll封装方法及调用的方法。(以VS2010为开发平台) 1.动态链接库dll封装方法 封装步骤 (1),在VS2010中新建一个win32-&amp;amp;amp;gt;dll工程; (2),新建一个头文件Dll4.h #ifndef ...
  • 构建之后生成如下文件dll和对应的lib。 调用测试DLL 创建控制台应用程序. 将之前生成的DLL和Lib和头文件放到新的项目中供调用. 添加SDK, 多个模块封装成一个DLL 多个模块整合成一个DLL,可以将每...
  • dll动态链接将函数封装

    万次阅读 2012-11-18 13:12:23
    如果熟悉windows的用户就会发现windows下有很多dll文件,但很多人不知道这些文件究竟是用来干什么的,为什么要做成dll文件。现在我就windows下的systems文件夹下的user32.dll文件,利用VC++6.0的depends工具打开,...
  • .h头文件是编译时必须的,lib是链接时...如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。 .h .lib .dll三者的关系是: H文件作用是:声明函数接口 DLL文件作用是: 函数可执行代码
  • flask接口非常简单 我简化成了两个文件 https://download.csdn.net/download/Andrwin/12408796 改成动态链接有点复杂 所有需要的环境见上篇文章 https://blog.csdn.net/Andrwin/article/details/106077665 用...
  • 因工作需要项目中涉及到调用东软医保接口相关操作,本次项目使用C#本地调用C++封装dll动态库实现医保接口访问操作。 一、接口说明如下 3.1 用户接口函数 本系统提供给医院的是一个动态库接口,无用户界面,输入...
  • .h头文件是编译时必须的,lib是链接时需要的,...如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。 .h .lib .dll三者的关系是: H文件作用是:声明函数接口 DLL文件作用是: 函
  • .h头文件是编译时必须的,lib是链接... .h .lib .dll三者的关系是: H文件作用是:声明函数接口 DLL文件作用是: 函数可执行代码 当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道
  • .h头文件 .lib动态链接库文件 .dll 动态链接 .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和...
  • VS2015 C++ dll动态库的制作以及调用

    万次阅读 2018-12-19 15:18:46
    动态库和静态库的区别:静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需...
  • 以前没有封装dll动态库,接触新的工作之后需要用,折腾了一整天,终于搞定了 毕竟是生手,就简单分析注意点和遇到的一些问题及解决方案: (1)首先准备要生成DLL的工程。 (1.1)我用的是BCB(C++ Builder),File->...
  • 简介 动态链接最大的优势在于可以提供给其他应用程序共享的资源,最小化应用程序代码...(1)创建DLL动态链接项目 (2)在DllMain函数的上方或下方创建一个自定义函数(样例使用ShowMessageBox函数) ...
  • 文章目录开发流程lib、dll介绍静态库:动态库:生成和使用静态库lib生成和使用动态库实现流程(隐式调用)实现流程(显式调用) lib、dll介绍 静态库: 在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的...
  • c++动态库打包为dll文件供C#项目调用

    千次阅读 2018-09-27 11:32:15
    编写C++动态库 创建项目: 添加C++测试类: 在MathAPI.h定义接口方法: #pragma once class MathAPI { public: MathAPI(); ~MathAPI(); static _declspec(dllexport) double Add(double a, double b)...
  • 动态链接dll)是一个可以被其他应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中,这种称为静态,其特点是可执行...
  • 一、为什么要在node.js中调用动态链接库 由于之前公司的一个项目中,需要调用第三方的接口API,特别是与硬件设备进行通信...在前端部分使用node-ffi直接调用dll动态库文件 最终我们使用了第二个方案! 二、什么是no...
  • c# 封装动态链接库dll

    千次阅读 2017-08-08 20:54:11
    前天学习了下将自己的方法封装dll,同时在其他的项目里引用封装dll,并调用dll里的方法。同时还试探了下将Windows应用程序封装dll(Winform),下面详细介绍。 一、建立 类库 将方法封装dll 在VS里新建一...
  • (1).h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib 不是.dll 若生成了DLL ,则肯定也生成 LIB文件 如果要完成源代码的编译和链接,有头文件和...H文件 作用是:声明函数接口
  • MFC调用QT编写的DLL动态库

    千次阅读 2017-12-07 16:58:53
    MFC调用QT编写的动态库,如果QT dll里面用到了一些消息循环相关的接口,比如QTimer,QEventLoop等,会由于没有QAppAplication而导致这些调用出现异常,如果DLL里面没有界面显示的话,解决方法还是相对比较简单的。
  • STEP - 4 : 使用VS2013生成DLL动态链接库文件 1️⃣ 新建项目 文件–新建–项目–Visual C++模块–Win32–Win32控制台应用程序–输入项目名称 应用程序选择DLL, 附加选项选择空项目 – 完成 2️⃣ 引入...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,020
精华内容 8,008
热门标签
关键字:

dll动态库接口文件封装