源:使用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个函数。
?Min@@YANNN@Z
?Min@@YGHHH@Z
Max
其中的?Min@@YANNN@Z和?Min@@YGHHH@Z就是重载两个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 ;
}
/*
这和前面的例子重复了,主要用于调用的例子
*/
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[];
};
DLLEXPORT int __stdcall UseStruct(testStruct* p)
{
p->a = 'a';
p->b = ;
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[];
};
#pragma pack(pop)
DLLEXPORT int __stdcall UseStruct2(testStruct2* p)
{
p->a = 'a';
p->b = ;
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中的一些函数名前面会被加上_后面加上参数大小
?Min@@YANNN@Z ?Min@@YGHHH@Z Max _Max_d@16 _UseStruct2@4 _UseStruct@4 _UserCallBackFunc@8 swap
当然如果只是C使用的话不使用def文件也是可以的,但要是其他语言的话就有一些不方便了也得使用_Max_d@16
这样的函数名字,有些不美观.在这里增加def文件也就是改名的意思

编译方法如下
cl /c dlltest.cpp
link /DLL /DEF:dlltest.def dlltest.obj
使用了def文件后dll中所导出的函数如下
?Min@@YANNN@Z ?Min@@YGHHH@Z 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[];
}testStruct;
DLLIMPORT int __stdcall UseStruct(testStruct* p);
#pragma pack(push)
#pragma pack( 1 )
typedef struct __testStruct2
{
char a;
int b;
double c;
char sz[];
} 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=,y=;
testStruct s1;
testStruct2 s2;
#ifdef __cplusplus
printf("%d ",Min( , ) );
printf("%f ",Min( 2.0,3.0 ) );
#else
int(__stdcall *pMin)(int,int)=;
double(*pMin_d)(double,double)=;
HMODULE hDll = GetModuleHandle("dlltest.dll");
pMin_d = (double(*)(double,double)) GetProcAddress( hDll , "?Min@@YANNN@Z" );
if( pMin_d )
printf("%f ",pMin_d(3.0,5.0 ) );
pMin = (int(__stdcall*)(int,int)) GetProcAddress( hDll , "?Min@@YGHHH@Z" );
if( pMin )
printf("%d ",pMin( , ) );
#endif
swap( &x,&y );
printf("swap = %d,%d ",x,y);
printf( "%d " , Max( , ) );
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 ;
};

Delphi的使用方法

program test;
{$APPtype CONSOLE}
uses
SysUtils,Classes,Math,Windows; type
testStruct = record
a:Char;
b:Integer;
c:Double;
sz:array[..] of char;
end;
{$A1}{定义record使之和 修改了对齐的结构体有相同的内存布局 }
testStruct2 = record
a:Char;
b:Integer;
c:Double;
sz:array[..] 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 := ;
end;
{
这里是使用重载函数
}
function Min(x:Integer;y:Integer):Integer;stdcall;external 'dlltest.dll' name '?Min@@YGHHH@Z'; function Min_d(x:Double;y:Double):Double;cdecl;external 'dlltest.dll' name '?Min@@YANNN@Z'; procedure test_overland;
begin
writeln( 'Min(1,2)=' , Min( , ) );
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( , ) );
writeln( 'Max(1.0,2.0)=' , Max_d( 1.0,2.0 ) );
writeln( 'x=1,y=2' );
x :=;y := ;
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 *
End Type
Public Type Temp
sz As String *
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 "?Min@@YGHHH@Z" (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 + , vbNullChar)
CopyMemory s, lp, L
MsgBox s & " , " & Str$(L)
Debug.Print "CallBack", s
CallBack =
End Function Sub Main() Debug.Print Max_d(, ), Min(, ) Dim a As testStruct
Debug.Print UseStruct(a)
Debug.Print Chr(a.a), a.b, a.c, a.sz Dim buf() As Byte
Debug.Print "----------------"
Debug.Print UseStruct2(buf()) Dim t As Byte
CopyMemory t, buf(), Dim L As Long
CopyMemory L, buf(), Dim d As Double
CopyMemory d, buf(), Dim s As Temp
CopyMemory s, buf(),
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的函数声明.以适应我的需求。

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

  1. C++调用DLL有两种方法——静态调用和动态调用

    C++调用DLL有两种方法——静态调用和动态调用 标签: dllc++winapinullc 2011-09-09 09:49 11609人阅读 评论(0) 收藏 举报  分类: cpp(30)  [ ...

  2. openssl在多平台和多语言之间进行RSA加解密注意事项

    首先说一下平台和语言: 系统平台为CentOS6.3,RSA加解密时使用NOPADDING进行填充 1)使用C/C++调用系统自带的openssl 2)Android4.2模拟器,第三方openssl ...

  3. python基础-初识Python和不同语言之间的区别

    一.Python的创始人谁? Python之父:吉多·范罗苏姆GuidovanRossum 吉多·范罗苏姆是一名荷兰计算机程序员,他作为Python程序设计语言的作者而为人们熟知.在Python社区, ...

  4. 33.服务之间的调用之RPC、Restful深入理解

    33.服务之间的调用之RPC.Restful深入理解 2018年05月08日 01:52:42 郑学炜 阅读数 13577更多 分类专栏: 6.框架   版权声明:本文为博主原创文章,遵循CC 4.0 ...

  5. python调用另一个文件中的代码,pycharm环境下:同文件夹下文件(.py)之间的调用,出现红线问题

    如何调用另一个python文件中的代码无论我们选择用何种语言进行程序设计时,都不可能只有一个文件(除了“hello world”),通常情况下,我们都需要在一个文件中调用另外一个文件的函数呀数据等等, ...

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

    什么是 RPC?RPC原理是什么? 什么是 RPC? RPC(Remote Procedure Call)-远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.比 ...

  7. Java特性之多态父类与子类之间的调用

    问题描述: Java三大特性,封装.继承.多态,一直没搞懂其中多态是什么,最近研究了一下,关于父类和子类之间的调用.下面是一个测试类,源代码如下: package com.test; public c ...

  8. html iframe 元素之间的调用

    html iframe 元素之间的调用一.简介 一般需要引入一个独立页面的时候,我们会使用iframe.在业务需要的时候,我们需要在父页面与iframe页面之间进行交互.交互的时候,我们就需要使 用到 ...

  9. 两个APP之间怎么调用《IT蓝豹》

    两个app之间怎么调用?   (1):通过显示Intent 启动    首先:配置好B app 的action,即AndroidManifest.xml中声明 <intent-filter> ...

