65.9K
CodeProject 正在变化。 阅读更多。
Home

用于 Web 和 Windows 应用程序的 ActiveX 磁条阅读器(带自定义事件)

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (1投票)

2005 年 2 月 25 日

CPOL

8分钟阅读

viewsIcon

150691

downloadIcon

5364

使用 ActiveX、COM 对象读取 KioskMsr 阅读器的磁条阅读器示例

Web card reader tester

引言

本文介绍如何使用 ActiveX 磁条阅读器组件在 Html 页面或 Asp、Aspx 页面中读取磁卡。

案例研究

此程序可用于例如您需要在银行自动化和自助服务终端的 Kiosk 中读取磁卡。
Kiosk
您需要创建一个能够读取客户插入卡片并进行验证,然后授予或拒绝访问网站的 Web 应用程序。为了实现目标,您需要创建两个组件。首先,您需要创建一个 activeX 程序,然后创建一个 Web 页面来测试 activeX 组件。ActiveX 组件需要能够与读卡器硬件进行交互。

要测试此程序,您需要购买一个读卡器(45 美元)。另一个选择是购买一个 Kiosk 虚拟读卡器(如上图所示)。

在我的示例中,我选择了第一个选项。在另一个示例中,我将展示如何与 Kiosk 虚拟读卡器进行交互。我用来自提供商 http://www.casauto.com.tw/ 的“USB EZ-100PU 智能卡阅读器”系列进行了测试。我从同一个网站上传了驱动程序。您还需要 API(应用程序编程接口)使用手册。型号是“Kiosk100 混合卡片阅读器,带 USB 接口”。软件是“EZ100/200/Mini PC/SC 系列智能卡阅读器,可在 Kiosk100 混合卡片阅读器中读取磁条卡数据”。

使用代码

首先,我在 VB 6.0 中创建了一个 ActiveX Dll 项目。如果您了解 Visual C++,可以创建 ATL 项目。在 Borland C++ 中也可以创建一个项目。在我的示例中,我使用了 API 声明来导入名为“WinScard.dll”和“KIOSKMSR.DLL”(包含在本文章的源代码中)的 DLL 中的函数。首先,您需要将 API 函数从 C++ 翻译成 VB,例如:

LONG SCardEstablishContext(IN DWORD dwScope,IN LPCVOID pvReserved1,IN LPCVOID pvReserved2,OUT LPSCARDCONTEXT phContext);

在 Visual Basic 中等同于:

Public Declare Function SCardEstablishContext Lib "WinScard" (ByVal dwScope As Long, ByVal pvReserved1 As Long, ByVal pvReserved2 As Long, ByRef phContext As Long) As Long


在模块中,我声明了用于使用 API 库的函数。

Public Declare Function SCardEstablishContext Lib "WinScard" (ByVal dwScope As Long, ByVal pvReserved1 As Long, ByVal pvReserved2 As Long, ByRef phContext As Long) As Long
Public Declare Function SCardReleaseContext Lib "WinScard" (ByVal hContext As Long) As Long
Public Declare Function SCardListReaders Lib "WinScard" Alias "SCardListReadersA" (ByVal hContext As Long, ByVal mszGroups As String, ByVal mszReaders As String, ByRef phContext As Long) As Long

Public Declare Function CasOpenMSR Lib "KIOSKMSR" (ByVal szReader As String, ByRef phMSR As Long) As Long
Public Declare Function CasReadMSR Lib "KIOSKMSR" (ByVal hMSR As Long, ByRef pMSR_Data As MSR_DATA, ByVal ulTimeout As Integer) As Long
Public Declare Function CasWaitForMSR Lib "KIOSKMSR" (ByVal hMSR As Long, ByRef pMSR_Data As MSR_DATA) As Long
Public Declare Function CasDisableMSR Lib "KIOSKMSR" (ByVal hMSR As Long) As Long
Public Declare Function CasCloseMSR Lib "KIOSKMSR" (ByVal hMSR As Long) As Long
Global Const SCARD_S_SUCCESS = 0
Global Const SCARD_W_WAIT_MSR = &H8010006A

