首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

关于VB的指针以及VB跟C/C++间的参数传递

2012-10-19 
关于VB的指针以及VB和C/C++间的参数传递首先,我们使用C/C创建一个WIN32 DLL,这样VB才可以使用C/C的代码。此

关于VB的指针以及VB和C/C++间的参数传递

首先,我们使用C/C++创建一个WIN32 DLL,这样VB才可以使用C/C++的代码。

此前我对DLL导出函数已有专文讲述,这里就不再赘述,当然为了各位能够去验证,我还是把步骤截图上来:

打开VC6,新建一个Win32 DLL工程(使用VS.NET的朋友因为有中文版MSDN我就不截图了,我也不喜欢用.NET)

关于VB的指针以及VB跟C/C++间的参数传递

选择空白的DLL就可以了,代码我们自己写。

关于VB的指针以及VB跟C/C++间的参数传递

创建头文件和代码文件,其实最重要的是#include <windows.h>,标准化吧。

关于VB的指针以及VB跟C/C++间的参数传递

创建模块定义文件(关于.DEF文件上一篇已经有说明)

关于VB的指针以及VB跟C/C++间的参数传递

头文件的代码如下:

#ifndef __HOOK_H_#define __HOOK_H_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#define WIN32_LEAN_AND_MEAN//#define EXPORT_API extern "C" __declspec(dllexport)#define EXPORT_API __declspec(dllexport)#include <windows.h>// ...typedef union _unTagPack{BYTE Byte;SHORT Integer;LONG Long;DWORD Pointer;float Single;double Double;char Text[256];}TAGPACK, *PTAGPACK;/*//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.*/#endif


程序代码如下:

#include "Hook.h"BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){switch(ul_reason_for_call ){case DLL_PROCESS_ATTACH:break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;}EXPORT_API DWORD __stdcall fnHook(DWORD dwIndex){return dwIndex;}EXPORT_API DWORD __stdcall fnVarptr(void *dwArg, DWORD dwFlag){TAGPACK Arg;switch(dwFlag){case 0:Arg.Pointer = (DWORD)dwArg;Arg.Byte = *(BYTE*)dwArg;break;case 1:Arg.Pointer = (DWORD)dwArg;Arg.Integer = *(SHORT*)dwArg;break;case 2:Arg.Pointer = (DWORD)dwArg;Arg.Long = *(LONG*)dwArg;*(LONG*)dwArg = 0x10000000;break;case 3:Arg.Pointer = (DWORD)dwArg;Arg.Single = *(float*)dwArg;break;case 4:Arg.Pointer = (DWORD)dwArg;Arg.Double = *(double*)dwArg;break;case 5:Arg.Pointer = (DWORD)dwArg;Arg.Pointer = *(DWORD*)dwArg;strcpy(Arg.Text, (const char*)dwArg);break;}//*dwArg = 0x80000001;return 0;}


模块定义文件输出这两个函数就可以了。

EXPORTSfnHookfnVarptr


那么,我们给两个函数做个断点,方便等下调试。

关于VB的指针以及VB跟C/C++间的参数传递

 

OK!现在我们编写VB的代码,并生成Project1.exe,就一个窗口:

Option ExplicitPrivate Declare Function fnHook Lib "Hook.dll" (ByVal dwIndex As Long) As LongPrivate Declare Function fnVarptr Lib "Hook.dll" (ByVal dwPtr As Long, ByVal dwFlag As Long) As LongPrivate Type T_ARG    b As Byte    i As Integer    l As Long    s As Single    d As Double    t(32) As ByteEnd TypeDim a As T_ARGPrivate Sub Form_Load()    '    Dim ret As Long, s As String    a.b = 255    a.i = 32767    a.l = &H7FFFFFFF    a.s = 12345.6789    a.d = 0.123456789    a.t(0) = Asc("D")    a.t(1) = Asc("L")    a.t(2) = Asc("L")    a.t(3) = 0    s = "Fuck You!"        Call fnHook(0)    ret = fnVarptr(VarPtr(a.b), 0)    Call fnHook(1)    ret = fnVarptr(VarPtr(a.i), 1)    Call fnHook(2)    ret = fnVarptr(VarPtr(a.l), 2)    Call fnHook(3)    ret = fnVarptr(VarPtr(a.s), 3)    Call fnHook(4)    ret = fnVarptr(VarPtr(a.d), 4)    Call fnHook(5)    ret = fnVarptr(VarPtr(a.t(0)), 5)    Call fnHook(6)    ret = fnVarptr(StrPtr(s), 5)    Call fnHook(7)End Sub

