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





3.00/5 (1投票)
使用 ActiveX、COM 对象读取 KioskMsr 阅读器的磁条阅读器示例
引言
本文介绍如何使用 ActiveX 磁条阅读器组件在 Html 页面或 Asp、Aspx 页面中读取磁卡。
案例研究
此程序可用于例如您需要在银行自动化和自助服务终端的 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。
这是我的第一个版本,您可以进行改进和修改。如果该应用程序在 Internet(而非内ranet)上使用,则 cabinet 组件需要使用有效的证书进行签名(Verisign 提供证书,每年费用约为 100 美元),以便在客户端计算机上正确安装。
我还编程了 idckde 读取器来读取磁道。
感谢所有人的帮助。