二代證、很奇怪的Dll調用(附匯編代碼)


碰到一個很奇怪的問題,百思不得其解,特向高手請教

開發工具: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

請各位高手不吝賜教!

20 个解决方案

#1


不懂   UP一下  

#2


unsigned char對於的是Byte,雖然使用string也可以接收數據,但是需要預先分配空間。unsigned int *類似的倒是可以寫太 Byref xx as Long

#3


是傳值用ByVal變元,還是傳址呀用ByRef變元,你的函數參數設定有問題

#4


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 Long

Dim b1(1023) As Byte, b2(1023) As Byte
Dim s1 As String * 1024, s2 As String * 1024

Private Sub Form_Load()
iPort=1
ifOpen=1
SDT_ReadBaseMsg iPort, s1, L1, s2, L2, ifOpen
End Sub

#5



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
   

#6


To:of123
您的意思是該函數不是__stdcall調用嗎?廠家的程序也是VB編的,經過分析,
1、應該沒有使用中間庫
2、從匯編代碼看,使用重載的可能也不大,
3、我有懷疑庫文件是不是不一樣,參數定義不同,就把南瘋大俠的庫文件替換過去,並修改函數入口,信息可以正確讀取。說明參數表是相同的。

#7


老馬還沒到嗎?嗷嗷叫的老馬啊,快來救命啊。

#8



哦,上次沒有仔細看你的代碼。

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) 的方式。

#9



下面是微軟網站的說明:

VB "Bad DLL Calling Convention" Means Stack Frame Mismatch

SUMMARY
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. 

#10


路過

#11


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 Long

Dim B1(1023) As Byte, B2(1023) As Byte
Dim S1 As String * 1024, S2 As String * 1024

Private 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, 64

Err.Clear
SDT_ReadBaseMsgByByte iPort, B1(0), L1, B2(0), L2, ifOpen
If Err Then MsgBox "ByByte:" & Err.Description, 64
End Sub

#12


用的是哪家公司的讀卡器?

最近也做過身份證讀取的,不過硬件廠商直接提供了一個Ocx,所以很快就搞定

我試了一下C#部分的是可以讀到相關信息,確認一下前面幾個部分是否已成功?

比如SDT_OpenPort、SDT_StartFindIDCard、SDT_SelectIDCard

#13


用的是華視的CVR-100
前面的返回值正常,調用約定錯誤和前面的應該不相關的。

#14



哦,忽略了一點兒東西。試試:

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

返回值的類型要聲明一下。

#15


To:of123

太感謝您了,已經可以了,輸出值是Unicode編碼,轉化一下就可以得到正確結果了,謝謝你了。

#16


提供國騰二代證閱讀器、接口、二次開發例子。QQ:35499174

#17


如需要了解后者購買關於第二代身份證閱讀器的信息可以與我聯系,本人可提供包括二次開發在內的專業的技術支持。
QQ:35499174

#18


購買第二代身份證閱讀器免費提供開發接口及二次開發的使用手冊。
QQ:35499174

#19


謝謝啦 找到問題所在啦

#20


提供通用二代身份證WEB終端控件及控件的VB源代碼:

提供二代身份證WEB終端控件 lkySfzCard.ocx ,用於WEB開發,並提供該控件的VB源代碼,有需要者請聯系我。
QQ:150227334


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com