现在,我们在VC++的环境中配置好Project1.exe的路径,F5直接运行就会在这个断点停下:
关于VB的指针以及VB跟C/C++间的参数传递

你可能已经明白,这个函数只是方便我们定位VB的代码。不错,看汇编窗口的代码:

17:   EXPORT_API DWORD __stdcall fnHook(DWORD dwIndex)18:   {   return dwIndex;10001070   push        ebp10001071   mov         ebp,esp10001073   sub         esp,40h10001076   push        ebx10001077   push        esi10001078   push        edi10001079   lea         edi,[ebp-40h]1000107C   mov         ecx,10h10001081   mov         eax,0CCCCCCCCh10001086   rep stos    dword ptr [edi]10001088   mov         eax,dword ptr [ebp+8]19:   }1000108B   pop         edi1000108C   pop         esi1000108D   pop         ebx1000108E   mov         esp,ebp10001090   pop         ebp10001091   ret         4

呵呵,所以说不是必要最好别定义函数,你至少多有10条汇编指令要运行。而且,这只是在C/C++。

一旦运行到ret 4,以后的代码就是VB的代码了。

当然,我们“装”聪明一点,跳开这个函数回到主调函数,那么主调函数的代码就是VB的代码(DLL函数是VB调用的)。

比如第一次传入一个Byte的时候:

00401B43   push        000401B45   call        004018B000401B4A   mov         esi,dword ptr ds:[401014h]00401B50   call        esi00401B52   mov         edx,dword ptr [ebp-34h]00401B55   mov         edi,dword ptr ds:[40105Ch]00401B5B   push        edx00401B5C   call        edi00401B5E   push        000401B60   push        eax00401B61   call        004018F400401B66   call        esi00401B68   push        100401B6A   call        004018B0

也许你会认为,这个004018B0就是fnHook的地址,其实不是。从上面的汇编代码我们已经知道它的地址是10001070。

那么为什么不是PUSH 0,然后CALL 10001070呢?这是因为VB调用DLL是DllFunctionCall来完成的。

VB跟.NET一样,应该说.NET跟VB一样在虚拟机环境中运行,它编译后的代码很接近机器码。

这跟VB的历史有关,VB是伴随着ActiveX技术发展起来的,它的成功远超出了微软的预料,这也是.NET借鉴它的原因之一。

点到为止,回到正题。

 

我们不管此CALL 004018B0后做什么样的操作,最终都是调用fnHook然后返回,那么我们就得到了VB代码所对应的汇编代码:

00401B68   push        100401B6A   call        004018B000401B6F   call        esi00401B71   mov         eax,dword ptr [ebp-38h]00401B74   push        eax00401B75   call        edi00401B77   push        100401B79   push        eax00401B7A   call        004018F400401B7F   call        esi00401B81   push        200401B83   call        004018B000401B88   call        esi00401B8A   mov         ecx,dword ptr [ebp-3Ch]00401B8D   push        ecx00401B8E   call        edi00401B90   push        200401B92   push        eax00401B93   call        004018F400401B98   call        esi00401B9A   push        300401B9C   call        004018B000401BA1   call        esi00401BA3   mov         edx,dword ptr [ebp-40h]00401BA6   push        edx00401BA7   call        edi00401BA9   push        300401BAB   push        eax00401BAC   call        004018F400401BB1   call        esi00401BB3   push        400401BB5   call        004018B000401BBA   call        esi00401BBC   push        ebx00401BBD   call        edi00401BBF   push        400401BC1   push        eax00401BC2   call        004018F400401BC7   call        esi00401BC9   push        500401BCB   call        004018B000401BD0   call        esi00401BD2   mov         eax,dword ptr [ebp-44h]00401BD5   push        eax00401BD6   call        edi00401BD8   push        500401BDA   push        eax00401BDB   call        004018F400401BE0   call        esi00401BE2   push        600401BE4   call        004018B000401BE9   call        esi00401BEB   mov         ecx,dword ptr [ebp-1Ch]00401BEE   push        ecx00401BEF   call        dword ptr ds:[40105Ch]00401BF5   push        500401BF7   push        eax00401BF8   call        004018F400401BFD   call        esi00401BFF   push        700401C01   call        004018B000401C06   call        esi00401C08   mov         dword ptr [ebp-4],000401C0F   wait

