精华内容
下载资源
问答
  • 动态链接库封装源码(第一条链接源码): 1、第一条里面需要注意的是当  我们在.pro文件里加上 DESTDIR =Mydll, 它的意思是我们把生成的内容放到这个文件夹里面,如果没有它会自动生成在sld工程文件之外,并且...

    其实这三个链接的操作照着做就行啦,很简单,只有一个地方最关键:就是文件路径的配置。只有这个地方容易出错,其他的都不会有问题出现。上传的代码一定有用,只要你路径配置正确。欢迎下载



    推荐学习链接是:

    ①https://blog.csdn.net/vample/article/details/78841945

    动态链接库封装源码(第一条链接源码):

    1、第一条里面需要注意的是当 我们在.pro文件里加上DESTDIR =Mydll,它的意思是我们把生成的内容放到这个文件夹里面,如果没有它会自动生成在sld工程文件之外,并且生成的文件夹是与sld文件夹处于平行位置,并且需要点击项目把Shadow build 去掉勾选,这样文件夹生成的内容会放到这个文件夹Mydll里面,并且这个文件夹将会自动产生,并且在sld工程文件夹里面。

    2、在项目选项处去掉勾选项shadow build勾选,这个其实去不去掉都行啦,只要你们生成三个sld_global.h,sld.h和sld.dll文件即可。

    3、在动态链接库中封装内部函数的方法就是先声明在sld.h文件QString GetStr();,后在sld.cpp文件里面定义函数

    class SLDSHARED_EXPORT Sld
    {
    
    
    public:
        Sld();
    
    
        QString GetStr();//声明内部函数
    };
    
    

    QString Sld::GetStr()//定义内部函数
    {
        return "aa";
    }

    在动态链接库中封装外部函数的方法就是在sld.h文件的Sld类的外面先声明

    extern "C" SLDSHARED_EXPORT Q_DECL_EXPORT int add(int i, int j);
    ,后在sld.cpp文件Sld类的外面定义函数
    int add(int i, int j)
    {
        return i+j;
    }

    代码如下:

    extern "C" SLDSHARED_EXPORT Q_DECL_EXPORT int add(int i, int j);//外部函数定义,必须加上extern声明
    //外部函数
    int add(int i, int j)
    {
        return i+j;
    }

    ②https://blog.csdn.net/vample/article/details/78850300

    动态链接库隐式调用操作的源码(第二条链接源码):

    怎么使用动态链接库(隐式调用)

    新建一个Qt WidgetsApplication项目

    点击项目不把shadow build勾选去掉 

    在开始之前先分析一下dll在什么时候需要,我们写程序的时候两个地方需要,一个是编译的时候,一个是exe文件运行的时候,那么因此,我们需要在两份dll文件(只用一份请看方法二)。

    首先,我们把dll相关的头文件拷贝出来,放到一个目录下,目录是随意的,然后用includepath来加载,这个没什么好说的,拷贝的文件是:sld_global和sld.h(总之你把dll相关的头文件都拷贝出来就行了),

    这里我们把它放在/heads目录下,然后把sld.dll拷贝到/release目录下(运行时要用)

    和sld

          VS编译的编译的动态链接库需要三个文件sld.h、sld.lib、sld.dll

    在.pro文件里修改(MinGW)

    
     
    INCLUDEPATH +=../MyDialog/heads
    LIBS += ../MyDialog/release/sld.dll

     

    第一句是把sld项目的文件夹包含到这个项目里来,这样我们就能直接用它里面的头文件sld_global和sld.h

    第二句是告诉编译器lib在哪(我用的是vs的编译器如果 用mingw可以直接添加sld.dll)

           如果是lib则要指向lib目录

    我样在窗体上加一个按钮,并添加槽

    .h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
     
    #include <QMainWindow>
    
     
    namespace Ui {
    class MainWindow;
    }
    
     
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
     
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
     
    private slots:
        void on_pushButton_clicked();
    
     
    private:
        Ui::MainWindow *ui;
    };
    
     
    #endif // MAINWINDOW_H
    
     
    
     

    .CPP
    
     
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "sld.h"
    #include <QMessageBox>
    #include <QString>
    
     
    
     
    
     
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
     
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
     
    void MainWindow::on_pushButton_clicked()
    {
    
     
        Sld s;
    
     
        QMessageBox::information(this, tr("Inform;ation"),s.GetStr());
    
     
        //QMessageBox::information(this, tr("Information"),QString::number(add(5,6)));
    }
    
     

    好了,构建,运行看效果吧

    总结,隐式调用就是:

    一:在pro文件用LIBS给它指定一下dll文件在哪里,就是前面的两句就是了,这是需要.dll文件的第一个位置;     

    二:在你的程序编译生成的文件夹里面的debug( 或者说含.exe文件的目录位置),放一个dll文件(这个是非常有必要加的,不加的话,程序就会挂掉)。

    ③https://blog.csdn.net/vample/article/details/78850369

    动态链接库显示调用操作的源码:https://download.csdn.net/download/wise18146705004/10536646

    动态链接库显式调用

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    
    #include <QMainWindow>
    
    
    namespace Ui {
    class MainWindow;
    }
    
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    
    private slots:
        void on_pushButton_clicked();
    
    
    private:
        Ui::MainWindow *ui;
    };
    
    
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QDebug>
    #include <QLibrary>
    
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    
    void MainWindow::on_pushButton_clicked()
    {
        typedef int(*FUN2)(int, int); //  函数指针,定义函数add别名
        QLibrary lib("../MyDialog1/sld.dll");//指定dll路径,可以不在exe目录下
    
    
        if(lib.load())
        {
            qDebug() << "load ok!";
            FUN2 add = (FUN2)lib.resolve("add");
    
    
            if (add)
            {
                qDebug() << "load add ok!";
                qDebug() << add(3, 5);
            }
            else
            {
                qDebug() << "load add error";
            }
    
    
        }
        else
        {
            qDebug() << "load error!";
        }
    }
    
    
    
    

    总结:1、显式调用目前只能调用外部函数,没有看到能调用类中的函数()也就是无法调用内部函数



    展开全文
  • opencv封装动态链接给Java使用JNI技术,不懂得可以联系我可以无偿给与帮助
  • 目前上期技术CTP系统提供的API版本是C++版本,而很多客户采用Java开发,就产生了一些问题。SWIG是一个能将C/C++接口转换为其他语言的工具,目前可以支持Python,Java,R等语言。 本文主要介绍Windows 32位平台下利用...

    前言:
    目前上期技术CTP系统提供的API版本是C++版本,而很多客户采用Java开发,就产生了一些问题。SWIG是一个能将C/C++接口转换为其他语言的工具,目前可以支持Python,Java,R等语言。
    本文主要介绍Windows 32位平台(64位平台请使用对应的软件和API)下利用Swig工具将CTP C++接口API转换为Java可调用的接口。原创不易,欢迎点赞。

    0. 欢迎交流

    github: https://github.com/nicai0609/JAVA-CTPAPI


    1. 准备工作

    • 从CTP官网上下载CTP API点击下载。32位的API文件包清单如下:

    error.dtd
    error.xml
    ThostFtdcMdApi.h
    ThostFtdcTraderApi.h
    ThostFtdcUserApiDataType.h
    ThostFtdcUserApiStruct.h
    thostmduserapi.dll
    thostmduserapi.lib
    thosttraderapi.dll
    thosttraderapi.lib

    • 安装Swig软件,本文中所用的Swig是swigwin-2.0.11版本,点击下载
    • 安装JDK,注意要安装32位版本,将环境变量配置好。本文所用的是1.8.0_111版本,点击下载
    • 安装libiconv库。这个库主要适用于字节编码转换,因为CTP的结算单信息是GB2312编码,而Java采用UTF-8编码,如果不进行字节转换,得到的结算单信息中的中文将会是乱码。 本文采用的libiconv版本是自己下载的源码编译的静态库,可至群内直接下载源码自己编译,也可以到我的github libiconv下载编译好的。本文一开始采用的是第三方库libiconv转换,下面也以libiconv为例继续。C++11库中已有字节编码转换方式,采用这种方式可以不用libiconv库,下面和libiconv相关的都可以略去,见《Swig转换C++接口中文乱码解决方案》
    • 安装Eclipse,注意,也需要下载32位版本。主要用与Java demo的测试,点击下载
    • 安装vs2013,主要用于生成包装dll。

    2. 通过Swig得到jar包

    在刚刚下载得到的API文件夹20180109_tradeapi_windows内,新建文件thosttraderapi.i,内容如下

    %module(directors="1") thosttradeapi 
    %{ 
    #include "ThostFtdcTraderApi.h"
    #include "iconv.h"
    %}
    
    %typemap(out) char[ANY], char[] {
        if ($1) {
            iconv_t cd = iconv_open("utf-8", "gb2312");
            if (cd != reinterpret_cast<iconv_t>(-1)) {
                char buf[4096] = {};
                char **in = &$1;
                char *out = buf;
                size_t inlen = strlen($1), outlen = 4096;
    
                if (iconv(cd, in, &inlen, &out, &outlen) != static_cast<size_t>(-1))
                    $result = JCALL1(NewStringUTF, jenv, (const char *)buf);
                iconv_close(cd);
            }
        }
    }
    
    %feature("director") CThostFtdcTraderSpi; 
    %ignore THOST_FTDC_VTC_BankBankToFuture;
    %ignore THOST_FTDC_VTC_BankFutureToBank;
    %ignore THOST_FTDC_VTC_FutureBankToFuture;
    %ignore THOST_FTDC_VTC_FutureFutureToBank;
    %ignore THOST_FTDC_FTC_BankLaunchBankToBroker;
    %ignore THOST_FTDC_FTC_BrokerLaunchBankToBroker;
    %ignore THOST_FTDC_FTC_BankLaunchBrokerToBank;
    %ignore THOST_FTDC_FTC_BrokerLaunchBrokerToBank;  
    %feature("director") CThostFtdcTraderSpi; 
    %include "ThostFtdcUserApiDataType.h"
    %include "ThostFtdcUserApiStruct.h" 
    %include "ThostFtdcTraderApi.h"  
    

    这是一个接口文件,用于告诉swig为哪些类和方法创建接口。***.i文件的具体解释参考这篇文章《用Swig封装C/C++》。然后在当前目录内建立文件夹srcwraptradeapi32ctp,在ctp文件夹内建立文件夹thosttraderapi。文件夹结构图如下:

    20180109_tradeapi_windows

    │─── ctp ─── thosttraderapi

    │─── tradeapi32

    │─── src

    │─── wrap

    │ ThostFtdcMdApi.h
    │ ThostFtdcTraderApi.h
    │ ThostFtdcUserApiDataType.h
    │ …

    打开windows cmd工具,cd到当前目录\20180109_tradeapi_windows下。 在cmd中运行命令

    swig.exe -c++ -java -package ctp.thosttraderapi -outdir src -o thosttraderapi_wrap.cpp thosttraderapi.i
    

    这运行可能要持续1分钟,中间会报一次warning 514:...警告,在程序员眼里警告直接忽略:)。等到运行完成后,可以看到当前目录下生成了

    thosttraderapi_wrap.h
    thosttraderapi_wrap.cpp

    这两个文件是用于包装原来C++接口的文件,下面要用。打开src文件夹,可以看到这时在里面生成了一系列方法的java文件,如下:

    CThostFtdcAccountregisterField.java
    CThostFtdcAuthenticationInfoField.java
    … … …
    thosttradeapiJNI.java

    cmdcdsrc文件夹底下,运行命令

    javac *.java
    

    运行结束之后可以看到生成了等量的class文件,将所有的class文件拷贝到\ctp\thosttraderapi\文件夹中,cmdcd20180109_tradeapi_windows目录下,运行命令

    jar cf thosttraderapi.jar ctp
    

    这样我们就在当前文件夹下得到了jar包thosttraderapi.jar

    3. 通过C++得到java可调用的dll动态库

    接下来在wrap文件夹中,建立一个C++工程,工程为Win32控制台应用程序,工程名为thosttraderapi_wrap,点下一步,工程的应用程序类型选DLL,附加选项选空项目。另外建好项目后,在工程属性-c/c+±代码生成-运行库中选多线程(/MT)。步骤图如下:
    1)

    2)
    3)

    完成之后,将如下文件拷贝到\wrap\thosttraderapi_wrap文件夹下:

    ThostFtdcTraderApi.h
    ThostFtdcUserApiDataType.h
    ThostFtdcUserApiStruct.h
    thosttraderapi.lib
    thosttraderapi_wrap.cpp
    thosttraderapi_wrap.h
    libiconv.lib
    iconv.h

    c++工程中添加现有项,将这些文件全部添加到工程中去。下面还要做几步:

    • 将你安装jdk目录\Java\jdk1.8.0_111\include下的jni.hwin32文件夹下的jni_md.h, jawt_md.h一共三个文件拷贝到安装vs的include目录底下\Microsoft Visual Studio 12.0\VC\include。这是因为thosttraderapi_wrap.cpp文件中包含了<jni.h>,这是用于生成Java可调用接口的库文件。

    这所有完成之后,C++工程中文件应该如下:

    然后选择release版编译。我们按F7编译,在\wrap\thosttraderapi_wrap\Release目录底下可见thosttraderapi_wrap.dll动态库文件,说明编译成功,这样CTP Java API就编译成功了。编译中如果出现error LNK2005: abort 已经在 LIBCMT.lib(abort.obj) 中定义...等错误,可以右击工程 - 属性 ”配置属性 - 链接器 - 命令行” 添加: /NODEFAULTLIB:"libcmt.lib"解决。

    4. Java Demo

    打开Eclipse,新建traderapidemo工程。

    • 在工程中新建lib文件夹,将刚刚的thosttraderapi.jar包拷贝到该文件夹底下,刷新工程,在工程中jar包上右击选择BuildPath/Add to Build Path将jar包导入到工程。
    • 将如下动态库文件

    thosttraderapi.dll
    thosttraderapi_wrap.dll

    拷贝到你电脑环境变量path路径底下,如果自己不清楚,可以在Java中用如下代码获得

    System.out.println(System.getProperty("java.library.path"));
    

    我直接拷贝到了\Java\jdk1.8.0_111底下。
    完整的tradeapidemo代码如下:

    import ctp.thosttraderapi.*;
    
    class TraderSpiImpl extends CThostFtdcTraderSpi{	
    	final static String m_BrokerId = "9999";
    	final static String m_UserId = "070624";
    	final static String m_InvestorId = "070624";
    	final static String m_PassWord = "passwd"; 
    	final static String m_TradingDay = "20181122";
    	final static String m_AccountId = "070624";
    	final static String m_CurrencyId = "CNY";
    	TraderSpiImpl(CThostFtdcTraderApi traderapi)
    	{
    		m_traderapi =  traderapi;
    	}
    	
    	@Override
    	public void OnFrontConnected(){
    		System.out.println("On Front Connected");
    		CThostFtdcReqUserLoginField field = new CThostFtdcReqUserLoginField();
    		field.setBrokerID(m_BrokerId);
    		field.setUserID(m_UserId);
    		field.setPassword(m_PassWord);
    		field.setUserProductInfo("JAVA_API");
    		m_traderapi.ReqUserLogin(field,0);
    		System.out.println("Send login ok");
    	}
    	
    	@Override
    	public void OnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast)
    	{
    		if (pRspInfo != null && pRspInfo.getErrorID() != 0)
    		{
    			System.out.printf("Login ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
    
    			return;
    		}
    		System.out.println("Login success!!!");
    		CThostFtdcQryTradingAccountField qryTradingAccount = new CThostFtdcQryTradingAccountField();
    		qryTradingAccount.setBrokerID(m_BrokerId);
    		qryTradingAccount.setCurrencyID(m_CurrencyId);;
    		qryTradingAccount.setInvestorID(m_InvestorId);
    		m_traderapi.ReqQryTradingAccount(qryTradingAccount, 1);
    				
    	}
    	
    	@Override
    	public void OnRspQryTradingAccount(CThostFtdcTradingAccountField pTradingAccount, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) 
    	{
    		if (pRspInfo != null && pRspInfo.getErrorID() != 0)
    		{
    			System.out.printf("OnRspQryTradingAccount ErrorID[%d] ErrMsg[%s]\n", pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
    
    			return;
    		}
    		
    		if (pTradingAccount != null)
    		{
    			System.out.printf("Balance[%f]Available[%f]WithdrawQuota[%f]Credit[%f]\n",
    				pTradingAccount.getBalance(), pTradingAccount.getAvailable(), pTradingAccount.getWithdrawQuota(),
    				pTradingAccount.getCredit());
    		}
    		else
    		{
    			System.out.printf("NULL obj\n");
    		}
    	}	
    	
    	private CThostFtdcTraderApi m_traderapi;
    }
    
    public class tradeapidemo{
    
    	static{
    		System.loadLibrary("thosttraderapi");
    		System.loadLibrary("thosttraderapi_wrap");
    	}
    	final static String ctp1_TradeAddress = "tcp://180.168.146.187:10000";
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		CThostFtdcTraderApi traderApi = CThostFtdcTraderApi.CreateFtdcTraderApi();
    		TraderSpiImpl pTraderSpi = new TraderSpiImpl(traderApi);
    		traderApi.RegisterSpi(pTraderSpi);
    		traderApi.RegisterFront(ctp1_TradeAddress);
    		traderApi.SubscribePublicTopic(THOST_TE_RESUME_TYPE.THOST_TERT_RESTART);
    		traderApi.SubscribePrivateTopic(THOST_TE_RESUME_TYPE.THOST_TERT_RESTART);
    		traderApi.Init();
    		traderApi.Join();
    		return;
    	}
    }
    
    

    行情mdapidemo如下

    import ctp.thostmduserapi.*;
    
    class mdspiImpl extends CThostFtdcMdSpi{
    	final static String m_BrokerId = "9999";
    	final static String m_UserId = "070624";
    	final static String m_InvestorId = "070624";
    	final static String m_PassWord = "passwd"; 
    	final static String m_TradingDay = "20181122";
    	final static String m_AccountId = "070624";
    	final static String m_CurrencyId = "CNY";
    	mdspiImpl(CThostFtdcMdApi mdapi)
    	{
    		m_mdapi =  mdapi;
    	}
    	
    	public void OnFrontConnected(){
    		System.out.println("On Front Connected");
    		CThostFtdcReqUserLoginField field = new CThostFtdcReqUserLoginField();
    		field.setBrokerID(m_BrokerId);
    		field.setUserID(m_UserId);
    		field.setPassword(m_PassWord);
    		m_mdapi.ReqUserLogin(field, 0);
    		
    	}
    	
    	public void OnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
    		if (pRspUserLogin != null) {
    			System.out.printf("Brokerid[%s]\n",pRspUserLogin.getBrokerID());			
    		}
    		
    		String[] instruementid = new String[1];
    		instruementid[0]="rb1906";		
    		m_mdapi.SubscribeMarketData(instruementid,1);
    	}
    	
    	public void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField pDepthMarketData) {
    		if (pDepthMarketData != null)
    		{
    			System.out.printf("AskPrice1[%f]BidPrice1[%f]\n",
    				pDepthMarketData.getAskPrice1(),pDepthMarketData.getBidPrice1());
    		}
    		else
    		{
    			System.out.printf("NULL obj\n");
    		}
    	}	
    	private CThostFtdcMdApi m_mdapi;
    }
    
    public class MdapiDemo {
    	static{
    		System.loadLibrary("thostmduserapi");
    		System.loadLibrary("thostmduserapi_wrap");
    	}
    	final static String ctp1_MdAddress = "tcp://180.168.146.187:10031";
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		CThostFtdcMdApi mdApi = CThostFtdcMdApi.CreateFtdcMdApi();
    		mdspiImpl pMdspiImpl = new mdspiImpl(mdApi);
    		mdApi.RegisterSpi(pMdspiImpl);
    		mdApi.RegisterFront(ctp1_MdAddress);
    		mdApi.Init();
    		mdApi.Join();		
    		return;
    	}
    }
    

    本人所编译的CTP0606版本的交易、行情Java API点击下载。声明:仅是个人爱好编译,对此API引起的您的任何损失不负责任。

    各位注意一下,在上传的win32资源中,mdapi里的一个dll上传错了,只要将ctp官网中的“thostmduserapi.dll”替换掉“thosttraderapi.dll”就可以运行了。是我粗心上传错了,抱歉!

    展开全文
  • 动态库编程详解

    千次阅读 2012-08-03 22:07:42
    一、动态库概念与分类 1、什么是动态库 2、动态库分类 4、动态库解决的问题 二、动态库的创建 1、规则动态库 2、声明导出函数的两种方式 2.1__declspec(dllexport)导出 2.2 .def文件导出 3...

     

    目录

    概述

    一、动态库概念与分类

    1、什么是动态库

    2、动态库分类

    4、动态库解决的问题

    二、动态库的创建

    1、规则动态库

    2、声明导出函数的两种方式

    2.1__declspec(dllexport)导出

    2.2 .def文件导出

    3、导出导入类

    三、隐式、显示调用动态库

    1、动态库隐式调用

    2、动态库显示调用

    3.显示、隐式调用的区别

    四、动态库的测试

     

     

     

    概述

          动态库是继静态库发展起来的一种封装重用技术,在灵活性、扩展性、重用性各方面取得了突破,本文只介绍动态库的基础知识,关于动态库的高级编程,推荐参看Jeffrey ReichterChiristophe Nasarre合著的《Windows核心编程》第5版。

          

    一、动态库概念与分类

    1、什么是动态库

       DLL(Dynamic Linkable Library)动态链接库亦简称动态库,它是一块封装好的代码块,包含着一些方法,一般不包括消息循环,也建议不要去包含这些。可把它看成一个仓库,其提供了可直接使用的变量、函数、类等。打个不太生动的比喻,动态库犹如保卫森严的生产基地,但你可以通过正确入口进入,获得你想要的东西,你不用管也管不着这东西是怎么生产的,拿走从出口出来就行,同时生产基地是共享的,大家都可以通过入口获得相应的东西。

          在“库”的发展史上经历了“无库---静态库---动态库”的时代,无论是动态库还是静态库都能解决代码共享的问题。

    动态库是基于二进制级重用的,所以与语言无关、环境无关(前提是你动态库中没有涉及对环境有依赖的东西,如调用一些第三方DLL)的,再一个得遵循DLL接口规范和调用约定,简而言之,用各种语言编写的标准DLL其他语言都可以调用。所以如果想创建一个通用的DLL,那么得严格遵守DLL规范,包括导出、调用约定、形参几方面的内容。

     

    2、动态库分类 

    通过VC++工具编写的动态库分为两类----规则DLL与非规则动态库,Visual C++支持编写三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLLMFC规则DLL)、MFC Extension DLLMFC扩展DLL)。非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFCMFC编写的应用程序所调用;MFC规则DLL包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

    4、动态库解决的问题

          节省资源:如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以动态地引用和卸载这个与EXE独立的DLL文件。

          节省内存,假如本地有多个进程用到动态库,那么在内存只是只载入一次的,两个进程共用该DLL在内存中的页面。

    灵活性:我认为灵活性才是动态库最值得称道的地方,发行的动态库,只要原有的接口不改变,那么你可以任意地改动,增加动态库里面的内容,同时以新版本替换旧版本,而不影响依赖于此动态库的程序。举个例子吧,例如你的一个软件已经发行了,如果出了什么问题,只要找出出问题的模块(假如它就是个动态库),并处理好,把新版的动态库给用户替换旧版的就OK了,如果全是静态库呢?那只有把整个工程编译一编,发一个大包给人家,人家也许还要把以前的软件卸载,重新装一次新的版本。

    模块化:这对大项目特别有利,利用动态库可以把项目切割成N个小块加以分工,还有利于错误定位等,岂不快哉。

    语言无关性:只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL其他语言都可以相互调用。

    特殊用途:windows提供的一些特性只有通过DLL才用使用,如钩子(hook)就需要通过DLL来实现,关于钩子的编程细节,将在后续的博文中详细介绍。

     

    二、动态库的创建

    1、规则动态库

       VC++6.0为开发工具,在VC++中创建Win32 Dynamic-Link Library工程,下图中选择第一项,当然也可以选择其他项。

    在创建的工程中创建一个头文件MyDll.h与一个CPP文件MyDll.cpp,在头文件与源文件中写入以下代码:

    //MyDll.h

    #ifndef MYDLLEIPORT

    #define MYDLLEIPORT extern "C" __declspec(dllimport)

    #endif

    MYDLLEIPORT int add(int a, int b);

    MYDLLEIPORT int     g_nCount;

     

    定义实体:

    #define MYDLLEIPORT extern "C" __declspec(dllexport)

    #include "MyDll.h"

    int g_nCount = 0;

    int add(int a, int b){

          return (g_nCount += a + b);

    }

    代码写完了,现在的工作是怎么把函数导出的问题了,下面分析如何导出函数,与导出的方式

    2、声明导出函数的两种方式

    DLL中导出函数的声明有两种方式:一种是在函数声明中加上__declspec(dllexport)调用DLL时配合.lib文件通过__declsped(dllimport)指令导入函数即可,另外一种方式是采用模块定义(.def)文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。

    通过修饰符__declspec(dllexport)导出的函数,可能会面临函数名改编的问题,而通过.def文件导出的,将不会有此问题,要导出编译时函数名不会被改编的函数,还有一种方法,就是在DLL源文件中输入以下代码:

    #pragma comment(linker, “/export:add=_add@8”)

    不过此方法有个麻烦事,你得事先知道函数改篇后的名字是什么,如函数add改编后是_add@8,还有此方法导出的函数名是两个,”add””_add@8”,个人建议使用.def文件导出最为方便快捷,省去非常多的麻烦。

          这两种导出方式都会生成一个与Dll同名的.lib文件,此文件在隐式调用时得用。.lib文件只是包含一些导出函数的属性说明信息。

    2.1__declspec(dllexport)导出

       在头文件声明函数前加上__declspec(dllexport),如下:

    #ifndef MYDLLEIPORT

    #define MYDLLEIPORT extern "C" __declspec(dllimport)

    #endif

    MYDLLEIPORT int add(int a, int b);//导出函数

    MYDLLEIPORT int g_nCount;//导出变量

    其中extern"C"表示函数是安C语言的方式编译,C++对函数名称编译时会改编如add,C++编译后变成_add@8,改名后,C语言或者其他语言调用的时候会出现问题,会找不到,同时不同厂家的编译器编译时生成的函数名也可能不一样; __declspec(dllexport)是导出修饰符,指明导出此函数,不用过分解释,照用就行;下面是对extern "C"的解释:

    extern "C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”,表示可被外部文件引擎;其次,被它修饰的目标是“C”的,表示按照C语言方式编译和连接。

    编译规(extern "C")必须指定,才能编译出规范的DLL接口。用这个方法导出接口,如果用VC++编译,还是会有函数名改编的问题,编译后,函数名应该是_add@8,调用DLL时,如果用同一厂家(VC++)编译器,是不是会有问题的,用其他厂家的编译器,那就不能保证了,所以这种导出方法并不是很好,可采用下面介绍的通过.def文件导出的方法。

    2.2 .def文件导出

          此方法导出函数,将避免函数名改编的问题。在工程中添加一.def文件---MyDll.def:

    在新加入的MyDll.def文件中输入以下代码:

    ;MyDll.def :导出DLL函数

    LIBRARY DLLDemo

    EXPORTS

    add @ 1

    g_nCount DATA

    .def文件的规则为:
      (1)LIBRARY语句说明.def文件相应的DLL
      (2)EXPORTS语句后列出要导出函数的名称或者变量。可以在.def文件中的导出函数

    名后加@n,表示要导出函数的序号为n,在进行函数调用时,这个序号将发挥其作用,

    可通过序号来调用相应的函数(通过序号取得函数方法:GetProcAddress(hDll,

    MAKEINTRESOURCE(1))),不过此方法不推荐,因为难以维护之类的原因,现在基本

    上不怎么使用;
      (3).def文件中的注释由每个注释行开始处的分号(;)指定,且注释不能与语句共享一行。

          (4)导出全局变量格式:全局变量DATADATA是关键字,例:g_nCount DATA

    其实也可简单地写成如下格式:

    EXPORTS

    add

    g_nCount DATA

    不需要序号,只给个函数名就OK,记住别把函数名弄错了。

     

    3、导出导入类

       最好不要导出类,类的导出会破坏DLL的通用性,C++写的类,C#或者其他语言不一定支持,在这里只是简单的说一下。

    //文件名:point.hpoint类的声明
    #ifndef   POINT_H
    #define  POINT_H
    #ifdef  DLL_FILE
     class _declspec(dllexport) point //导出类point
    #else
     class _declspec(dllimport) point //导入类point
    #endif
    {
     public:
      float y;
      float x;
      point();
      point(float x_coordinate, float y_coordinate);
    };

    #endif

    此处有宏POINT_HDLL_FILE,第一个宏的作用当然是防止头文件重编译,第二个宏则是用来判断当前是用来导出还是导入的,在此头的实现文件(.cpp)中先对DLL_FILE定义,则导出此文件,而当用户用这个动态库时,因为不知道DLL_FILE,所以自然没定义,则会导入此文件。
    //文件名:point.cpppoint类的实现
    #ifndef DLL_FILE
     #define DLL_FILE
    #endif
    #include "point.h"
    //
    point的缺省构造函数
    point::point()
    {
     x = 0.0;
     y = 0.0;
    }

    类的导出导入看似乎不适合动态调用,因为要包含其头文件,在主调应用程序中生成对象。注意类的导出导入格式如下:

    class _declspec(dllexport) 类名 //导出类point
        class _declspec(dllimport) 类名 //导入类point

    三、隐式、显示调用动态库

    1、动态库隐式调用

       隐式调用也有通过指令与通过IDE设置两种方式。隐式调用需要.lib文件,把.dll.lib放同一目录下,然后按以下方法操作

    Ø #pragma comment指令:

          #include “..\MyDll.h”----可以是绝对路径,也以的相对路径

          #pragma comment(lib, “..\\DllDemo.lib”) ----可以是绝对路径,也可以是相对路径

    这种方法似乎有点麻烦,必须把路径设对了,不然可能就找不到。

    Ø 在编译器(VC)中设置:

    1、依次选择tools->options->directories->Show directories for->Library files然后

    添加DllDemo.lib的路径。

    2、依次选择tools->options->directories->Show directories for->Include files然后选择

    MyDll.h的头文件的路径。

          3、依次选择Proctect->Setting->Link然后在Object/library modules中添加上自定义

    .lib库(DllDemo.lib)。

          4、使用自定义库中的函数的时候#include “MyLib.h”就可以了。

    示例代码:

    #include "..\\MyDll.h"

    #pragma comment(lib, "..\\Debug\\DLLDemo.lib")

    int nResutl = add(1, 2);//直接调用

    2、动态库显示调用

       显示调用,不需要头文件,也不需要.lib库文件,随时载入随时释放,比较灵活。

    查看动态库信息的工具:

    VC++的安装目录下的Depends工具,可以看到动态库的接口信息。

    显式调用:

    显示调用首先得知道Dll的路径,然后通过API(LoadLibrary)装载DLL,获得DLL的句柄,通过API(GetProcAddress)配合LoadLibrary返回的句柄再找到想要调用的函数的地址(返回的是指向对应函数地址的指针),通过函数指针就可以调用函数了。就是这么简单,以下是方法:

    //定义函数指针类型,用以调用动态库中函数,以下是定义函数指针类型的格式:

    //typedef [返回类型] (__stdcall *[指针名])(形参);

    typedef int (* PADD)(int, int); //定义指针类型
    int main(int argc, char *argv[])
    {  HINSTANCE hDll; //DLL
    句柄
      PADD addFun; //函数指针

    HINSTANCE hInstance = LoadLibrary(".\\DllDemo.dll");
      if (hInstance != NULL) {     

          addFun = (PADD)GetProcAddress(hInstance, "add");//获得函数地址

          int nResult = pAdd(1, 6);  //通过函数指针调用函数

          int *pCount = (int *)GetProcAddress(hInstance, "g_nCount");//获得全局变量指针

          int nCount = *pCount;

    FreeLibrary(hInstance );                //记得释放哦
     }
     return 0;
    }

    显示调用与隐式调用却有较大差异,下面我们来逐一分析。
      首先,语句typedef int ( * lpAddFun)(int,int)定义了一个与add函数接受参数类型和返回值均相同的函数指针类型。随后,在main函数中定义了lpAddFun的实例addFun

      其次,在函数main中定义了一个DLL HINSTANCE句柄实例hInstance,通过Win32 Api函数LoadLibrary动态加载了DLL模块并将DLL模块句柄赋给了hDll

      再次,在函数main中通过Win32 Api函数GetProcAddress得到了所加载DLL模块中函数add的地址并赋给了addFun。经由函数指针addFun进行了对DLLadd函数的调用;

      最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已经加载的DLL模块。

          :如果是用.def文件定义的导出函数,并为函数给定的序号,则可用以下方法调用函数被调用的方法,GetProcAddress(hDll, MAKEINTRESOURCE(1))

     

    3.显示、隐式调用的区别

       从使用层面上讲,隐式调用方便,载入一次(主进程启动的时候载入),就可以到处使用,而显示调用则麻烦些,得载入释放,还有取得相应函数的地址之类。然而显示调用灵活,节省空间,因为使用时才载入,而不用时释放,同时显示调用不需要.lib库之类。

     

    四、动态库的测试

       动态库写好之后,如何测试呢,在这介绍一种比较简单的方法,在动态库的工程中,按F5将出现以下界面:

    意思是动态库是不能单独运行的,你得给它配一个主进程调用它,所以可以写一个简单的程序按上面调用动态库的方法调用动态库中想要测试的函数,编译通过后把exe文件的路径设置在上图的输入框中,点确定之后,再按F5,这时主进程就启动了,你在主进程中触发调用动态库函数的操作,这时将跳进动态库的工程中,动态库的工程处于调试状态,你可以一步一步跟踪,这样就能很好地测试动态库,并找出bug

    配套源码下载:http://download.csdn.net/detail/mingojiang/4475172

     

    转载请注明出自:http://blog.csdn.net/mingojiang

     

    展开全文
  • 下载linux下的liviconv,自己编译,我这里用到的是liviconv.a静态。 从上期技术官网获得linux版本的tradeapi。拷贝到相应linux环境目录底下。 2 通过swig获得jar包linux和windows下的jar包得到方式是一样的,...

    前言:上篇中已经讲了windows下的Java API编译,这篇讲linux的。

    0 欢迎交流

    CTP_API技术交流群:767101469

    https://github.com/nicai0609/JAVA-CTPAPI


    1 准备工作

    • 下载linux下的libiconv库,自己编译,我这里用到的是liviconv.a静态库,可以到我github上下我编译好的。
    • 从上期技术官网获得linux版本的tradeapi。拷贝到相应linux环境目录底下。

    2 通过swig获得jar包

    linux和windows下的jar包得到方式是一样的,参考上一篇,可以直接拿来用

    3 编译得到包装动态库so

    将上一篇中windows下的thosttraderapi_wrap.cppthosttraderapi_wrap.h两个文件拷贝到linux相应的目录底下。现在linux目录底下的文件应该如下

    libiconv.a
    ThostFtdcTraderApi.h
    ThostFtdcUserApiStruct.h
    thosttraderapi_wrap.h
    libthosttraderapi.so
    makefile
    ThostFtdcUserApiDataType.h
    thosttraderapi_wrap.cpp
    iconv.h

    其中makefile的内容如下:

    OBJS=thosttraderapi_wrap.o
    INCLUDE=-I./ -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
    TARGET=libthosttraderapi_wrap.so
    CPPFLAG=-shared -fPIC
    CC=g++
    LDLIB=-L. -lthosttraderapi
    $(TARGET) : $(OBJS)
            $(CC) $(CPPFLAG) $(INCLUDE) -o $(TARGET) $(OBJS) $(LDLIB) ./libiconv.a                                                                              
    $(OBJS) : %.o : %.cpp
            $(CC) -c -fPIC $(INCLUDE) $< -o $@ 
    clean:
            -rm -f $(OBJS)
            -rm -f $(TARGET)
    install:
            cp $(TARGET) /usr/lib
    

    执行make命令,得到libthosttraderapi_wrap.so库,说明编译成功。

    4 Java Demo

    将jar文件,两个动态库so文件,上一篇中的Demo.java文件拷贝到同一个文件夹test中,该文件夹中清单如下:

    libthosttraderapi.so
    libthosttraderapi_wrap.so
    thosttraderapi.jar
    Demo.java

    在当前目录下执行命令:

    javac -cp ./thosttraderapi.jar Demo.java
    

    编译成功后,执行运行命令:

    java -cp ./thosttraderapi.jar:./ Demo
    

    可以看到运行成功。

    Linux 64位 CTP JavaAPI 点击下载

    展开全文
  • 我有一个梦想,是改变世界,这是很多技术人员的梦想;从小事做起,踏实做人做事,当身边的人或事因为自己能向更好的方向改变或发展的时候,那就是在改变世界,至花甲之时,可能我的梦想也无法实现,但我会一直追逐着...
  • 起因 理论功底 动态库和静态库 介绍 静态库和动态库的区别 举个例子, iOS 项目中使用 Embeded Framework ...静态库和动态库如何构建和加载 ...静态库和动态库依赖关系 ...制作动态库
  • 完整视频播放器封装库

    千次阅读 2018-01-16 14:40:28
    1.关于此视频封装库介绍 1.1 能够满足那些业务需求 1.2 对比同类型的有哪些优势 2.关于使用方法说明 2.1 关于gradle引用说明 2.2 添加布局 2.3 最简单的视频播放器参数设定 2.4 注意的问题 2.5 关于开源...
  • 研究了分布式数据源并行查询优化技术、多源异构数据转换处理技术、基于API变化捕获的数据库同步方法,实现了对各服务商、二级中心实时配件库存信息的Web封装动态调用。提出的模型与算法在汽车产业链协同平台上...
  • 静态库和动态库的区别

    千次阅读 2013-10-15 08:52:11
    注意:Linux 静态编译时将动态库也编入文件中。 文件预览 文件目录树如下,如你所见,非常简单。  1. libtest/  2. |-- lt.c  3. |-- lt.h  4. `-- test.c 代码 #lt.c  1. /* lt.c  2. *  3. */  4....
  • <br />摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理。 但不同...
  • 在Linux环境下打包成动态库

    千次阅读 2017-03-14 17:39:18
    在Linux环境下打包成so动态库,被java调用。 一、用到了ndk技术。(略说)  1.生成头文件 .h  2.C/C++接口代码被jni调用。 二、打包so动态库如下: 1.打包成扩展名为o的文件。  g++ -fPIC -D_...
  • iOS静态相关-封装lib

    万次阅读 2013-06-27 17:13:30
    所以在实际的项目开发中,经常会使用到函数库,函数库分为静态库和动态库两种。和多数人所熟悉的动态语言和静态语言一样,这里的所谓静态和动态是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,...
  • 研究了分布式数据源并行查询优化技术、多源异构数据转换处理技术、基于API变化捕获的数据库同步方法,实现了对各服务商、二级中心实时配件库存信息的Web封装动态调用。提出的模型与算法在汽车产业链协同平台上...
  • 3,也可以重用,注意不是共享使用使用动态库的好处1使用动态库,可以将最终可执行文件体积缩小 2使用动态库,多个应用程序共享内存中得同一份库文件,节省资源 3使用动态库,可以不重新编译连接可执行程序的前提下...
  • 动态库和静态库的区别 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。 1. 静态函数库 这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个...
  • JSP/Java调用本地动态链接,调用第三方动态链接
  • 分析Windows和Linux动态库

    千次阅读 2008-12-25 09:41:00
    摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理。但不同操作系统的动态库...
  • 要DLL 是一种应用最为广泛的动态链接技术但是由于在DLL 中封装和调用对象受到对象动态绑定机制的限制使得DLL 在封装对象方面有一定的技术难度导致有些Delphi 程序员误以为DLL 只支持封装函数不支持封装对象本文着重...
  • iOS 静态库,动态库与 Framework 浅析

    千次阅读 2016-12-15 17:40:16
    静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用。 什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人看到...
  • 这个是我自己总结的,非常详细,每一个地方都有截图说明。...我和同事花了三天时间成功实现java调用c++封装的dll文件。就自己总结了一下,和大家分享。新手上路,如有错误请多见谅,请多多指出!
  • C++编译动态库第三方库及使用

    千次阅读 2019-02-13 16:05:55
    C++编译动态库第三方库及使用 为什么很多人,尤其各大厂商只给我们库文件让我们调用,而不直接提供源代码呢?这样的好处是1.把代码封装成各个模块以便复用。 2.省去各种底层调用,省去了大量的代码复制,更符合面向...
  • 一,VS2013动态库文件的创建 1.新建项目,win32,win32项目,输入项目名称,例如:MakeDll。 2.“确定”---“下一步”,选择“DLL”选项,再点“完成”: 3.菜单栏选择“项目—>添加新项”,来创建...
  • 封装技术-软件开发者的爱与恨

    千次阅读 2007-12-10 10:51:00
    谈起封装技术,我想大家并不陌生,从二进制到汇编,再到c语言和其它面向过程的语言,都是从简单易用的角度进行了封装,已经屏蔽了相当多的细节了,不光是不同的硬件,以及硬件的操作指令及其二进制结构。封装意味着...
  • 问题集合 ---- linux 静态库和动态库 =================================================================== linux静态库和动态库分析 本文转自 ...
  • IOS静态相关-封装lib

    千次阅读 2013-07-11 20:11:05
    所以在实际的项目开发中,经常会使用到函数库,函数库分为静态库和动态库两种。和多数人所熟悉的动态语言和静态语言一样,这里的所谓静态和动态是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,...
  • Java JNI调用本地动态库使用详解

    千次阅读 2018-09-30 14:35:51
    因此,需要有一个中介(或代理),由本地的Java代码先调用这个中介(或代理)动态库,再由这个中介(或代理)动态库调用目的动态库。 我们给中介dll起个名字,叫做Lib2Invoke。生成这个中介dll的过程比较复杂,下面...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 121,108
精华内容 48,443
关键字:

动态库封装技术