' Output track
Public Type Track
    '// A buffer that receives the MSR Track data from the reader
    Buffer As String * 255
    '// The length of the data of Track received from the reader
    BufferLength As Long
End Type
Public Type MSR_DATA
    Track01 As Track
    Track02 As Track
    Track03 As Track
End Type

第一个声明使用 WinScard 库中的 WinScard 函数来列出本地计算机上安装和检测到的读卡器。WinScard 库包含在所有 Windows 版本中,您可以在 System32 目录中找到它(运行时,程序会在 system32 目录中搜索 winscard.dll)。您还需要验证“Smart Card”服务是否已启动。您可以通过从“开始菜单 -> 运行”中输入 services.msc 来验证,并在显示的“Smart Card”服务窗口中找到它。如果已停止,则启动它,我建议将启动类型更改为“自动”。

其他声明(Kioskmsr)用于读取 MSR(磁条阅读器)的磁道。而“Type structure”用于存储卡片读取的磁道。

我还使用 user32.dll 来导入 Timer 函数,用于程序中的超时选项。

Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long 
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long 
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long) 

另一个重要点是称为 LecturaTracks 的事件。

Public Event LecturaTracks(ByVal valor As Long) 

转到操作
在网页中,我使用了这个事件“CardReader_LecturaTracks”。当客户插入卡片时,我在 Visual Basic 中可以检测到它,并向网页引发 LecturaTracks 事件。此方法的第一部分是类名,第二部分是事件名。

<HTML><HEAD><TITLE>Kiosk100Msr.CAB</TITLE></HEAD> 
<BODY onload="StartReader()" onunload="TerminateReader()"> 
<SCRIPT LANGUAGE="VBScript"> 
Function CardReader_LecturaTracks(valor) 
 alert("EventCardReader_LecturaTracks") 
 if (valor = 0) then 
   alert(CardReader.Track1 + chr(10)+ CardReader.Track2 + chr(10)+ CardReader.Track3) 
 else 
   alert("MSR read fail: " + valor) 
end if
End Function 
Sub ReadTracks 
  call CardReader.LeerTracks() 
  alert("Read tracks") 
End Sub 
Sub StartReader 
  call CardReader.Inicializar() 
  alert("Started") 
End Sub 
Sub TerminateReader 
  call CardReader.Finalizar() 
  alert("Finalized") 
End Sub </SCRIPT> 
<OBJECT id="CardReader" codeBase="BPKiosk100Msr.CAB#version=1,0,0,0" classid="CLSID:EE1E2BF0-5033-44C2-82B4-0D822A257369">
</OBJECT> 
<INPUT id="Button1" type="button" value="ReadTracks" name="Button1" onclick="ReadTracks()" >
</BODY>
 </HTML> 

当 Internet Explorer 显示网页时,body 标签通过 onLoad 事件执行调用“StartReader”方法。此方法调用“CardReader.Inicializar”。

在 Visual Basic 程序中,“Inicializar”方法执行以下操作:首先验证智能卡服务是否已启动(SCardEstablishContext),如果失败则返回 -1,并显示“Establish Context Fail. Start windows service named: 'Smart card reader' and try again”。其次,该方法验证是否存在可用的读卡器(SCardListReaders),如果失败则返回 -2,并显示“SCardListReaders Fail!. Connect the MSR and try again”。如果存在一个或多个读卡器,则使用第一个读卡器(GetFirstLector)。

lResult = SCardEstablishContext(2, 0, 0, ScardContext) 
If lResult <> SCARD_S_SUCCESS Then 
  Inicializar = -1
  Exit Function 
End If 
Dim mszGroups As String 
Dim szReaderLists As String * 256 
Dim nReaderCount As Long 
nReaderCount = 255 
lResult = SCardListReaders(ScardContext, mszGroups, szReaderLists, nReaderCount) 
If lResult <> SCARD_S_SUCCESS Then 
  Inicializar = -2 
  Exit Function 
