精华内容
下载资源
问答
  • 亲测可用,java 成功调用dll函数。包含调用回调函数,springboot版本。最近由于公司业务需要,要调用dll文件,用JNA调用。
  • 我们在写DLL的时候,需要在DLL项目里面加上window的API函数... 这样,windows会在用LoadLibrary加载DLL后,调用DllMain函数来执行,而你把自己的函数已经先写在在DllMain里面了,那么你的函数也自然而然被间接调用了. ...
    		我们在写DLL的时候,需要在DLL项目里面加上window的API函数DllMain.然后DllMain函数里面写上你自己的函数,
    		这样,windows会在用LoadLibrary加载DLL后,调用DllMain函数来执行,而你把自己的函数已经先写在在DllMain里面了,那么你的函数也自然而然被间接调用了.
    
    展开全文
  • 易语言dll函数动态调用源码易语言动态调用dll源码 系统结构:内部接口_查看插件,内部接口_加载插件,载入动态链接库_,呼叫窗口函数地址_,取进程地址_,FreeLibrary,外部接口_统一调用接口
  • 下面小编就为大家带来一篇C#中加载dll调用函数的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 这是一个从内存(资源形式)直接加载并调用DLL函数的例子。 xDll工程只是一个测试用的dll,附上代码,编译出的xDll.dll直接放在testLoadDll工程目录下 testLoadDll是实际测试代码,从资源直接加载Dll并调用其导出...
  • 详细描述如何加载一个动态链接库(DLL) 和调用链接库中的函数(提供有代码)
  • 易语言动态调用DLL函数源码,动态调用DLL函数,LoadLibraryExA,LoadLibraryA,FreeLibrary,GetProcAddress,CallWindowProc1,CallWindowProc
  • 易语言源码动态调用DLL函数.rar 易语言源码动态调用DLL函数.rar 易语言源码动态调用DLL函数.rar 易语言源码动态调用DLL函数.rar 易语言源码动态调用DLL函数.rar 易语言源码动态调用DLL函数.rar
  • C#调用DLL函数方法

    2013-12-16 14:23:37
    C#调用DLL函数方法: 介绍了C#下调用dll的方法和代码示例
  • 易语言调用远程进程DLL函数,非常不错的
  • 32位进程调用64位DLL函数.rar
  • QT中调用dll中的函数,3种方式demo, 1、采用显示链接,调用DLL中全局函数 2、采用显示链接,调用C++类中的类对象、成员函数 3、采用隐式链接方法,通过QLibrary类对DLL中类对象、全局函数的调用
  • 易语言动态调用DLL函数.rar 易语言动态调用DLL函数.rar 易语言动态调用DLL函数.rar 易语言动态调用DLL函数.rar 易语言动态调用DLL函数.rar 易语言动态调用DLL函数.rar
  • Dll查看器,可查看各dll文件内函数代码及调用规则,方便实用
  • c++调用dll文件访问其中的函数,c++调用dll文件访问其中的函数,c++调用dll文件访问其中的函数,c++调用dll文件访问其中的函数
  • C#调用C++DLL,C++DLL中的函数参数包含指针。 绝对可用。
  • Python调用DLL中的函数

    千次阅读 2018-09-14 17:00:44
    最近研究了一下用Python调用dll,Python果然很神奇,代码不多既实现了从窗口创建到调用dll函数的功能,程序也不复杂,最后打包成exe可直接执行的程序,测试过程中先用Python3.5,但用PyInstaller打包后的程序在xp...

    最近研究了一下用Python调用dll,Python果然很神奇,代码不多既实现了从窗口创建到调用dll中函数的功能,程序也不复杂,最后打包成exe可直接执行的程序,测试过程中先用Python3.5,但用PyInstaller打包后的程序在xp系统上不能运行,后来用2.7,打包后的exe在xp或win7、win10上均可运行了,本人用的win10,程序安装如下:

    1、https://www.python.org/-》download-》windows-》latest python 2 release-Python2.7.15
    2、pip install pywin32
    3、pip install PyInstaller

     

    附源码如下:

    #coding:utf-8
    import tkinter
    import Tkinter
    import tkinter.messagebox
    import tkinter as tk
    from tkinter import ttk
    from Tkinter import *
    import ctypes
    
    import sys      #保证能正常把二代证输出的gb2312码能够转成utf-8
    reload(sys)
    sys.setdefaultencoding('gb2312')
    
    dll= ctypes.windll.LoadLibrary('IdCard_GWI_V001.dll')
    
    def openPort(port):
        sBuf = 'A'
        pStr = ctypes.c_char_p()
        pStr.value = sBuf
        iRet=dll.InitComm(int(port), pStr, 9600)
        if iRet==0:
            msgInfo.insert(INSERT,'打开端口成功\n')
            msgInfo.insert(END,'')
        else:
            msgInfo.insert(INSERT,'打开端口失败\n')
            msgInfo.insert(END,'')
    def closePort():
        iRet=dll.CloseComm()
        msgInfo.insert(INSERT,'端口已经关闭\n')
        msgInfo.insert(END,'')
    def Authenticate():
        iRet=dll.Authenticate()
        if iRet==0:
            msgInfo.insert(INSERT,'卡认证成功\n')
            msgInfo.insert(END,'')
        else:
            msgInfo.insert(INSERT,'卡认证失败\n')
            msgInfo.insert(END,'')
    def ReadBaseMsg():
        sDllPath='.\\'
        pDllPath=ctypes.c_char_p()
        pDllPath.value=sDllPath
    
        sPhotoPath='.\\'
        pPhotoPath=ctypes.c_char_p()
        pPhotoPath.value=sPhotoPath
        sMsg="\0"*1024
        pMsg=ctypes.c_char_p()
        pMsg.value=sMsg
    
        intLen = ctypes.c_int(0)
        #tkinter.messagebox.showinfo('二代证测试',sDllPath+sPhotoPath+sMsg)
        iRet=dll.ReadBaseMsg(pDllPath,sPhotoPath,pMsg,ctypes.byref(intLen))
        if iRet==0:
            msgInfo.insert(INSERT,'读卡成功\n')
            msg=ctypes.string_at(sMsg)
            msg1 = msg.encode('utf-8')
            msgInfo.insert(INSERT,msg1+'\n')
            msgInfo.insert(END,'')
        else:
            msgInfo.insert(INSERT,'读卡失败\n')
            msgInfo.insert(END,'')
    def cardMessage(port):
        iPortNo=int(port)
    
        sBuf = 'A'
        extendPort = ctypes.c_char_p()
        extendPort.value = sBuf
    
        iBaudRate=9600
        iTimeOut=20
    
        hotohead='c:\\temp\\head.bmp'
        photohead = ctypes.c_char_p()
        photohead.value = hotohead
    
        sMsg="\0"*1024
        buf=ctypes.c_char_p()
        buf.value=sMsg
    
        errInfo="\0"*1024
        psErrInfo=ctypes.c_char_p()
        psErrInfo.value=errInfo
    
        iRet=dll.Get_Info(iPortNo,extendPort,iBaudRate, iTimeOut,photohead,buf, psErrInfo)
        if iRet==0:
            msgInfo.insert(INSERT,'读卡成功\n')
            msg=ctypes.string_at(sMsg)
            msg1 = msg.encode('utf-8')
            msgInfo.insert(INSERT,msg1+'\n')
            msgInfo.insert(END,'')
        else:
            msgInfo.insert(INSERT,'读卡失败\n')
            msgInfo.insert(END,'')
    def cardMessagePhoto(port):
        iPortNo=int(port)
    
        sBuf='A'
        extendPort=ctypes.c_char_p()
        extendPort.value=sBuf
    
        iBaudRate=9600
        iTimeOut=20
    
        hotohead='C:\\TEMP\\head.jpg'
        photohead=ctypes.c_char_p()
        photohead.value=hotohead
    
        hotoface='C:\\TEMP\\face.jpg'
        photoface=ctypes.c_char_p()
        photoface.value=hotoface
    
        hotoback='C:\\TEMP\\back.jpg'
        photoback=ctypes.c_char_p()
        photoback.value=hotoback
    
        sMsg="\0"*1024
        buf=ctypes.c_char_p()
        buf.value=sMsg
    
        errInfo="\0"*1024
        psErrInfo=ctypes.c_char_p()
        psErrInfo.value=errInfo
    
        iRet=dll.Get_Info_WithPhoto(iPortNo,extendPort ,iBaudRate,iTimeOut,photohead,photoface,photoback,buf,psErrInfo)
        if iRet==0:
            msgInfo.insert(INSERT,'读卡成功\n')
            msg=ctypes.string_at(sMsg)
            msg1 = msg.encode('utf-8')
            msgInfo.insert(INSERT,msg1+'\n')
            msgInfo.insert(END,'')
        else:
            msgInfo.insert(INSERT,'读卡失败\n')
            msgInfo.insert(END,'')
    def cleanText():
        msgInfo.delete(1.0,END)
    #tkinter.messagebox.showinfo('二代证测试',str(port))
    root=Tk()
    root.title('二代证测试程序')
    root['bg'] = '#bcbcbc'
    root.attributes("-alpha", 0.9)
    root.geometry('800x500')
    
    #dhl:显示交互信息的文本框
    msgInfo = Text(root,width=110,height=30)
    msgInfo.place(x=10,y=90,anchor=NW)
    
    #dhl:输入端口号
    Label(root,text='端口号').place(x=10,y=10,anchor=NW)
    port = tk.StringVar()
    port.set(9)
    portNum = ttk.Entry(root, width=6,textvariable=port)
    portNum.place(x=55,y=10,anchor=NW)
    
    #dhl:打开端口按钮
    oPort=Button(root,text='打开端口',width=10,command=lambda:openPort(port.get()))
    oPort.place(x=10,y=40,anchor=NW)
    
    #dhl:卡认证
    Aut=Button(root,text='认证卡片',width=10,command=lambda:Authenticate())
    Aut.place(x=100,y=40,anchor=NW)
    
    #dhl:读信息
    Read=Button(root,text='读卡',width=10,command=lambda:ReadBaseMsg())
    Read.place(x=190,y=40,anchor=NW)
    
    #dhl:关闭端口按钮
    cPort=Button(root,text='关闭端口',width=10,command=lambda:closePort())
    cPort.place(x=280,y=40,anchor=NW)
    
    
    #Label(root,text='XX农信接口',height=2).place(x=450,y=40,anchor=NW)
    
    Label(root,bitmap='info',text='  XX农信-->>', height=22,compound=Tkinter.LEFT).place(x=410,y=40,anchor=NW)
    
    
    #dhl:XX农信的接口1,获取文字信息
    cardMsg=Button(root,text='读卡信息',width=10,command=lambda:cardMessage(port.get()))
    cardMsg.place(x=530,y=40,anchor=NW)
    
    #dhl:XX农信的接口1,获取文字及图片
    cardMsgPhoto=Button(root,text='文字图片',width=10,command=lambda:cardMessagePhoto(port.get()))
    cardMsgPhoto.place(x=615,y=40,anchor=NW)
    
    #dhl:清除显示内容
    clean=Button(root,text='清除',width=10,command=lambda:cleanText())
    clean.place(x=700,y=40,anchor=NW)
    
    root.mainloop()

     

    展开全文
  • 实例在Visual Studio 2008 SP1 IDE中如何创建、编写和导出DLL,以及如何调用生成的DLL
  • 使用Delphi编写静态调用动态链接库(DLL),完成三个数的大小对比,求出最大值,最小值。
  • 易语言模块动态调用DLL函数.rar 易语言模块动态调用DLL函数.rar 易语言模块动态调用DLL函数.rar 易语言模块动态调用DLL函数.rar 易语言模块动态调用DLL函数.rar 易语言模块动态调用DLL函数.rar
  • 使用JNA调用DLL函数遇到的问题

    万次阅读 2017-02-24 10:45:45
    在项目中需要使用JAVA调用DLL函数,搞了几天终于搞通了,赶紧把理解的重要内容记录下来。 使用DLL函数查看器进行查看函数名称及各参数 本例子就拿GetMac函数进行举例,记事本中为在VB环境中调用此函数的...

    在项目中需要使用JAVA调用DLL函数,搞了几天终于搞通了,赶紧把理解的重要内容记录下来。

    使用DLL函数查看器进行查看函数名称及各参数



    本例子就拿GetMac函数进行举例,记事本中为在VB环境中调用此函数的函数声明

    此函数只有一个参数,通过传入字符串类型的IP地址,可以得到对应IP地址的MAC地址

    因为传入参数为Long整形,故猜测应该传入的类型为指针类型,在JNA中对应的Pointer类,则我们需要使用long(指针类型)至String字符类型的转换


    Pointer类中有peer这个成员变量记录的是真实原生指针地址类型为long 

    通过查看JNA的API初始化Pointer实例类可以通过new Pointer(Long long) {long 转换为 Pointer类型}还有new Memory(long size){在内存中开辟大小为size长度的字符串空间这里需要注意size大小要比真实字符串长度大1否则在赋值的时候会提示数组边界超出} 等多种方式实现


    nativeVlaue(Pointer p) 可以将指针类型转换为long 类型


    可以设置Pointer类型中字符串的值即给Pointer类型进行字符串的赋值操作


    可以将Pointer类型中的字符串值进行取出,至此各种类型转换函数已具备,开始写代码,定义JNAMAC接口类方便DLL的调用

    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import jdk.nashorn.internal.objects.NativeString;
    
    public interface JNAMAC extends Library {
        JNAMAC instanceDll = Native.loadLibrary("NetDLL", JNAMAC.class);
        public long GetLocalMac();
        public long GetMac(long ip);
    }

    测试部分代码如下:

    
    
    String ip = "127.0.0.1";			//ip字符串
    Pointer pointer = new Memory(ip.length() + 1);	//初始化指针开辟内存空间
    pointer.setString(0, ip);			//指针字符串赋值
    System.out.println(new Pointer(Pointer.nativeValue(pointer)).getString(0));
    
    long l = JNAMAC.instanceDll.GetMac(Pointer.nativeValue(pointer));//调用DLL中GetMac函数
    Pointer p = new Pointer(l);			//定义返回值指针
    System.out.println("MAC:" + p.getString(0));	//返回指针字符并显示

    运行发生Invalid Memory Access错误


    只能卡在这里,不知道问题出在何处,无奈时经过仔细的查看API看到


    仔细经过思考,觉得Pointer中的成员变量peer为真实的内存空间地址,若调用DLL访问真实地址,肯定会导致非法内存访问而报错,而getLong函数返回的是Pointer的对应指针虚拟地址(这只是我自己个人的浅薄理解,如果哪位高人知道真正原因,请不吝赐教,留言等,在此非常感谢)

    故抱着试试看的态度修改代码

    String ip = "127.0.0.1";			//ip字符串
    Pointer pointer = new Memory(ip.length() + 1);	//初始化指针开辟内存空间
    System.out.println("赋值前Pointer peer:" + Pointer.nativeValue(pointer));
    System.out.println("赋值前Pointer long:" + pointer.getLong(0));
    pointer.setString(0, ip);			//指针字符串赋值
    System.out.println("赋值后Pointer peer:" + Pointer.nativeValue(pointer));
    System.out.println("赋值后Pointer long:" + pointer.getLong(0));
    System.out.println(new Pointer(Pointer.nativeValue(pointer)).getString(0));
    	
    long l = JNAMAC.instanceDll.GetMac(pointer.getLong(0));//调用DLL中GetMac函数
    Pointer p = new Pointer(l);			//定义返回值指针
    System.out.println("MAC:" + p.getString(0));	//返回指针字符并显示


    至此结果正确,问题得到完美解决,可以看到真实地址peer在赋值前后没有发生变化,而赋值前后的虚拟地址发生了变化。针对以上结论欢迎高人给予正确的指点,谢谢。

    展开全文
  • DLL的优点 简单的说,dll有以下几个优点: 1) 节省内存。同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中。如果使用dll,则只...

    DLL的优点
    简单的说,dll有以下几个优点:

    1)      节省内存。同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中。如果使用dll,则只在内存中加载一次,所有使用该dll的进程会共享此块内存(当然,像dll中的全局变量这种东西是会被每个进程复制一份的)。

    2)      不需编译的软件系统升级,若一个软件系统使用了dll,则该dll被改变(函数名不变)时,系统升级只需要更换此dll即可,不需要重新编译整个系统。事实上,很多软件都是以这种方式升级的。例如我们经常玩的星际、魔兽等游戏也是这样进行版本升级的。

    3)      Dll库可以供多种编程语言使用,例如用c编写的dll可以在vb中调用。这一点上DLL还做得很不够,因此在dll的基础上发明了COM技术,更好的解决了一系列问题。

    最简单的dll
    开始写dll之前,你需要一个c/c++编译器和链接器,并关闭你的IDE。是的,把你的VC和C++ BUILDER之类的东东都关掉,并打开你以往只用来记电话的记事本程序。不这样做的话,你可能一辈子也不明白dll的真谛。我使用了VC自带的cl编译器和link链接器,它们一般都在vc的bin目录下。(若你没有在安装vc的时候选择注册环境变量,那么就立刻将它们的路径加入path吧)如果你还是因为离开了IDE而害怕到哭泣的话,你可以关闭这个页面并继续去看《VC++技术内幕》之类无聊的书了。

    最简单的dll并不比c的helloworld难,只要一个DllMain函数即可,包含objbase.h头文件(支持COM技术的一个头文件)。若你觉得这个头文件名字难记,那么用windows.H也可以。源代码如下:dll_nolib.cpp

    #include <objbase.h>
    
    #include <iostream.h>
    
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
    
    {
    
        HANDLE g_hModule;
    
        switch(dwReason)
    
        {
    
        case DLL_PROCESS_ATTACH:
    
           cout<<"Dll is attached!"<<endl;
    
           g_hModule = (HINSTANCE)hModule;
    
           break;
    
        case DLL_PROCESS_DETACH:
    
           cout<<"Dll is detached!"<<endl;
    
           g_hModule=NULL;
    
           break;
    
        }
    
        return true;
    
    }

    其中DllMain是每个dll的入口函数,如同c的main函数一样。DllMain带有三个参数,hModule表示本dll的实例句柄(听不懂就不理它,写过windows程序的自然懂),dwReason表示dll当前所处的状态,例如DLL_PROCESS_ATTACH表示dll刚刚被加载到一个进程中,DLL_PROCESS_DETACH表示dll刚刚从一个进程中卸载。当然还有表示加载到线程中和从线程中卸载的状态,这里省略。最后一个参数是一个保留参数(目前和dll的一些状态相关,但是很少使用)。

    从上面的程序可以看出,当dll被加载到一个进程中时,dll打印"Dll is attached!"语句;当dll从进程中卸载时,打印"Dll is detached!"语句。

    编译dll需要以下两条命令:

    cl /c dll_nolib.cpp

    这条命令会将cpp编译为obj文件,若不使用/c参数则cl还会试图继续将obj链接为exe,但是这里是一个dll,没有main函数,因此会报错。不要紧,继续使用链接命令。

    Link /dll dll_nolib.obj

    这条命令会生成dll_nolib.dll。

    注意,因为编译命令比较简单,所以本文不讨论nmake,有兴趣的可以使用nmake,或者写个bat批处理来编译链接dll。

    加载DLL(显式调用)
    使用dll大体上有两种方式,显式调用和隐式调用。这里首先介绍显式调用。编写一个客户端程序:dll_nolib_client.cpp

    #include <windows.h>
    
    #include <iostream.h>
    
    int main(void)
    
    {
    
        //加载我们的dll
    
        HINSTANCE hinst=::LoadLibrary("dll_nolib.dll");
    
        if (NULL != hinst)
    
        {
    
           cout<<"dll loaded!"<<endl;
    
        }
    
        return 0;
    
    }

    注意,调用dll使用LoadLibrary函数,它的参数就是dll的路径和名称,返回值是dll的句柄。 使用如下命令编译链接客户端:

    Cl dll_nolib_client.cpp

    并执行dll_nolib_client.exe,得到如下结果:

    Dll is attached!
    
    dll loaded!
    
    Dll is detached!

    以上结果表明dll已经被客户端加载过。但是这样仅仅能够将dll加载到内存,不能找到dll中的函数。

    使用dumpbin命令查看DLL中的函数
    Dumpbin命令可以查看一个dll中的输出函数符号名,键入如下命令:

    Dumpbin –exports dll_nolib.dll

    通过查看,发现dll_nolib.dll并没有输出任何函数。

    如何在dll中定义输出函数
    总体来说有两种方法,一种是添加一个def定义文件,在此文件中定义dll中要输出的函数;第二种是在源代码中待输出的函数前加上__declspec(dllexport)关键字。

    Def文件
    首先写一个带有输出函数的dll,源代码如下:dll_def.cpp

    #include <objbase.h>
    
    #include <iostream.h>
    
    void FuncInDll (void)
    
    {
    
        cout<<"FuncInDll is called!"<<endl;
    
    }
    
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
    
    {
    
        HANDLE g_hModule;
    
        switch(dwReason)
    
        {
    
        case DLL_PROCESS_ATTACH:
    
           g_hModule = (HINSTANCE)hModule;
    
           break;
    
        case DLL_PROCESS_DETACH:
    
            g_hModule=NULL;
    
            break;
    
        }
    
        return TRUE;
    
    }

    这个dll的def文件如下:dll_def.def

    ;
    
    ; dll_def module-definition file
    
    ;
    
    LIBRARY         dll_def.dll
    
    DESCRIPTION     '(c)2007-2009 Sachie kang'
    
    EXPORTS
    
                    FuncInDll @1 PRIVATE

    你会发现def的语法很简单,首先是LIBRARY关键字,指定dll的名字;然后一个可选的关键字DESCRIPTION,后面写上版权等信息(不写也可以);最后是EXPORTS关键字,后面写上dll中所有要输出的函数名或变量名,然后接上@以及依次编号的数字(从1到N),最后接上修饰符。

    用如下命令编译链接带有def文件的dll:

    Cl /c dll_def.cpp
    
    Link /dll dll_def.obj /def:dll_def.def

    再调用dumpbin查看生成的dll_def.dll:

    Dumpbin –exports dll_def.dll

    得到如下结果:

    Dump of file dll_def.dll
    
    File Type: DLL
    
    Section contains the following exports for dll_def.dll
    
               0 characteristics
    
        46E4EE98 time date stamp Mon Sep 10 15:13:28 2007
    
            0.00 version
    
               1 ordinal base
    
               1 number of functions
    
               1 number of names
    
        ordinal hint RVA      name
    
              1    0 00001000 FuncInDll
    
    Summary
    
            2000 .data
    
            1000 .rdata
    
            1000 .reloc
    
            6000 .text

    观察这一行

              

    1    0 00001000 FuncInDll

    会发现该dll输出了函数FuncInDll。

    显式调用DLL中的函数
    写一个dll_def.dll的客户端程序:dll_def_client.cpp

    #include <windows.h>
    
    #include <iostream.h>
    
    int main(void)
    
    {
    
        //定义一个函数指针
    
        typedef void (* DLLWITHLIB )(void);
    
        //定义一个函数指针变量
    
        DLLWITHLIB pfFuncInDll = NULL;
    
        //加载我们的dll
    
        HINSTANCE hinst=::LoadLibrary("dll_def.dll");
    
        if (NULL != hinst)
    
        {
    
           cout<<"dll loaded!"<<endl;
    
        }
    
        //找到dll的FuncInDll函数
    
        pfFuncInDll = (DLLWITHLIB)GetProcAddress(hinst, "FuncInDll");
    
        //调用dll里的函数
    
        if (NULL != pfFuncInDll)
    
        {
    
           (*pfFuncInDll)();  
    
        }
    
        return 0;
    
    }

    有两个地方值得注意,第一是函数指针的定义和使用,不懂的随便找本c++书看看;第二是GetProcAddress的使用,这个API是用来查找dll中的函数地址的,第一个参数是DLL的句柄,即LoadLibrary返回的句柄,第二个参数是dll中的函数名称,即dumpbin中输出的函数名(注意,这里的函数名称指的是编译后的函数名,不一定等于dll源代码中的函数名)。

    编译链接这个客户端程序,并执行会得到:

    dll loaded!
    
    FuncInDll is called!

    这表明客户端成功调用了dll中的函数FuncInDll。

    __declspec(dllexport)
    为每个dll写def显得很繁杂,目前def使用已经比较少了,更多的是使用__declspec(dllexport)在源代码中定义dll的输出函数。

    Dll写法同上,去掉def文件,并在每个要输出的函数前面加上声明__declspec(dllexport),例如:

    __declspec(dllexport) void FuncInDll (void)

    这里提供一个dll源程序dll_withlib.cpp,然后编译链接。链接时不需要指定/DEF:参数,直接加/DLL参数即可,

    Cl /c dll_withlib.cpp
    
    Link /dll dll_withlib.obj

    然后使用dumpbin命令查看,得到:

    1    0 00001000 ?FuncInDll@@YAXXZ

    可知编译后的函数名为?FuncInDll@@YAXXZ,而并不是FuncInDll,这是因为c++编译器基于函数重载的考虑,会更改函数名,这样使用显式调用的时候,也必须使用这个更改后的函数名,这显然给客户带来麻烦。为了避免这种现象,可以使用extern “C”指令来命令c++编译器以c编译器的方式来命名该函数。修改后的函数声明为:

    extern "C" __declspec(dllexport) void FuncInDll (void)

    dumpbin命令结果:

    1    0 00001000 FuncInDll

    这样,显式调用时只需查找函数名为FuncInDll的函数即可成功。

    extern “C”
    使用extern “C”关键字实际上相当于一个编译器的开关,它可以将c++语言的函数编译为c语言的函数名称。即保持编译后的函数符号名等于源代码中的函数名称。

    隐式调用DLL
    显式调用显得非常复杂,每次都要LoadLibrary,并且每个函数都必须使用GetProcAddress来得到函数指针,这对于大量使用dll函数的客户是一种困扰。而隐式调用能够像使用c函数库一样使用dll中的函数,非常方便快捷。

    下面是一个隐式调用的例子:dll包含两个文件dll_withlibAndH.cpp和dll_withlibAndH.h。

    代码如下:dll_withlibAndH.h

    extern "C" __declspec(dllexport) void FuncInDll (void);
    
    dll_withlibAndH.cpp
    
    #include <objbase.h>
    
    #include <iostream.h>
    
    #include "dll_withLibAndH.h"//看到没有,这就是我们增加的头文件
    
    extern "C" __declspec(dllexport) void FuncInDll (void)
    
    {
    
        cout<<"FuncInDll is called!"<<endl;
    
    }
    
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
    
    {
    
        HANDLE g_hModule;
    
        switch(dwReason)
    
        {
    
        case DLL_PROCESS_ATTACH:
    
           g_hModule = (HINSTANCE)hModule;
    
           break;
    
        case DLL_PROCESS_DETACH:
    
            g_hModule=NULL;
    
            break;
    
        }
    
        return TRUE;
    
    }

    编译链接命令:

    Cl /c dll_withlibAndH.cpp
    
    Link /dll dll_withlibAndH.obj

    在进行隐式调用的时候需要在客户端引入头文件,并在链接时指明dll对应的lib文件(dll只要有函数输出,则链接的时候会产生一个与dll同名的lib文件)位置和名称。然后如同调用api函数库中的函数一样调用dll中的函数,不需要显式的LoadLibrary和GetProcAddress。使用最为方便。客户端代码如下:dll_withlibAndH_client.cpp

    #include "dll_withLibAndH.h"
    
    //注意路径,加载 dll的另一种方法是 Project | setting | link 设置里
    
    #pragma comment(lib,"dll_withLibAndH.lib")
    
    int main(void)
    
    {
    
        FuncInDll();//只要这样我们就可以调用dll里的函数了
    
        return 0;
    
    }

    __declspec(dllexport)和__declspec(dllimport)配对使用
    上面一种隐式调用的方法很不错,但是在调用DLL中的对象和重载函数时会出现问题。因为使用extern “C”修饰了输出函数,因此重载函数肯定是会出问题的,因为它们都将被编译为同一个输出符号串(c语言是不支持重载的)。

    事实上不使用extern “C”是可行的,这时函数会被编译为c++符号串,例如(?FuncInDll@@YAXH@Z、 ?FuncInDll@@YAXXZ),当客户端也是c++时,也能正确的隐式调用。

    这时要考虑一个情况:若DLL1.CPP是源,DLL2.CPP使用了DLL1中的函数,但同时DLL2也是一个DLL,也要输出一些函数供Client.CPP使用。那么在DLL2中如何声明所有的函数,其中包含了从DLL1中引入的函数,还包括自己要输出的函数。这个时候就需要同时使用__declspec(dllexport)和__declspec(dllimport)了。前者用来修饰本dll中的输出函数,后者用来修饰从其它dll中引入的函数。

    所有的源代码包括DLL1.H,DLL1.CPP,DLL2.H,DLL2.CPP,Client.cpp。源代码可以在下载的包中找到。你可以编译链接并运行试试。

    值得关注的是DLL1和DLL2中都使用的一个编码方法,见DLL2.H

    #ifdef DLL_DLL2_EXPORTS
    
    #define DLL_DLL2_API __declspec(dllexport)
    
    #else
    
    #define DLL_DLL2_API __declspec(dllimport)
    
    #endif
    
    DLL_DLL2_API void FuncInDll2(void);
    
    DLL_DLL2_API void FuncInDll2(int);

    在头文件中以这种方式定义宏DLL_DLL2_EXPORTS和DLL_DLL2_API,可以确保DLL端的函数用__declspec(dllexport)修饰,而客户端的函数用__declspec(dllimport)修饰。当然,记得在编译dll时加上参数/D “DLL_DLL2_EXPORTS”,或者干脆就在dll的cpp文件第一行加上#define DLL_DLL2_EXPORTS。

    VC生成的代码也是这样的!!

    DLL中的全局变量和对象
    解决了重载函数的问题,那么dll中的全局变量和对象都不是问题了,只是有一点语法需要注意。如源代码所示:dll_object.h

    #ifdef DLL_OBJECT_EXPORTS
    
    #define DLL_OBJECT_API __declspec(dllexport)
    
    #else
    
    #define DLL_OBJECT_API __declspec(dllimport)
    
    #endif
    
    DLL_OBJECT_API void FuncInDll(void);
    
    extern DLL_OBJECT_API int g_nDll;
    
    class DLL_OBJECT_API CDll_Object {
    
    public:
    
        CDll_Object(void);
    
        show(void);
    
        // TODO: add your methods here.
    
    };

    Cpp文件dll_object.cpp如下:

    #define DLL_OBJECT_EXPORTS
    
    #include <objbase.h>
    
    #include <iostream.h>
    
    #include "dll_object.h"
    
    DLL_OBJECT_API void FuncInDll(void)
    
    {
    
        cout<<"FuncInDll is called!"<<endl;
    
    }
    
    DLL_OBJECT_API int g_nDll = 9;
    
    CDll_Object::CDll_Object()
    
    {
    
        cout<<"ctor of CDll_Object"<<endl;
    
    }
    
    CDll_Object::show()
    
    {
    
        cout<<"function show in class CDll_Object"<<endl;
    
    }
    
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
    
    {
    
        HANDLE g_hModule;
    
        switch(dwReason)
    
        {
    
        case DLL_PROCESS_ATTACH:
    
           g_hModule = (HINSTANCE)hModule;
    
           break;
    
        case DLL_PROCESS_DETACH:
    
            g_hModule=NULL;
    
            break;
    
        }
    
        return TRUE;
    
    }

    编译链接完后Dumpbin一下,可以看到输出了5个符号:

    1    0 00001040 ??0CDll_Object@@QAE@XZ
    
    2    1 00001000 ??4CDll_Object@@QAEAAV0@ABV0@@Z
    
    3    2 00001020 ?FuncInDll@@YAXXZ
    
    4    3 00008040 ?g_nDll@@3HA
    
    5    4 00001069 ?show@CDll_Object@@QAEHXZ

    它们分别代表类CDll_Object,类的构造函数,FuncInDll函数,全局变量g_nDll和类的成员函数show。下面是客户端代码:dll_object_client.cpp

    #include "dll_object.h"
    
    #include <iostream.h>
    
    //注意路径,加载 dll的另一种方法是 Project | setting | link 设置里
    #pragma comment(lib,"dll_object.lib")
    
    int main(void)
    
    {
    
        cout<<"call dll"<<endl;
    
        cout<<"call function in dll"<<endl;
    
        FuncInDll();//只要这样我们就可以调用dll里的函数了
    
        cout<<"global var in dll g_nDll ="<<g_nDll<<endl;
    
        cout<<"call member function of class CDll_Object in dll"<<endl;
    
        CDll_Object obj;
    
        obj.show();
    
        return 0;
    
    }

    运行这个客户端可以看到:

    call dll
    
    call function in dll
    
    FuncInDll is called!
    
    global var in dll g_nDll =9
    
    call member function of class CDll_Object in dll
    
    ctor of CDll_Object
    
    function show in class CDll_Object

    可知,在客户端成功的访问了dll中的全局变量,并创建了dll中定义的C++对象,还调用了该对象的成员函数。

    中间的小结
    牢记一点,说到底,DLL是对应C语言的动态链接技术,在输出C函数和变量时显得方便快捷;而在输出C++类、函数时需要通过各种手段,而且也并没有完美的解决方案,除非客户端也是c++。

    记住,只有COM是对应C++语言的技术。

    下面开始对各各问题一一小结。

    显式调用和隐式调用
    何时使用显式调用?何时使用隐式调用?我认为,只有一个时候使用显式调用是合理的,就是当客户端不是C/C++的时候。这时是无法隐式调用的。例如用VB调用C++写的dll。(VB我不会,所以没有例子)

    Def和__declspec(dllexport)
    其实def的功能相当于extern “C” __declspec(dllexport),所以它也仅能处理C函数,而不能处理重载函数。而__declspec(dllexport)和__declspec(dllimport)配合使用能够适应任何情况,因此__declspec(dllexport)是更为先进的方法。所以,目前普遍的看法是不使用def文件,我也同意这个看法。

    从其它语言调用DLL
    从其它编程语言中调用DLL,有两个最大的问题,第一个就是函数符号的问题,前面已经多次提过了。这里有个两难选择,若使用extern “C”,则函数名称保持不变,调用较方便,但是不支持函数重载等一系列c++功能;若不使用extern “C”,则调用前要查看编译后的符号,非常不方便。

    第二个问题就是函数调用压栈顺序的问题,即__cdecl和__stdcall的问题。__cdecl是常规的C/C++调用约定,这种调用约定下,函数调用后栈的清理工作是由调用者完成的。__stdcall是标准的调用约定,即这些函数将在返回到调用者之前将参数从栈中删除。

    这两个问题DLL都不能很好的解决,只能说凑合着用。但是在COM中,都得到了完美的解决。所以,要在Windows平台实现语言无关性,还是只有使用COM中间件。

    总而言之,除非客户端也使用C++,否则dll是不便于支持函数重载、类等c++特性的。DLL对c函数的支持很好,我想这也是为什么windows的函数库使用C加dll实现的理由之一。

    在VC中编写DLL
    在VC中创建、编译、链接dll是非常方便的,点击fileàNewàProjectàWin32 Dynamic-Link Library,输入dll名称dll_InVC然后点击确定。然后选择A DLL that export some symbols,点击Finish。即可得到一个完整的DLL。

    仔细观察其源代码,就能发现很多地方似曾相识

    显式调用就是手动调用,隐式是自动编译器调用

    class T{
    public:
        T(int x){}
    };
    
    T t=8;        // 隐式调用
    
    class E{
    pubilc:
         T& operator=( int x){}
    };
    E e;
    e=8;      //显式调用

     

    展开全文
  • 易语言32位进程调用64位DLL函数源码,32位进程调用64位DLL函数,X64Call
  • C#动态调用DLL中的函数

    千次阅读 2017-02-21 11:16:24
    [System.Runtime.InteropServices.DllImport("E:\\Project\\DLL\\XXX.dll", EntryPoint = "OpenComm", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern int
  • Qt调用dll的功能函数

    2012-12-23 20:54:36
    本资料的内容分两部分:第一部分是QT在windows上DLL的导出和调用;第二部分是QT在linux上DLL的导出和调用
  • C#和c++dll函数相互调用范例,unity3d环境下,仅代码文件,无工程
  • DLL的大部分调用都是利用.h和.lib,即使动态调用DLL也只是调用其中的导出的函数,而不是导出的类。也许很多小伙伴会说我动态调用是多此一举,但这里主要是技术交流,让大家知道这样做也是可行的。注意:类成员函数...
  • vb6调用C#Dll函数

    2013-12-23 14:45:31
    vb6调用C#Dll函数
  • 易语言源码易语言32位进程调用64位DLL函数源码.rar 易语言源码易语言32位进程调用64位DLL函数源码.rar 易语言源码易语言32位进程调用64位DLL函数源码.rar 易语言源码易语言32位进程调用64位DLL函数源码.rar ...
  • 函数名 ZtDevice_CR_ReadTrack3 出口参数 byTrackDataThree BYTE * 3 出口参数 nDataThreeLen INT * 入口参数 nTimeOut INT 返回值 short 我的声明 unction ZtDevice_CR_ReadTrack2(out ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 206,016
精华内容 82,406
关键字:

怎么调用dll里的函数