碰到一个很奇怪的问题,百思不得其解,特向高手请教开发工具:VB6
程序:二代证的读取
条件:读卡器、二代证、stdapi.dll文件、厂家的读卡程序(VB编的)、没有SDK资料
问题:在使用SDT_ReadBaseMsg读取信息时返回“Dll调用约定错误”函数原型(网络资料):int SDT_ReadBaseMsg(
int iPort,
unsigned char *pucCHMsg,
unsigned int *puiCHMsgLen,
unsigned char *pucPHMsg,
unsigned int *puiPHMsgLen,
int iIfOpen);南疯大侠的C#声明(参考:http://www.cnblogs.com/name-lh/archive/2007/04/10/324003.html):
public static extern int SDT_ReadBaseMsg(
int iPortID,
string pucCHMsg,
ref int puiCHMsgLen,
string pucPHMsg,
ref int puiPHMsgLen,
int iIfOpen);VB声明(自己翻译的):Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByRef pucCHMsg As String,
ByRef pucCHMsgLen As Long,
ByRef puvPHMsg As String,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long)问题在于不管如何改变参数类型,总是报错,我试过全改成Any,也不行,Byte和数组也不行,改成Long然后传入PtrVar()也不行,厂家的读卡程序一切正常
这是厂家程序调用的汇编代码:
:004F61A7 0B47001800 ImpAdCallI2 ;Call ptr_004F2874; check stack 0018; Push EAX
:004F61AC 706EFF FStI2 ;Pop WORD [LOCAL_0092]
:004F61AF 3C SetLastSystemError ;Kernel GetLastError
:004F61B0 6B6EFF FLdI2 ;Push WORD [LOCAL_0092]
:004F61B3 F39000 LitI2 ;Push 0090
:004F61B6 C6 EqI2 ;
:004F61B7 1CF906 BranchF ;If Pop=0 then ESI=004F66A1
:004F61BA 222400 ImpAdLdPr ;
:004F61BD 895000 MemLdI2 ;Push WORD [[SR]+0050]
:004F61C0 F400 LitI2_Byte ;Push 00
:004F61C2 C6 EqI2 ;
:004F61C3 1C4802 BranchF ;If Pop=0 then ESI=004F61F0
:004F61C6 F500000000 LitI4 ;Push 00000000
:004F61CB F508000000 LitI4 ;Push 00000008
:004F61D0 054600 ImpAdLdRf ;Push ptr
:004F61D3 5968FF PopTmpLdAdStr ;
:004F61D6 4D58FF1160 CVarRef ;
:004F61DB 0448FF FLdRfVar ;Push LOCAL_00B8请各位高手不吝赐教!
程序:二代证的读取
条件:读卡器、二代证、stdapi.dll文件、厂家的读卡程序(VB编的)、没有SDK资料
问题:在使用SDT_ReadBaseMsg读取信息时返回“Dll调用约定错误”函数原型(网络资料):int SDT_ReadBaseMsg(
int iPort,
unsigned char *pucCHMsg,
unsigned int *puiCHMsgLen,
unsigned char *pucPHMsg,
unsigned int *puiPHMsgLen,
int iIfOpen);南疯大侠的C#声明(参考:http://www.cnblogs.com/name-lh/archive/2007/04/10/324003.html):
public static extern int SDT_ReadBaseMsg(
int iPortID,
string pucCHMsg,
ref int puiCHMsgLen,
string pucPHMsg,
ref int puiPHMsgLen,
int iIfOpen);VB声明(自己翻译的):Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByRef pucCHMsg As String,
ByRef pucCHMsgLen As Long,
ByRef puvPHMsg As String,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long)问题在于不管如何改变参数类型,总是报错,我试过全改成Any,也不行,Byte和数组也不行,改成Long然后传入PtrVar()也不行,厂家的读卡程序一切正常
这是厂家程序调用的汇编代码:
:004F61A7 0B47001800 ImpAdCallI2 ;Call ptr_004F2874; check stack 0018; Push EAX
:004F61AC 706EFF FStI2 ;Pop WORD [LOCAL_0092]
:004F61AF 3C SetLastSystemError ;Kernel GetLastError
:004F61B0 6B6EFF FLdI2 ;Push WORD [LOCAL_0092]
:004F61B3 F39000 LitI2 ;Push 0090
:004F61B6 C6 EqI2 ;
:004F61B7 1CF906 BranchF ;If Pop=0 then ESI=004F66A1
:004F61BA 222400 ImpAdLdPr ;
:004F61BD 895000 MemLdI2 ;Push WORD [[SR]+0050]
:004F61C0 F400 LitI2_Byte ;Push 00
:004F61C2 C6 EqI2 ;
:004F61C3 1C4802 BranchF ;If Pop=0 then ESI=004F61F0
:004F61C6 F500000000 LitI4 ;Push 00000000
:004F61CB F508000000 LitI4 ;Push 00000008
:004F61D0 054600 ImpAdLdRf ;Push ptr
:004F61D3 5968FF PopTmpLdAdStr ;
:004F61D6 4D58FF1160 CVarRef ;
:004F61DB 0448FF FLdRfVar ;Push LOCAL_00B8请各位高手不吝赐教!
1、如果没有分配空间,最多结果不对,也不会调用错误
2、传入定长字符变量,问题依旧,即使是固定Byte数组也不行
stdapi.dll下载地址:http://files.cnblogs.com/name-lh/ICCARD.rar
测试代码:只要不出现调用错误就可以。Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" ( _
ByVal iPortID As Long, _
ByRef pucCHMsg As String, _
ByRef puiCHMsgLen As Long, _
ByRef pucPHMsg As String, _
ByRef puiPHMsgLen As Long, _
ByVal iIfOpen As Integer)Dim ifOpen As Long
Dim iPort As Long
Dim L1 As Long, L2 As LongDim b1(1023) As Byte, b2(1023) As Byte
Dim s1 As String * 1024, s2 As String * 1024Private Sub Form_Load()
iPort=1
ifOpen=1
SDT_ReadBaseMsg iPort, s1, L1, s2, L2, ifOpen
End Sub
VB 只能缺省调用 __stdcall 调用规范的函数。解决方法有二:
1 你用 VC++ 写一个过渡 DLL 文件,原样调用厂家提供的函数,不同在于你的函数统统声明__stdcall 。2 在 VB 中子类化,下面是例子:[Visual Basic]
Imports System
Imports Microsoft.VisualBasic
Imports System.Runtime.InteropServices
Public Class LibWrap
' Visual Basic does not support varargs, so all arguments must be
' explicitly defined. CallingConvention.Cdecl must be used since the stack
' is cleaned up by the caller.
' int printf( const char *format [, argument]... )
<DllImport("msvcrt.dll", CallingConvention := CallingConvention.Cdecl)> _
Overloads Shared Function printf ( _
format As String, i As Integer, d As Double) As Integer
End Function
<DllImport("msvcrt.dll", CallingConvention := CallingConvention.Cdecl)> _
Overloads Shared Function printf ( _
format As String, i As Integer, s As String) As Integer
End Function
End Class 'LibWrap
Public Class App
Public Shared Sub Main()
LibWrap.printf(ControlChars.CrLf + "Print params: %i %f", 99,
99.99)
LibWrap.printf(ControlChars.CrLf + "Print params: %i %s", 99, _
"abcd")
End Sub 'Main
End Class 'App
您的意思是该函数不是__stdcall调用吗?厂家的程序也是VB编的,经过分析,
1、应该没有使用中间库
2、从汇编代码看,使用重载的可能也不大,
3、我有怀疑库文件是不是不一样,参数定义不同,就把南疯大侠的库文件替换过去,并修改函数入口,信息可以正确读取。说明参数表是相同的。
哦,上次没有仔细看你的代码。String 必须 Byval。因为它已经内部实现了指针。函数原型(网络资料):int SDT_ReadBaseMsg(
int iPort,
unsigned char *pucCHMsg,
unsigned int *puiCHMsgLen,
unsigned char *pucPHMsg,
unsigned int *puiPHMsgLen,
int iIfOpen); Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByVal pucCHMsg As String,
ByRef pucCHMsgLen As Long,
ByVal puvPHMsg As String,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long) 或者Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByRef pucCHMsg As Byte,
ByRef pucCHMsgLen As Long,
ByRef puvPHMsg As Byte,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long) 后者调用时,Byte 型参数处采用类似 Buffer(0) 的方式。
下面是微软网站的说明:VB "Bad DLL Calling Convention" Means Stack Frame MismatchSUMMARY
When you call a dynamic link library (DLL) function from Visual Basic for Window...When you call a dynamic link library (DLL) function from Visual Basic for Windows, the "Bad DLL Calling Convention" error is often caused by incorrectly omitting or including the ByVal keyword from the Declare statement or the Call statement. The ByVal keyword affects the size of data placed on the stack. Visual Basic for Windows checks the change in the position of the stack pointer to detect this error. When Visual Basic for Windows generates the run time error "Bad DLL Calling Convention," the most common cause when calling API functions is omitting the ByVal keyword from the Declaration of the external function or from the call itself. It can also occur due to including the ByVal keyword when the function is expecting a 4 byte pointer to the parameter instead of the value itself. This changes the size (number of bytes) of the values placed on the stack, and upon return from the DLL, Visual Basic for Windows detects the change in the position of the stack frame and generates the error. MORE INFORMATION
There are two calling conventions, or inter-language protocols: the Pascal/Basic/FORTRAN calling convention, and the C calling convention. Visual Basic for Windows uses the Pascal calling convention, as do the Microsoft Window API functions and other Microsoft Basic language products. Under the Pascal convention, it is the responsibility of the called procedure to adjust or clean the stack. (In addition, parameters are pushed onto the stack in order from the leftmost parameter to the rightmost.) Because the DLL function is responsible for adjusting the stack based on the type and number of parameters it expects, Visual Basic for Windows checks the position of the stack pointer upon return from the function. If the called routine has adjusted the stack to an unexpected position, then Visual Basic for Windows generates a "Bad DLL Calling Convention" error. Visual Basic for Windows assumes a stack position discrepancy because the DLL function uses the C calling convention. With the C calling convention, the calling program is responsible for adjusting the stack immediately after the called routine returns control.
ByVal iPort As Long, _
ByVal pucCHMsg As String, _
ByRef pucCHMsgLen As Long, _
ByVal puvPHMsg As String, _
ByRef puvPHMsgLen As Long, _
ByVal iIfOpen As Long)
Private Declare Function SDT_ReadBaseMsgByByte Lib "sdtapi.dll" Alias "SDT_ReadBaseMsg" ( _
ByVal iPort As Long, _
ByRef pucCHMsg As Byte, _
ByRef pucCHMsgLen As Long, _
ByRef puvPHMsg As Byte, _
ByRef puvPHMsgLen As Long, _
ByVal iIfOpen As Long)Dim ifOpen As Long
Dim iPort As Long
Dim L1 As Long, L2 As LongDim B1(1023) As Byte, B2(1023) As Byte
Dim S1 As String * 1024, S2 As String * 1024Private Sub Main()
On Error Resume Next
iPort = 1: ifOpen = 1
Err.Clear
SDT_ReadBaseMsgByStr iPort, S1, L1, S2, L2, ifOpen
If Err Then MsgBox "ByStr:" & Err.Description, 64Err.Clear
SDT_ReadBaseMsgByByte iPort, B1(0), L1, B2(0), L2, ifOpen
If Err Then MsgBox "ByByte:" & Err.Description, 64
End Sub
前面的返回值正常,调用约定错误和前面的应该不相关的。
哦,忽略了一点儿东西。试试:Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByVal pucCHMsg As String,
ByRef pucCHMsgLen As Long,
ByVal puvPHMsg As String,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long) As Long 或者 Private Declare Function SDT_ReadBaseMsg Lib "sdtapi.dll" (
ByVal iPort As Long,
ByRef pucCHMsg As Byte,
ByRef pucCHMsgLen As Long,
ByRef puvPHMsg As Byte,
ByRef puvPHMsgLen As Long,
ByVal iIfOpen As Long) As Long返回值的类型要声明一下。
QQ:150227334