End If 
szReader = GetFirstLector(szReaderLists) 
If (szReader = "") Then 
  Inicializar = -2 
  Exit Function 
End If 

回到网页,当用户单击 ReadTrack 按钮时,将执行“ReadTracks()”方法。在 Visual Basic 的“LeerTracks”方法中执行以下操作:首先禁用读卡器(CasDisableMSR),然后关闭(CasCloseMSR)。这是为了释放卡片以供使用。然后执行 CasOpemMSR,如果此 API 方法返回值不为零,则引发 -11 并显示“Open MSR Fail!.” & vbCrLf & “Connect the MSR and try again”。如果打开成功,则尝试读取磁条。API 方法 CasReadMSR 是一个循环,向读卡器发送信号以读取磁道。只有当卡片已插入读卡器时,才返回 0,但在大多数情况下返回 SCARD_W_WAIT_MSR,因为卡片尚未插入读卡器。在这种情况下,我激活了计时器。

Public Function LeerTracks() As Long 
  Dim lResult As Long lResult = CasDisableMSR(MsrHandle) 
  lResult = CasCloseMSR(MsrHandle) 
  lResult = CasOpenMSR(szReader, MsrHandle) 
  If (lResult <> SCARD_S_SUCCESS) Then 
    LeerTracks = -11 
    Exit Function 
  End If 
  MsrData.Track01.Buffer = Space(255) 
  MsrData.Track02.Buffer = Space(255) 
  lResult = CasReadMSR(MsrHandle, MsrData, 0) 
  If (lResult <> SCARD_S_SUCCESS) Then 
     If (lResult = SCARD_W_WAIT_MSR) Then 
        contTimeout = 0 
        MsrTimer.Enabled = True 
     Else 
        LeerTracks = -20 
        Exit Function 
     End If 
  End If 
  LeerTracks = 0 
Exit Function 

End Function  

计时器函数执行以下操作:首先使用 CasWaitForMSR API 函数在读卡器中验证卡片是否处于等待状态。如果可以读取磁道,则引发“LecturaTracks”事件并通知网页该事件已处理。

Private Sub MsrTimer_Timer() 
  Dim lResult As Long 
  lResult = CasWaitForMSR(MsrHandle, MsrData) 
  If (lResult = SCARD_S_SUCCESS) Then 
    MsrTimer.Enabled = False ' disable the timer
    RaiseEvent LecturaTracks(0) 
    Exit Sub 
  **** 
  ****




  Exit Sub 
End Sub  

在网页中,我可以在 CardReader_LecturaTracks 函数中验证它。在这种情况下,当“valor”等于 0 时,表示磁条已成功读取,您可以使用 CardReader.Track1、Track2、Track3 属性显示磁道。

Function CardReader_LecturaTracks(valor) 
alert("EventCardReader_LecturaTracks") 
if (valor = 0) then 
alert(CardReader.Track1 + chr(10)+ CardReader.Track2 + chr(10)+ CardReader.Track3) 
else 
alert("MSR read fail: " + valor) 
end if
End Function  Sub 
End Sub  

 

关注点

为了实现这个目标,我首先阅读了一些关于磁条的文章,现在我对磁条上存储的数据有所了解。ANSI/ISO 标准定义了 *3* 条磁道,每条磁道用于不同的目的。这些磁道仅由它们在磁条上的位置定义,因为整个磁条在磁性上是均匀的。所有卡片都有 3 条磁道。结构如下:

*** 磁道 1 布局:***
| SS | FC | PAN | Name | FS | Additional Data | ES | LRC |
SS=起始定界符 "%"
FC=格式代码
PAN=主账户号码(最多 19 位数字)
FS=字段分隔符 "^"
Name=最多 26 个字母数字字符。
附加数据=有效期、偏移量、加密 PIN 等。
ES=结束定界符 "?"
LRC=纵向冗余校验

