这份代码,主要是根据api文档来写的,文档上提供了各个方法的使用说明,并且有一个示例代码。以及网上找到的代码。
首先,需要2个dll,sdtapi.dll,用来读取身份证上的基本信息;WltRS.dll,用来解析照片,身份证上的照片。这两个文件放在bin目录的debug中,或者release中。然后对于sdtapi.dll该动态链接库,若用的是2004年左右的,需要一个temp.lib的授权文件;但是2005年左右的,则不需要了。
然后引入动态链接库中的方法,里面的方法都是参考网上的资料和文档写出来的。新建一个名叫DllMoudle的类,里面有7个方法,通过DllImport链接库导入,导入。方法主要是SDT_OpenPort,打开端口号和 SDT_ClosePort关闭端口号,SDT_StartFindIDCard开始找卡,SDT_SelectIDCard选择卡,以及SDT_ReadBaseMsg读取基本信息,和 SDT_ReadBaseMsgToFile从文件中读取基本信息,以及GetBmp获取图片。
public class DllMoudle
{
#region "dll声明"
/// <summary>
/// 打开串口/USB。
/// </summary>
/// <param name="iPort">[in] 整数, 表示端口号。 1-16(十进制)为串口,1001-1016(十进制)为USB口,缺省的一个USB 设备端口号是 1001。</param>
/// <returns>0x90 打开端口成功;1 打开端口失败/端口号不合法</returns>
[DllImport("sdtapi.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SDT_OpenPort(int iPort);
/// <summary>
/// 关闭串口/USB。
/// </summary>
/// <param name="iPort">[in] 整数,表示端口号。</param>
/// <returns>0x90 关闭端口成功。 0x01 端口号不合法</returns>
[DllImport("sdtapi.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SDT_ClosePort(int iPort);
/// <summary>
/// 读卡
/// </summary>
/// <param name="iPort"></param>
/// <param name="pucManaInfo">证/卡芯片管理号, 4 个字节。</param>
/// <param name="iIfOpen"></param>
/// <returns>0x9f 找卡成功;0x80 找卡失败</returns>
[DllImport("sdtapi.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SDT_StartFindIDCard (int iPort, byte[] pucManaInfo,int iIfOpen);
/// <summary>
/// 选择卡
/// </summary>
/// <param name="iPort"></param>
/// <param name="pucManaMsg">卡体管理号,28 字节。</param>
/// <param name="iIfOpen"></param>
/// <returns>0x90 读成功;其他 读失败(具体含义参见返回码表</returns>
[DllImport("sdtapi.dll",CallingConvention = CallingConvention.StdCall)]
public static extern int SDT_SelectIDCard (int iPort , byte[] pucManaMsg,int iIfOpen);
/// <summary>
/// 读取基本数据
/// </summary>
/// <param name="iPort"></param>
/// <param name="pucCHMsg"></param>
/// <param name="puiCHMsgLen"></param>
/// <param name="pucPHMsg"></param>
/// <param name="puiPHMsgLen"></param>
/// <param name="iIfOpen"></param>
/// <returns></returns>
[DllImport("sdtapi.dll",CallingConvention = CallingConvention.StdCall)]
public static extern int SDT_ReadBaseMsg(int iPort, byte[] pucCHMsg, ref UInt32 puiCHMsgLen, byte[] pucPHMsg, ref UInt32 puiPHMsgLen, int iIfOpen);
/// <summary>
/// 读取图片;
/// </summary>
/// <param name="file_name">wlt文件名,后缀是.wlt</param>
/// <param name="Port"></param>
/// <returns>1,正确;0,-1,-2,。。。-6</returns>
[DllImport("WltRS.dll",CallingConvention = CallingConvention.StdCall)]
public static extern int GetBmp(String file_name, int Port);
[DllImport("sdtapi.dll")]
public static extern int SDT_ReadBaseMsgToFile(int iPortID, string fileName1, ref UInt32 puiCHMsgLen, string fileName2, ref UInt32 puiPHMsgLen, int iIfOpen);
#endregion
}
现在这些方法就可以用了。
然后建立了一个实体clsEDZ,用来存放身份证上的基本信息。包括图片,二进制的和Image的。还有由于身份证上sex和nation读取到的都是code,根据code,拿到真正的值。如sex若是1,则是男;民族若是01,则是汉族。
public class clsEDZ
{
private SortedList lstMZ = new SortedList();
private string _Name; //姓名
private string _Sex_Code; //性别代码
private string _Sex_CName; //性别
private string _IDC; //身份证号码
private string _NATION_Code; //民族代码
private string _NATION_CName; //民族
private DateTime _BIRTH; //出生日期
private string _ADDRESS; //住址
private string _REGORG; //签发机关
private DateTime _STARTDATE; //身份证有效起始日期
private DateTime _ENDDATE; //身份证有效截至日期
private string _Period_Of_Validity_Code; //有效期限代码,许多原来系统上面为了一代证考虑,常常存在这样的字段,二代证中已经没有了
private string _Period_Of_Validity_CName; //有效期限
private byte[] _PIC_Byte; //照片二进制
private Image _PIC_Image; //照片
public clsEDZ()
{
lstMZ.Add("01", "汉族");
lstMZ.Add("02", "蒙古族");
lstMZ.Add("03", "回族");
lstMZ.Add("04", "藏族");
lstMZ.Add("05", "维吾尔族");
lstMZ.Add("06", "苗族");
lstMZ.Add("07", "彝族");
lstMZ.Add("08", "壮族");
lstMZ.Add("09", "布依族");
lstMZ.Add("10", "朝鲜族");
lstMZ.Add("11", "满族");
lstMZ.Add("12", "侗族");
lstMZ.Add("13", "瑶族");
lstMZ.Add("14", "白族");
lstMZ.Add("15", "土家族");
lstMZ.Add("16", "哈尼族");
lstMZ.Add("17", "哈萨克族");
lstMZ.Add("18", "傣族");
lstMZ.Add("19", "黎族");
lstMZ.Add("20", "傈僳族");
lstMZ.Add("21", "佤族");
lstMZ.Add("22", "畲族");
lstMZ.Add("23", "高山族");
lstMZ.Add("24", "拉祜族");
lstMZ.Add("25", "水族");
lstMZ.Add("26", "东乡族");
lstMZ.Add("27", "纳西族");
lstMZ.Add("28", "景颇族");
lstMZ.Add("29", "柯尔克孜族");
lstMZ.Add("30", "土族");
lstMZ.Add("31", "达翰尔族");
lstMZ.Add("32", "仫佬族");
lstMZ.Add("33", "羌族");
lstMZ.Add("34", "布朗族");
lstMZ.Add("35", "撒拉族");
lstMZ.Add("36", "毛南族");
lstMZ.Add("37", "仡佬族");
lstMZ.Add("38", "锡伯族");
lstMZ.Add("39", "阿昌族");
lstMZ.Add("40", "普米族");
lstMZ.Add("41", "塔吉克族");
lstMZ.Add("42", "怒族");
lstMZ.Add("43", "乌孜别克族");
lstMZ.Add("44", "俄罗斯族");
lstMZ.Add("45", "鄂温克族");
lstMZ.Add("46", "德昂族");
lstMZ.Add("47", "保安族");
lstMZ.Add("48", "裕固族");
lstMZ.Add("49", "京族");
lstMZ.Add("50", "塔塔尔族");
lstMZ.Add("51", "独龙族");
lstMZ.Add("52", "鄂伦春族");
lstMZ.Add("53", "赫哲族");
lstMZ.Add("54", "门巴族");
lstMZ.Add("55", "珞巴族");
lstMZ.Add("56", "基诺族");
lstMZ.Add("57", "其它");
lstMZ.Add("98", "外国人入籍");
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
public string Sex_Code
{
get { return _Sex_Code; }
set
{
_Sex_Code = value;
switch (value)
{
case "1":
Sex_CName = "男";
break;
case "2":
Sex_CName = "女";
break;
}
}
}
public string Sex_CName
{
get { return _Sex_CName; }
set { _Sex_CName = value; }
}
public string IDC
{
get { return _IDC; }
set { _IDC = value; }
}
public string NATION_Code
{
get { return _NATION_Code; }
set
{
_NATION_Code = value;
if (lstMZ.Contains(value))
NATION_CName = lstMZ[value].ToString();
}
}
public string NATION_CName
{
get { return _NATION_CName; }
set { _NATION_CName = value; }
}
public DateTime BIRTH
{
get { return _BIRTH; }
set { _BIRTH = value; }
}
public string ADDRESS
{
get { return _ADDRESS; }
set { _ADDRESS = value; }
}
public string REGORG
{
get { return _REGORG; }
set { _REGORG = value; }
}
public DateTime STARTDATE
{
get { return _STARTDATE; }
set { _STARTDATE = value; }
}
public DateTime ENDDATE
{
get { return _ENDDATE; }
set
{
_ENDDATE = value;
if (_ENDDATE == DateTime.MaxValue)
{
_Period_Of_Validity_Code = "3";
_Period_Of_Validity_CName = "长期";
}
else
{
if (_STARTDATE != DateTime.MinValue)
{
switch (value.AddDays(1).Year - _STARTDATE.Year)
{
case 5:
_Period_Of_Validity_Code = "4";
_Period_Of_Validity_CName = "5年";
break;
case 10:
_Period_Of_Validity_Code = "1";
_Period_Of_Validity_CName = "10年";
break;
case 20:
_Period_Of_Validity_Code = "2";
_Period_Of_Validity_CName = "20年";
break;
}
}
}
}
}
public string Period_Of_Validity_Code
{
get { return _Period_Of_Validity_Code; }
set { _Period_Of_Validity_Code = value; }
}
public string Period_Of_Validity_CName
{
get { return _Period_Of_Validity_CName; }
set { _Period_Of_Validity_CName = value; }
}
public byte[] PIC_Byte
{
get { return _PIC_Byte; }
set { _PIC_Byte = value; }
}
public Image PIC_Image
{
get { return _PIC_Image; }
set { _PIC_Image = value; }
}
}
再新建一个窗体readIdCard。在页面上加上一个文本框和button按钮,按钮点击一下读一下身份证信息,文本框显示返回的身份证信息。然后还有一个picturebox放照片。
public partial class readIdCard : Form
{
//接收身份证信息的实体
private clsEDZ objEDZ = new clsEDZ();
//构造函数
public readIdCard()
{
InitializeComponent();
}
//读卡
private void button1_Click(object sender, EventArgs e)
{
usbIdCard();
}
private void usbIdCard() {
//声明变量
DialogResult dr;
int iRet = 0x90; //返回码
int iPort = 1001; //端口号
int iIfOpen = 1; //是否需要打开端口
byte[] pubManaInfo = new byte[255]; //身份证管理号信息
byte[] pubManaMsg = new byte[255]; //
byte[] pubCHMsg = new byte[512]; //文字信息
byte[] pubPHMsg = new byte[3024]; //照片信息
byte[] pubFPMsg = new byte[1024]; //指纹信息
UInt32 puiCHMsgLen = 0; //文字信息的长度
UInt32 puiPHMsgLen = 0; //照片信息的长度
//打开端口
if (iIfOpen == 0) {
iRet = DllMoudle.SDT_OpenPort(iPort);
//若打开端口不成功
if (iRet != 0x90) {
MessageBox.Show("SDT_OpenPort Error,Error Code Is: " + iRet);
//关闭端口
DllMoudle.SDT_ClosePort(iPort);
return;
}
}
//找卡
do {
//开始找卡
iRet = DllMoudle.SDT_StartFindIDCard(iPort, pubManaInfo, iIfOpen);
if(iRet == 0x9f){
//找到之后,选择卡
iRet = DllMoudle.SDT_SelectIDCard(iPort, pubManaMsg, iIfOpen);
if (iRet == 0x90) {
break;
}
}
//若找不到卡,提示
dr = MessageBox.Show("尚未找到卡,您是否想继续找卡?", "找卡提示",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
}
//一直找卡,直到取消找卡
while ( dr == DialogResult.Yes);
//读卡信息
iRet = DllMoudle.SDT_ReadBaseMsg(iPort, pubCHMsg,ref puiCHMsgLen,pubPHMsg,ref puiPHMsgLen,iIfOpen);
if (iRet != 0x90) {
//不成功,关闭端口,退出
MessageBox.Show("SDT_ReadBaseMsg Error,Error Code Is: " + iRet);
closePort(iPort, iIfOpen);
return;
}
//读卡成功
//显示结果集
textBox1.Text = System.Text.ASCIIEncoding.Unicode.GetString(pubCHMsg);
//注意,在这里,用户必须有应用程序当前目录的读写权限
//删除掉这些文件
FileInfo objFile = new FileInfo("wz.txt");
if (objFile.Exists)
{
objFile.Attributes = FileAttributes.Normal;
objFile.Delete();
}
objFile = new FileInfo("zp.bmp");
if (objFile.Exists)
{
objFile.Attributes = FileAttributes.Normal;
objFile.Delete();
}
objFile = new FileInfo("zp.wlt");
if (objFile.Exists)
{
objFile.Attributes = FileAttributes.Normal;
objFile.Delete();
}
//将基本信息写到wz.txt中,将照片信息写到zp.wlt文件中
iRet = DllMoudle.SDT_ReadBaseMsgToFile(iPort, "wz.txt", ref puiCHMsgLen, "zp.wlt", ref puiPHMsgLen, iIfOpen);
if (iRet != 144)
{
iRet = DllMoudle.SDT_ClosePort(iPort);
MessageBox.Show("读卡失败!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//下面解析照片,注意,如果在C盘根目录下没有机具厂商的授权文件Termb.Lic,照片解析将会失败
//用2表示usb口,1表示串口。将zp.wlt转换为照片zp.bmp。
bool falg = true;
if (falg)
iRet = DllMoudle.GetBmp("zp.wlt", 2);
else
iRet = DllMoudle.GetBmp("zp.wlt", 1);
switch (iRet)
{
case 0:
MessageBox.Show("调用sdtapi.dll错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case 1: //正常
break;
case -1:
MessageBox.Show("相片解码错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case -2:
MessageBox.Show("wlt文件后缀错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case -3:
MessageBox.Show("wlt文件打开错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case -4:
MessageBox.Show("wlt文件格式错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case -5:
MessageBox.Show("软件未授权!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case -6:
MessageBox.Show("设备连接错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
//如果照片解析成功,则将照片,将从wz.txt中的内容赋值给二进制的bt
iRet = DllMoudle.SDT_ClosePort(iPort);
FileInfo f = new FileInfo("wz.txt");
FileStream fs = f.OpenRead();
byte[] bt = new byte[fs.Length];
fs.Read(bt, 0, (int)fs.Length);
fs.Close();
//然后根据位数分割,获取到相应的值,前30为是姓名,31-32是性别的编码。等等依次类推
= System.Text.UnicodeEncoding.Unicode.GetString(bt, 0, 30).Trim();
objEDZ.Sex_Code = System.Text.UnicodeEncoding.Unicode.GetString(bt, 30, 2).Trim();
objEDZ.NATION_Code = System.Text.UnicodeEncoding.Unicode.GetString(bt, 32, 4).Trim();
string strBird = System.Text.UnicodeEncoding.Unicode.GetString(bt, 36, 16).Trim();
objEDZ.BIRTH = Convert.ToDateTime(strBird.Substring(0, 4) + "年" + strBird.Substring(4, 2) + "月" + strBird.Substring(6) + "日");
objEDZ.ADDRESS = System.Text.UnicodeEncoding.Unicode.GetString(bt, 52, 70).Trim();
objEDZ.IDC = System.Text.UnicodeEncoding.Unicode.GetString(bt, 122, 36).Trim();
objEDZ.REGORG = System.Text.UnicodeEncoding.Unicode.GetString(bt, 158, 30).Trim();
string strTem = System.Text.UnicodeEncoding.Unicode.GetString(bt, 188, bt.GetLength(0) - 188).Trim();
objEDZ.STARTDATE = Convert.ToDateTime(strTem.Substring(0, 4) + "年" + strTem.Substring(4, 2) + "月" + strTem.Substring(6, 2) + "日");
strTem = strTem.Substring(8);
if (strTem.Trim() != "长期")
{
objEDZ.ENDDATE = Convert.ToDateTime(strTem.Substring(0, 4) + "年" + strTem.Substring(4, 2) + "月" + strTem.Substring(6, 2) + "日");
}
else
{
objEDZ.ENDDATE = DateTime.MaxValue;
}
//找到zp.bmp照片
objFile = new FileInfo("zp.bmp");
//若找存在,则将照片通过文件流中督考imgbyte的二进制中,然后用Memorystream流中创建图片Image,
//放到身份证的Image照片上;而imgbyte则放到照片的二进制中。最后将图片和memorystream都摧毁。
if (objFile.Exists)
{
FileStream fss = new FileStream("zp.bmp", FileMode.Open);
byte[] imgbyte = new byte[(int)objFile.Length];
fss.Read(imgbyte, 0, (int)objFile.Length);
fss.Close();
MemoryStream ms = new MemoryStream(imgbyte);
Image img = Image.FromStream(ms);
objEDZ.PIC_Image = (Image)img.Clone();
objEDZ.PIC_Byte = imgbyte;
img.Dispose();
ms.Dispose();
}
//最后界面显示值和照片
textBox1.Text = textBox1.Text + +objEDZ.Sex_Code + objEDZ.NATION_Code + objEDZ.BIRTH + objEDZ.ADDRESS + objEDZ.IDC + objEDZ.REGORG + objEDZ.STARTDATE + objEDZ.ENDDATE;
pictureBox1.Image = objEDZ.PIC_Image;
}
/// <summary>
/// 关闭端口
/// </summary>
/// <param name="iPort">端口号</param>
/// <param name="iIfOpen">是否开启</param>
private void closePort(int iPort,int iIfOpen) {
if (iIfOpen == 0)
{
DllMoudle.SDT_ClosePort(iPort);
}
}
}
以上就是读取身份证的简单版的代码,基本核心就是这些。其中由于我知道我们用的直接是usb口,所以iPort直接写的是1001,如果不知道的情况下,就需要做循环,将1-16和1001-1016都循环个遍,才行。并且对于iIfOpen是否自动开启端口,我没体会出这个的作用。