随机推荐

  1. ngnix配置文件

    使用nginx最大的好处就是负载均衡. #设定负载均衡的服务器列表    upstream service{        server 10.4.29.174:7477;    } server { ...

  2. 444A/CF

    题目链接[http://codeforces.com/problemset/problem/444/A] 题意:给出一个无向图,找出一个联通子图,定义密度#=v(顶点值的和)/e(边值的和). 条件: ...

  3. HBase性能优化方法总结(一):表的设计

    本文主要是从HBase应用程序设计与开发的角度,总结几种常用的性能优化方法.有关HBase系统配置级别的优化,可参考:淘宝Ken Wu同学的博客. 下面是本文总结的第一部分内容:表的设计相关的优化方法 ...

  4. 本地php 连接 MySQL

    1. 在D:\xampp\htdocs下创建test.php <?php $dbhost = 'localhost:3306'; //mysql服务器主机地址 $dbuser = 'root'; ...

  5. .bat批处理命令的介绍

    HUC = = D组 http://www.cnhonkerarmy.com/ 63707869 =====================================开始============ ...

  6. hdu_1007_Quoit Design(最近点对)

    题目连接:hdu_1007_Quoit Design 题意: 给你平面上的一些点,让你找出这些点的最近点对的距离 题解: 采用分治,达到O(nlognlogn)的时间复杂度就能艹过去了 #includ ...

  7. MusiCode 批量下载指定歌手的所有专辑(已解除验证码限制)

    一直想把喜欢的歌手的专辑全都归类并下载下来,由于那专辑数量实在太多了,再加上最近开始学习python,就想着何不用python写个脚本把下载过程自动化呢?所以就花了点时间写了这么个东西,分享给有需要的 ...

  8. 4-20ma电流信号转0-5v()

    源:4-20ma电流信号转0-5v 电压转电流电路原理图(0-5V转4-20mA) 4mA-20mA转0-5v电路问题,LM324一直输出10V

  9. oracle sql 分页

    Oracle实现分页时,需要引入一个rownum的函数,rownum可以给记录一个类似于id的字段. 以下收整理了常用的几种sql分页算法,数据库以Oracle中emp为例.查询结果如下: SQL&g ...

  10. 原创:LoadTest系列之Insert Condition

    当脚本中的部分内容需要满足某些条件后才执行时,则可以使用Insert Condition,例如有如下操作: 操作1:登录 操作2:添加一条数据: 在这两个操作中,只有操作1成功后,操作2才有意义,这时 ...