代码中CALL esi及以后的东西是VB自己生成的指令,我们不用理会。

好,注意下第一次传递Byte时候汇编代码的红色部分,所调用的函数会变代码是这样的:

7345C195   push        esi7345C196   call        dword ptr ds:[7339122Ch]7345C19C   push        dword ptr ds:[7349EF94h]7345C1A2   mov         esi,eax7345C1A4   call        dword ptr ds:[73391278h]7345C1AA   mov         dword ptr [eax+9Ch],esi7345C1B0   pop         esi7345C1B1   ret


注意红色那行,起跳转到这里执行:

7C92FE01   mov         eax,fs:[00000018]7C92FE07   mov         eax,dword ptr [eax+34h]7C92FE0A   ret

 

第二个CALL,我们不去理会它,你也可以继续跟踪,不过现在我们已经有了答案:

就是它就是VarPtr(),我们看VB IDE中函数的提示:

关于VB的指针以及VB跟C/C++间的参数传递

Ptr As Any中的Any就好比C/C++中的void*指针,既然是这样,就不能说VB完全不支持指针,而是把它和谐了。

当然,就一此说VB支持指针,也是错的。我们无法用*ptr = value 来操作指针,也无法给指针赋值。

然而,通过VarPtr我们得到了指针的值,把它传递给C/C++,我们看下以此传递进来后C/C++处理的结果:

关于VB的指针以及VB跟C/C++间的参数传递

将Byte的值传递给共用体的Byte成员。
关于VB的指针以及VB跟C/C++间的参数传递

 

关于VB的指针以及VB跟C/C++间的参数传递

将Integer赋值给共用体成员。

关于VB的指针以及VB跟C/C++间的参数传递

 

类似的Long也是一样:

关于VB的指针以及VB跟C/C++间的参数传递

 

单精度浮点型。

关于VB的指针以及VB跟C/C++间的参数传递

这里也许你会发现,VB中的12345.6789变成了12345.7,为什么?

这跟浮点型的存储方式有关,这里就不详细讲了。不过一点是,不要把long直接用内存复制(包括指针转换),赋值给Single

反之也是一样。虽然两者使用的内存都是4个字节。

 

双精度浮点型。

关于VB的指针以及VB跟C/C++间的参数传递

赋值后:

关于VB的指针以及VB跟C/C++间的参数传递

 

字符串,直接把字节数组传递进来:

关于VB的指针以及VB跟C/C++间的参数传递

 

字符串,用StrPtr传递的结果有所不同

关于VB的指针以及VB跟C/C++间的参数传递

少尉有点功底的都知道,这是Unicode编码的字符串。我们的DLL中使用strcpy,对此我们应该使用另外一个函数:wcscpy

关于VB的指针以及VB跟C/C++间的参数传递

 

最关键的是,到这里我们已经认识到,VB所谓VarPtr只是把指针搞成一个Long,字符串就多一点玄机。

只要认识到这一点,那么在处理VB和C/C++的DLL中数据的交换,或者换个说法叫函数调用,就了然了。

你还可以再去验证,把Byval As String传递给char*或者const char*是完全OK的。

 

Doc End!

1楼prsniper昨天 15:00
我晕,html标志被转换回源码了,代码中的<SPAN style="COLOR: #ff0000">和</SAPN>包括的文字应该显示为红色,如果你做过网页设计,你会懂的。。。

热点排行