使用DLL进行不同语言之间的调用(转)

源:使用DLL进行不同语言之间的调用

__declspec(dllexport) 是告诉编译器用来导出函数的,在代码中不另作说明了。

extern "C" 的意思就是用C的方式来导出函数。为什么要用C的方式来导出呢。
因为C++中有重载,编译器会对函数名进行更改,修饰成唯一的函数名。
__stdcall 告诉编译器函数调用方式。这点可以参考其他文章,我预计也会在blog中写上一篇关于函数调用方式。

extern "C" __declspec(dllexport) int  Max(int x,int y)
{
    return x>y?x:y;
}
__declspec(dllexport) int __stdcall Min(int x,int y)
{
    return x<y?x:y;
}
__declspec(dllexport) double Min(double x,double y)
{
    return x<y?x:y;
}

这是一段代码,使用参数和返回值为int 和double 是有目的的。
在VC8下int是32位的double是64位。
使用重载也是有目的的。
编译命令如下
cl /c dlltest.cpp
link /DLL dlltest.obj
编译后使用Depends查看dll中的内容.能看到dll中有3个函数。
[email protected]@[email protected]
[email protected]@[email protected]
Max
其中的[email protected]@[email protected]和[email protected]@[email protected]就是重载两个Min函数。
可以使用运行库中未公开函数__unDNameEx看到相对应的函数声明。
这两个名字就是C++函数和二进制文件中的函数名相对应的。
重载的时候根据参数不同链接到不同的函数名字
C如果使用的话得动态加载函数

接下来看如何传递指针类型.在以下的部分都使用C可以使用函数
于是将extern "C" __declspec(dllexport)定义为一个宏

#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT int swap(int* x,int& y)
{
    int z = *x;
    *x = y ;
    y = z;
    return 0;
}
/*
这和前面的例子重复了,主要用于调用的例子
*/
DLLEXPORT double __stdcall Max_d(double x,double y)
{
    return x>y?x:y;
}

接下来是使用结构体的,由于结构体会对成员进行对齐,所以在调用的时候需要注意和这里的结构体有相同的内存布局。

#include<string.h>
struct testStruct
{
    char a;
    int b;
    double c;
    char sz[5];
};
DLLEXPORT int __stdcall UseStruct(testStruct* p)
{
    p->a = ‘a‘;
    p->b = 20;
    p->c = 1.234;
    strcpy( p->sz , "abcd" );
    return sizeof(testStruct);
}
/*这是修改了内存对齐的结构体使用,主要在调用的时候有区别*/
#pragma pack(push)
#pragma pack( 1 )
struct testStruct2
{
    char a;
    int b;
    double c;
    char sz[5];
};
#pragma pack(pop)
DLLEXPORT int __stdcall UseStruct2(testStruct2* p)
{
    p->a = ‘a‘;
    p->b = 20;
    p->c = 1.234;
    strcpy( p->sz , "abcd" );
    return sizeof(testStruct2);
}

这是使用回调函数的例子,这里想成功调用主要还是要看如何调用。

DLLEXPORT int __stdcall UserCallBackFunc( const char* lp, int (__stdcall *p)(const char*) )
{
    return p( lp );
}

def文件内容如下
EXPORTS
    Max
    swap
    Max_d
    UseStruct
    UseStruct2
    UserCallBackFunc
这里的def文件不是必须的,如果不使用def文件编译,则dll中的一些函数名前面会被加上_后面加上参数大小
[email protected]@[email protected] [email protected]@[email protected] Max [email protected] [email protected] [email protected] [email protected] swap
当然如果只是C使用的话不使用def文件也是可以的,但要是其他语言的话就有一些不方便了也得使用[email protected]
这样的函数名字,有些不美观.在这里增加def文件也就是改名的意思

编译方法如下
cl /c dlltest.cpp
link /DLL /DEF:dlltest.def dlltest.obj
使用了def文件后dll中所导出的函数如下
[email protected]@[email protected] [email protected]@[email protected] Max Max_d UseStruct2 UseStruct UserCallBackFunc swap
除去重载的两个函数,凡是使用extern "C"的函数都可以正常现实

C/C++的使用方法
分别使用.c和.cpp的扩展名编译,就是C的调用方法和C++的调用方法了。

