我们经常要给用户实现读卡查询。有很多种读卡器,每个厂商的接口也不同。归纳为两类,一类是感应式读卡,卡片接触上去就读出数据。一种是触发式的,程序调用读卡方法,硬件再进入读卡轮询。对应触发式的只能加按钮触发了,对于感应式的可以模拟条码枪实现扫码效果。这样任意实现回车查询的界面都能用读卡程序。
首先抽取读卡接口,任何人只要实现该接口把实现类配置到读卡程序就可以实现读卡,主程序通过配置反射得到实现类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ReadCardService
{
///<summary NoteObject="Class">
/// [功能描述:读卡接口] <para/>
/// [创建者:zlz] <para/>
/// [创建时间:2018年09月26日] <para/>
///<说明>
/// [说明:读卡接口返回1-就诊卡,2-医保卡,3-身份证]<para/>
///</说明>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///<修改记录>
/// [修改时间:本次修改时间]<para/>
/// [修改内容:本次修改内容]<para/>
///</修改记录>
///</summary>
public interface IReadCard
{
string Read(string type);
}
}
实现实例1:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ReadCardService;
using System.Runtime.InteropServices;
namespace SYYDReadCard
{
/// <summary>
/// 读卡服务
/// </summary>
public class ReadCard : IReadCard
{
/// <summary>
/// 读身份证
/// </summary>
/// <returns></returns>
[DllImport("DHCCardInfo.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string ReadIDCardNo();
/// <summary>
/// 读银行卡
/// </summary>
/// <returns></returns>
[DllImport("DHCCardInfo.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string ReadBankCardNo();
/// <summary>
/// 读居民健康卡
/// </summary>
/// <returns></returns>
[DllImport("DHCCardInfo.dll")]
[return: MarshalAs(UnmanagedType.LPTStr)]
public static extern string ReadMHCCardNo();
/// <summary>
/// 读居民健康卡
/// </summary>
/// <returns></returns>
[DllImport("DHCCardInfo.dll")]
[return: MarshalAs(UnmanagedType.LPTStr)]
public static extern string ReadMHCCard();
/// <summary>
/// 读卡
/// </summary>
/// <returns></returns>
public string Read(string type)
{
if (type == "1")
{
return "";
}
else if (type == "2")
{
string ret = ReadMHCCardNo();
string[] retArr = ret.Split('^');
if (retArr.Length > 1)
{
return type + "-" + retArr[1];
}
return ret;
}
else if (type == "3")
{
string ret = ReadIDCardNo();
string[] retArr = ret.Split('^');
if (retArr.Length > 1)
{
return type+"-"+retArr[1];
}
return ret;
}
else if (type == "4")
{
string ret = ReadBankCardNo();
string[] retArr = ret.Split('^');
if (retArr.Length > 1)
{
return type + "-" + retArr[1];
}
return ret;
}
else if(type!="")
{
return "未支持的卡类型";
}
return "";
}
}
}
实现示例2:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using ReadCardService;
namespace JLGZLGWYYReadCard
{
/// <summary>
/// 读卡
/// </summary>
public class ReadCard : IReadCard
{
[DllImport("dcrf32.dll")]
public static extern int dc_init(Int16 port, long baud); //初试化
[DllImport("dcrf32.dll")]
public static extern short dc_exit(int icdev);
[DllImport("dcrf32.dll")]
public static extern short dc_reset(int icdev, uint sec);
[DllImport("dcrf32.dll")]
public static extern short dc_request(int icdev, char _Mode, ref uint TagType);
[DllImport("dcrf32.dll")]
public static extern short dc_card(int icdev, char _Mode, ref ulong Snr);
[DllImport("dcrf32.dll")]
public static extern short dc_halt(int icdev);
[DllImport("dcrf32.dll")]
public static extern short dc_anticoll(int icdev, char _Bcnt, ref ulong IcCardNo);
[DllImport("dcrf32.dll")]
public static extern short dc_beep(int icdev, uint _Msec);
[DllImport("dcrf32.dll")]
public static extern short dc_authentication(int icdev, int _Mode, int _SecNr);
[DllImport("dcrf32.dll")]
public static extern short dc_authentication_passaddr(int icdev, int _Mode, int _Addr, byte[] nkey);
[DllImport("dcrf32.dll")]
public static extern short dc_load_key(int icdev, int mode, int secnr, [In] byte[] nkey); //密码装载到读写模块中
[DllImport("dcrf32.dll")]
public static extern short dc_load_key_hex(int icdev, int mode, int secnr, string nkey); //密码装载到读写模块中
[DllImport("dcrf32.dll")]
public static extern short dc_write(int icdev, int adr, [In] byte[] sdata); //向卡中写入数据
[DllImport("dcrf32.dll")]
public static extern short dc_write(int icdev, int adr, [In] string sdata); //向卡中写入数据
[DllImport("dcrf32.dll")]
public static extern short dc_write_hex(int icdev, int adr, [In] string sdata); //向卡中写入数据(转换为16进制)
[DllImport("dcrf32.dll")]
public static extern short dc_read(int icdev, int adr, [Out] byte[] sdata);
[DllImport("dcrf32.dll")]
public static extern short dc_read(int icdev, int adr, [MarshalAs(UnmanagedType.LPStr)] StringBuilder sdata); //从卡中读数据
[DllImport("dcrf32.dll")]
public static extern short dc_read_hex(int icdev, int adr, [MarshalAs(UnmanagedType.LPStr)] StringBuilder sdata); //从卡中读数据(转换为16进制)
[DllImport("dcrf32.dll")]
public static extern int a_hex(string oldValue, ref string newValue, Int16 len); //普通字符转换成十六进制字符
[DllImport("dcrf32.dll")]
public static extern void hex_a(ref string oldValue, ref string newValue, int len); //十六进制字符转换成普通字符
/// <summary>
/// 读卡
/// </summary>
/// <returns></returns>
public string Read(string type)
{
int _icdev = 0;
try
{
if (_icdev <= 0)
{
_icdev = dc_init(100, 115200);
if (_icdev < 0)
{
LIS.Core.Util.LogUtils.WriteDebugLog("-1^初始化失败!");
return "";
}
}
uint ss = 0;
ulong icCardNo = 0;
char tt = (char)0;
float st = dc_reset(_icdev, ss);
st = dc_card(_icdev, tt, ref icCardNo);
if (icCardNo == 0)
{
LIS.Core.Util.LogUtils.WriteDebugLog("-1^寻卡失败!");
return "";
}
//核对密码
st = dc_beep(_icdev, 10);
string retStr = "";
if (icCardNo!=0)
{
retStr = "" + icCardNo;
if(retStr.Length<12)
{
int cha = 12 - retStr.Length;
for(int i=0;i<cha;i++)
{
retStr = "0" + retStr;
}
}
}
return retStr;
}
catch (Exception ex)
{
return "-1^" + ex.Message;
}
finally
{
if (_icdev > 0)
{
dc_exit((Int16)_icdev);
}
}
return "";
}
}
}
配置实现类:
C#模拟条码枪
[DllImport("user32.dll", SetLastError = true)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
public static void PressKey(Keys key, bool up)
{
const int KEYEVENTF_EXTENDEDKEY = 0x1;
const int KEYEVENTF_KEYUP = 0x2;
if (up)
{
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
}
else
{
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
}
}
char preChar;
/// <summary>
/// 发送字符串
/// </summary>
/// <param name="str"></param>
private void SendStr(string str)
{
foreach(var c in str)
{
if(c==preChar)
{
System.Threading.Thread.Sleep(300);
}
else
{
System.Threading.Thread.Sleep(50);
}
bool isUp = false;
if(c>='A'&&c<='Z')
{
isUp = true;
}
Keys k1 = (Keys)c;
PressKey(k1, isUp);
preChar = c;
}
}
主程序活动读卡实现类轮训调用模拟扫码即可:
/// <summary>
/// 开始监听
/// </summary>
public void StartMonitor()
{
try
{
//前面日志之类的底阿妈
//获取读卡服务实现对象**********************
IReadCard service = LIS.Core.Context.ObjectContainer.GetObject<IReadCard>();
if (service != null)
{
string cardType = "";
//调用读卡
string cardNo = service.Read(cardType);
if(cardNo.StartsWith("-1^"))
{
LIS.Core.Util.LogUtils.WriteDebugLog(cardNo);
cardNo = "";
}
if (cardType != "" && cardNo!="")
{
if (AddCardType == "1")
{
cardNo = cardType + "-" + cardNo;
}
}
//判断变化**********************
if ((cardNo!="")&&(cardNo != PreCardNo || ReadModel == "1" || cardType!=""))
{
LIS.Core.Util.LogUtils.WriteDebugLog("监听读取到卡号,卡号:" + cardNo);
LogDto dt = new LogDto();
dt.Address = "读取到卡号";
dt.Time = DateTime.Now.ToString();
dt.Result = cardNo;
//窗口是打开状态才绑数据
if (this.WindowState != FormWindowState.Minimized)
{
ManagedAsyncUtilsWithReport<LogDto>.InitUtil(new BackgroundWorker());
//通知界面改变
ManagedAsyncUtilsWithReport<LogDto>.Report(dt, null);
}
//读取的卡号变化了且不为空就通知服务
if (cardNo != "")
{
//模拟条码枪********************************
if (VirtuaBarGun == "1")
{
LIS.Core.Util.LogUtils.WriteDebugLog("模拟键盘输入:" + cardNo);
SendStr(cardNo);
//SendKeys.SendWait(cardNo);
System.Threading.Thread.Sleep(200);
PressKey(Keys.Enter, false);
//SendKeys.SendWait("{ENTER}");
}
//调自助打印
else
{
LIS.Core.Util.LogUtils.WriteDebugLog("调用自助打印程序服务,确保自助程序配置开启了服务。卡号:" + cardNo);
SendCardServiceReference.PrintClient client = new SendCardServiceReference.PrintClient();
client.Print(cardNo, "");
LIS.Core.Util.LogUtils.WriteDebugLog("调用自助打印程序服务结束");
}
}
else
{
//条码枪模式,没有读到卡号也添加回车事件
if (VirtuaBarGun == "1" && AddEnterEvent == "1")
{
PressKey(Keys.Enter,false);
}
}
}
PreCardNo = cardNo;
}
else
{
LIS.Core.Util.LogUtils.WriteDebugLog("未获取到读卡接口实现。。。");
}
LIS.Core.Util.LogUtils.WriteDebugLog("监听读卡结束。。。");
//阻塞进程
System.Threading.Thread.Sleep(WaitTime);
//有数据改变为活动图标
ChangeIcon(1);
}
catch (Exception ex)
{
LIS.Core.Util.LogUtils.WriteDebugLog("监听读卡过程发生错误:"+ex.Message);
}
}
这样就可以对感应式读卡做到扫条码枪的体验。对非感应式的主程序和BS界面交互。读卡实现者只要实现读卡接口,配置实现类就可。从而稳定BS界面和读卡主程序。读卡实现人也不需要关心庞大的主体程序。