*** 磁道 2 布局:***
| SS | PAN | FS | Additional Data | ES | LRC |
SS=起始定界符 ";"
PAN=主账户号码(最多 19 位数字)
FS=字段分隔符 "="
附加数据=有效期、偏移量、加密 PIN 等。
ES=结束定界符 "?"
LRC=纵向冗余校验

*** 磁道 3 布局:***
与磁道 1 和 2 类似。几乎从不使用。使用许多不同的数据标准。

在图片中,第一条磁道以“%”(起始定界符)开头,后跟格式代码(在此例中为“B”),最后是“?”。 “^”是字段分隔符。

现在我测试了用 Borland C++ 编写的原始程序,并尝试了一下,它运行正常。

在研究了 ActiveX 组件之后,我发现了一些关于 Visual C++ 和 Visual Basic 的文章。我创建了一个 Visual C++ ATL 项目并在 Windows 应用程序中进行了测试,它工作正常,但在 Web 应用程序中测试时却不行。然后我创建了一个 Visual Basic ActiveX 项目,它在 Windows 和 Web 应用程序中都可以正常工作。要在 Web 应用程序中进行测试,我修改了 Internet Explorer 的设置。首先,我打开 Internet Explorer,然后进入菜单“工具”->“选项”。在对话框窗口中,我选择“安全”选项卡,然后选择本地内ranet。之后,我单击“自定义级别...”按钮。我将以下选项更改为“启用”:
  - 下载已签名的 ActiveX 控件
  - 下载未签名的 ActiveX 控件
  - 初始化并脚本未标记为安全设置的 ActiveX 控件
注意:修改 Internet 区域(而不是本地内ranet 区域)不推荐,因为它会暴露您的计算机,允许安装所有 ActiveX,并可能安装病毒 ActiveX。

在编程中,最困难的问题是使用“KIOSKMSR.DLL”中的 API 函数。您可以使用 Dependency Walker 程序检查 DLL 中公开的方法。并通过“Kiosk 100 MSR API Reference”将 API 条目翻译成 Visual Basic。

另一点是在 Visual Basic 中创建公共事件。这可以在 Internet Explorer 的网页中使用。

之后,我在 Visual Basic 中创建了 Cab 安装程序来生成 HTML 页面,并获取 classid 以使用 ActiveX 组件。

有必要将 KIOSKMSR.DLL 复制到 system32 目录,因为 Internet Explorer 在“%ProgramFiles%\Internet Explorer”目录、system32 目录中搜索此 DLL。另一个选择是将其复制到“%ProgramFiles%\Internet Explorer”。

当您打开页面时,cab 组件会在您的计算机上安装。您可以在“C:\WINDOWS\Downloaded Program Files”中查看已安装的组件。如果您没有编译程序,则可以使用以下语法手动注册 cab 组件中包含的“Kiosk100Msr.dll”:regsvr32 Kiosk100Msr.dll。

因为我为虚拟 Kiosk 编写了读取器,所以我需要全屏显示,并且我需要更改这些注册表项:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_WINDOW_RESTRICTIONS] @="" "iexplore.exe"=dword:00000000
"explorer.exe"=dword:00000001
"msimn.exe"=dword:00000001

[HKEY_CURRENT_USER\Console]
"FullScreen"=dword:00000001

最后,在 Kiosk 上,我安装了 Windows XP 的 SP2,并配置为禁用所有程序,然后使用“tweakUI PowerToy”配置“自动登录”。然后 Kiosk 就可以使用了。
 

历史

有两个演示程序。一个是用于测试读取器的 Windows 应用程序,还有一个是用于在 Web 上测试的 Kiosk100MsrTESTER.HTM。

window card reader tester

这是我的第一个版本,您可以进行改进和修改。如果该应用程序在 Internet(而非内ranet)上使用,则 cabinet 组件需要使用有效的证书进行签名(Verisign 提供证书,每年费用约为 100 美元),以便在客户端计算机上正确安装。

我还编程了 idckde 读取器来读取磁道。

感谢所有人的帮助。

 

© . All rights reserved.