#ifdef __cplusplus
    extern "C" __declspec(dllimport) int  Max(int x,int y);
    int __stdcall Min(int x,int y);
    double Min(double x,double y);
#else
    __declspec(dllimport) int Max(int x,int y);
#endif

#ifdef __cplusplus
#    define DLLIMPORT extern "C" __declspec(dllimport)
#else
#    define DLLIMPORT __declspec(dllimport)
#endif

DLLIMPORT int swap(int* x,int* y);
DLLIMPORT double __stdcall Max_d(double x,double y);

#include<string.h>
typedef struct __testStruct
{
    char a;
    int b;
    double c;
    char sz[5];
}testStruct;
DLLIMPORT int __stdcall UseStruct(testStruct* p);
#pragma pack(push)
#pragma pack( 1 )
typedef struct __testStruct2
{
    char a;
    int b;
    double c;
    char sz[5];
} testStruct2;
#pragma pack(pop)
DLLIMPORT int __stdcall UseStruct2(testStruct2* p);
DLLIMPORT int __stdcall UserCallBackFunc( const char* lp, int (__stdcall *p)(const char*) );

#include<stdio.h>
#pragma comment(lib,"dlltest.lib")
#include<windows.h>

int __stdcall CallBackFunc(const char*lp)
{
    return printf("%s ",lp);
}
int main()
{
    int x=2,y=3;
    testStruct s1;
    testStruct2 s2;
#ifdef __cplusplus
    printf("%d ",Min( 2,3 ) );
    printf("%f ",Min( 2.0,3.0 ) );
#else
    int(__stdcall *pMin)(int,int)=0;
    double(*pMin_d)(double,double)=0;
    HMODULE hDll = GetModuleHandle("dlltest.dll");
    pMin_d = (double(*)(double,double)) GetProcAddress( hDll , "[email protected]@[email protected]" );
    if( pMin_d )
        printf("%f ",pMin_d(3.0,5.0 ) );
    pMin = (int(__stdcall*)(int,int)) GetProcAddress( hDll , "[email protected]@[email protected]" );
    if( pMin )
        printf("%d ",pMin( 3 , 5 ) );
#endif
    swap( &x,&y );
    printf("swap = %d,%d ",x,y);
    printf( "%d " , Max( 2,4 ) );
    printf( "%f " , Max_d( 2.0,4.0 ) );
    UseStruct(&s1);
    UseStruct2( &s2 );
    printf( "%c,%d,%f,%s ",s1.a,s1.b,s1.c,s1.sz);
    printf( "%c,%d,%f,%s ",s2.a,s2.b,s2.c,s2.sz);
    UserCallBackFunc("abcdef",CallBackFunc);

    return 0;
};

Delphi的使用方法

program test;
{$APPtype CONSOLE}
uses
  SysUtils,Classes,Math,Windows;

type
  testStruct = record
    a:Char;
    b:Integer;
    c:Double;
    sz:array[0..4] of char;
  end;
{$A1}{定义record使之和 修改了对齐的结构体有相同的内存布局 }
  testStruct2 = record
    a:Char;
    b:Integer;
    c:Double;
    sz:array[0..4] of char;
  end;
{$A8}
  CallBackFunc type = function(x:PChar):Integer;stdcall;
  function Max( x:Integer;y:Integer ):Integer;cdecl;external ‘dlltest.dll‘ name ‘Max‘;
  function Max_d( x:Double;y:Double):Double    ;stdcall;external ‘dlltest.dll‘ name ‘Max_d‘;
  function swap(var x:Integer;var y:Integer):Integer;stdcall;external ‘dlltest.dll‘ name ‘swap‘ ;
  function UseStruct(var x:testStruct):Integer;stdcall;external ‘dlltest.dll‘ name ‘UseStruct‘ ;
  function UseStruct2(var x:testStruct2):Integer;stdcall;external ‘dlltest.dll‘ name ‘UseStruct2‘ ;
  function UserCallBackFunc( lp:PChar ; F:CallBackFunctype ):Integer;stdcall;external ‘dlltest.dll‘ name ‘UserCallBackFunc‘ ;
  function CallFunc(lp:PChar):Integer;stdcall;
  begin
    writeln( ‘CallFunc=‘ , lp );
    result := 0 ;
  end;
{
    这里是使用重载函数
}
  function Min(x:Integer;y:Integer):Integer;stdcall;external ‘dlltest.dll‘ name ‘[email protected]@[email protected]‘;

  function Min_d(x:Double;y:Double):Double;cdecl;external ‘dlltest.dll‘ name ‘[email protected]@[email protected]‘;

  procedure test_overland;
  begin
    writeln( ‘Min(1,2)=‘ , Min( 1,2 ) );
    writeln( ‘Min_d(1.0,2.1)=‘ , Min_d( 1.0,2.1 ) );
  end;

