碰到一个很奇怪的问题,百思不得其解,特向高手请教开发工具: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请各位高手不吝赐教!

解决方案 »

  1.   

    unsigned char对于的是Byte,虽然使用string也可以接收数据,但是需要预先分配空间。unsigned int *类似的倒是可以写太 Byref xx as Long
      

  2.   

    是传值用ByVal变元,还是传址呀用ByRef变元,你的函数参数设定有问题
      

  3.   

    To:僵哥
    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
      

  4.   


    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
       
      

  5.   

    To:of123
    您的意思是该函数不是__stdcall调用吗?厂家的程序也是VB编的,经过分析,
    1、应该没有使用中间库
    2、从汇编代码看,使用重载的可能也不大,
    3、我有怀疑库文件是不是不一样,参数定义不同,就把南疯大侠的库文件替换过去,并修改函数入口,信息可以正确读取。说明参数表是相同的。
      

  6.   


    哦,上次没有仔细看你的代码。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) 的方式。
      

  7.   


    下面是微软网站的说明: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. 
      

  8.   

    To off123:还是不行,在两台电脑上都试了,问题依旧,您能否帮我测试一下,代码如下:Private Declare Function SDT_ReadBaseMsgByStr Lib "sdtapi.dll" Alias "SDT_ReadBaseMsg" ( _
    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
      

  9.   

    用的是哪家公司的读卡器?最近也做过身份证读取的,不过硬件厂商直接提供了一个Ocx,所以很快就搞定我试了一下C#部分的是可以读到相关信息,确认一下前面几个部分是否已成功?比如SDT_OpenPort、SDT_StartFindIDCard、SDT_SelectIDCard
      

  10.   

    用的是华视的CVR-100
    前面的返回值正常,调用约定错误和前面的应该不相关的。
      

  11.   


    哦,忽略了一点儿东西。试试: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返回值的类型要声明一下。
      

  12.   

    To:of123太感谢您了,已经可以了,输出值是Unicode编码,转化一下就可以得到正确结果了,谢谢你了。
      

  13.   

    提供通用二代身份证WEB终端控件及控件的VB源代码:提供二代身份证WEB终端控件 lkySfzCard.ocx ,用于WEB开发,并提供该控件的VB源代码,有需要者请联系我。
    QQ:150227334