前言:
函数的参数是通过堆栈来处理的,32位系统一般是在EBP寄存器做增减的。长整形的偏移量为8个字节。如果汇编去访问函数参数变量则可以使用dword ptr [ebp+8]、dword ptr [ebp+16]这样的形式来访问。这里就不探讨汇编的了。
探讨:
如果要用C编写一个函数,提供给Pb的程序使用(参数为一个正的长整形数据,返回值也为一个正长整形),那么PB中如何定义和正确传递参数和返回值呢?
显然如果是一个长整形(64位)的传递则没有什么可说的,直接使用PB提供的QUAD数据类型作为传递及接收类型。但是传递正的长整形由于和带符号的长整型取值范围不同,使用QUAD变量类型则不是安全的了,可能会得到一个非正确的值。
说明:
举例:C代码
Ulong ComputePacket(Ulong a)
{
Ulong b = 0;
b = a + 147463586973328;
return b;
}
将上面这个函数生成DLL后,提供给PB调用。我们看到C原型中参数定义为一个8字节Ulong型变量,取值范围位2^64。函数运行后将在堆栈上申请一个8字节的空间用于存放参数变量。由于参数变量类型指定为Ulong型,C编译器则会按0~2^64这个数值范围运算。而PB编译器确是按-2^63~0~2^63-1这个取值范围处理。显然在数值超范围传递过程中存在溢出问题。
那么如何确保参数传递安全了?
下面PB实例说明:
//============================================================
// 不安全的函数声明
//============================================================
DECLARE FUNCTION ComputePacket LIB "TEST.dll" ALIAS
"ComputePacket@8" (BYVAL Data1 AS QUAD)
//虽然也是在堆栈申请8字节空间,并压入Data1的数据,但是由于QUAD的取值范围问题
//可能出现数据边界溢出的风险。
上面演示用PB引用函数声明,虽然也是在堆栈申请8字节,并压入Data1的数据,但是由于QUAD的取值范围问题可能出现数据边界溢出的风险。
由于C系统的ULong型位正数8字节,在PB32位系统虽然没有类型定义,但是也可以用两个DWORD字形数据替代,两个DWORD正好也是8字节,且PB编译器会按两个0~2^32来处理,正好代表了Ulong型的高位和低位值。
安全函数声明如下:
//============================================================
// 安全的函数声明
//============================================================
DECLARE FUNCTION ComputePacket LIB "TEST.dll" ALIAS
"ComputePacket@8" (BYVAL bHigh AS DWORD, BYVAL bLow AS DWORD) AS DWORD
//在函数过程堆栈顶部申请两个4字节空间,分别压入bHigh和bLow的数据, 返回值用DWORD型接收地址。
//不要用QUAD去接收值(不安全)
同理用其他兼容64位的编程系统生成的DLL,做参数传递的过程时也可以如上声明。
总结:
最后用FreeBasic做为函数提供方,编写原型返回值不要用Ulongint,而要注意PB32位的数值类型,应该传递地址提供给PB访问。
// 参数Data1堆栈申请8字节。
// 返回参数Uinteger等价于PB的Dword型,用于接收数据地址。
// 不要用Ulongint型作为返回类型
Function ComputePacketKey(ByVal Data1 As Ulongint) As Uinteger Export
Dim b As Ulongint = 0
b = Data1 + 53487329187932
Function = Cast(Uinteger, VarPtr(b))
End Function
PB引用函数如下:
// 将84563247289342代入函数用于计算
Type Uint64
low As Dword
high As Dword
End Type
Local pBuf As Uint64 Ptr
pBuf = ComputePacketKey( 84563247289342\(2^32), (84563247289342 And &HFFFFFFFF) )
// 返回值
@pBuf.high*(2^32) + @pBuf.low