var
  x,y:Integer;
  a_struct:testStruct;
  b_struct:testStruct2;
begin
  writeln( ‘Max(1,2)=‘ , Max( 1,2 ) );
  writeln( ‘Max(1.0,2.0)=‘ , Max_d( 1.0,2.0 ) );
  writeln( ‘x=1,y=2‘ );
  x :=1;y :=2 ;
  swap( x, y);
  writeln( ‘swap(x,y)=‘ , x , ‘ ‘, y );

  writeln( ‘UseStruct( a_struct ) result=‘ , UseStruct( a_struct ) , ‘,sizeof=‘ , sizeof(a_struct) );
  writeln( ‘UseStruct=‘ , a_struct.a, ‘ ‘ , a_struct.b, ‘ ‘  , a_struct.c , ‘ ‘ ,a_struct.sz );

  writeln( ‘UseStruct2( b_struct ) result=‘ , UseStruct2( b_struct ) , ‘,sizeof=‘ , sizeof(b_struct) );
  writeln( ‘UseStruct2=‘ , b_struct.a, ‘ ‘ , b_struct.b, ‘ ‘  , b_struct.c , ‘ ‘ ,b_struct.sz );

  UserCallBackFunc( PChar(‘abcdef‘) , CallFunc );

  test_overland;

  readln;
end.

VB6的使用方法
由于VB6只能使用__stdcall方式的函数,所以只有部分函数能被VB6所调用。

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Long) As Long

Public Type testStruct
        a As Byte
        b As Long
        c As Double
        sz As String * 5
End Type
Public Type Temp
    sz As String * 5
End Type
Public Declare Function Max_d Lib "dlltest" (ByVal a As Double, ByVal b As Double) As Double
Public Declare Function Min Lib "dlltest" Alias "[email protected]@[email protected]" (ByVal a As Long, ByVal b As Long) As Long
Public Declare Function UseStruct Lib "dlltest" (ByRef a As testStruct) As Long
Public Declare Function UseStruct2 Lib "dlltest" (ByRef a As Any) As Long
Public Declare Function UserCallBackFunc Lib "dlltest" (ByVal s As String, ByVal f As Long) As Long

Function CallBack(ByVal lp As Long) As Long

    Dim L As Long
    L = lstrlen(lp)
    Dim s As String
    s = String$(L + 1, vbNullChar)
    CopyMemory s, lp, L
    MsgBox s & " , " & Str$(L)
    Debug.Print "CallBack", s
    CallBack = 3
End Function

Sub Main()

    Debug.Print Max_d(4, 5), Min(4, 6)

    Dim a As testStruct
    Debug.Print UseStruct(a)
    Debug.Print Chr(a.a), a.b, a.c, a.sz

    Dim buf(18) As Byte
    Debug.Print "----------------"
    Debug.Print UseStruct2(buf(0))

    Dim t As Byte
    CopyMemory t, buf(0), 1

    Dim L As Long
    CopyMemory L, buf(1), 4

    Dim d As Double
    CopyMemory d, buf(5), 8

    Dim s As Temp
    CopyMemory s, buf(13), 5
    Debug.Print Chr(t), L, d, s.sz

    Debug.Print UserCallBackFunc("_测试asdasd中文sdfasdf", AddressOf CallBack)
End Sub

VB版本需要注意的是lstrlen 的声明 参数不是String而是Long类型,这是因为如果是String的话VB会对参数进行改造,将字符串指针转化为String类型,而我这里不需要改变,就需要一个原始的Long类型的指针.所以就更改了API的函数声明.以适应我的需求。

时间: 2024-10-27 16:58:27

使用DLL进行不同语言之间的调用(转)的相关文章

c++/c语言中如何调用DLL

参考网站如下: ?http://blog.csdn.net/yusongwhu/article/details/7577461 http://www.cnblogs.com/lhbssc/archive/2012/02/08/2342853.html 具体内容如下: (1)篇? 今天在研究怎么在vc中调用动态dll的问题,看了一个下午,总算有些眉目. 首先来说说调用的原理: 调用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同

服务之间的调用为啥不直接用 HTTP 而用 RPC?

什么是 RPC?RPC原理是什么? 什么是 RPC? RPC(Remote Procedure Call)-远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.比如两个不同的服务 A.B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好. RPC 的出现就是为了解决这个问题. RPC原理是什么? 服务消费方(client)调用以本地调用方式调用服务: cl

D语言与C语言的互相调用

很多语言都可以实现与C语言的互相调用,但我觉得D语言做的更好些,D与C的兼容是二进制兼容,不是在源码上的兼容,也就是说编译手的二进制文件是可以互相链接的.废话不多说,直接上例子 一.在D语言中调用C函数 首先是准备一个C函数 // foo.c #include <stdio.h> int test() {     printf("message from c\n");     return 123; // 随便返回一个值 } $ gcc -c foo.c 生成 foo.o

Android技术10:Java与C语言之间简单数据的传递

由于C语言和Java语言之间很多类型不一致性,因此使用native时,需要数据类型转换.下面演示分别传递整型,字符串,整型数组,Java静态方法传递数据. 1.创建native方法 我们单独创建一个NativeClass类来存放native方法 1 package com.forsta.ndk; 2 3 public class NativeClass { 4 public native int add(int x,int y); 5 public native String showStrin

Delphi编写DLL(以及静态和动态方式调用)

Delphi编写DLL(以及静态和动态方式调用) 作者/cadenza7 什么是DLL? DLL是Dynamic Link Library(动态链接库)的缩写形式.DLL 是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件,动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数,函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译.链接并与使用它们的进程分开存储的函数.DLL 还有助于共享数据和资源,多个应用程序可同时访问内存中单个DLL 副本的内容

JAVA 构造方法之间的调用

this:看上去,用来区分局部变量和成员变量的情况this:就是代表本类对象,this代表它所在方法所属对象的引用构造方法之间的调用只能通过this语句来完成构造方法之间进行调用时this语句只能出现在第一行,构造方法要先执行,如果初始化当中还有初始化,那就去执行更细节的初始化 class G{ private String name; private int age = 19; G(String name){ System.out.println(name); System.out.print

Iframe父页面与子页面之间的调用

专业词语解释如下:     Iframe:iframe元素是文档中的文档.     window对象: 浏览器会在其打开一个HTML文档时创建一个对应的window对象.但是,如果一个文档定义了一个或者多个框架(即:包含一个或者多个frame或者iframe标签),浏览器就会为原始文档创建一个window对象,再为每个iframe创建额外的window对象,这些额外的window对象是原始窗口的子窗口. contentWindow: 是指指定的iframe或者iframe所在的window对象.

分布式系统的那些事儿(三) - 系统与系统之间的调用

系统与系统之间的调用通俗来讲,分为本地同一台服务器上的服务相互调用与远程服务调用,这个都可以称之为RPC通信.浅白点讲,客户访问服务器A,此时服务器要完成某个动作必须访问服务器B,服务器A与B互相通信,相互调用,A访问B的时候,A挂起,等待B的响应,B响应返回相应的数据个A,A再返回给用户,这就是一个很简单的栗子. 举个常见栗子,用户上传图片,用户上传图片首先会经过自身的服务器,然后再对图片进行处理,此时图片处理放在图片服务器中进行,那么我们只需要调用图片服务器提供的接口即可,然后等待响应返回的

异步服务之间的调用方式

本文为原作,转载请发自原文出处 http://www.cnblogs.com/jermmy/p/6752950.html,谢谢合作! 对与异步服务之间的调用方式LZ只使用了future和callback两种方式,其实异步服务之间的调用有以下几种 1.oneway oneway 是一种只管发送而不管结果的交互方式,不关心服务方的请求结果如何,属于一种单向的通知 2.future 多线程获取数据以及异步交互获取数据使用callable和future的组合是多服务中经常用到的,该接口在juc包中,ca