之前为了能做硬件操作学习了C++,一直停留在试验阶段。由于之前项目都是HIS做好读卡对接了,就直接C#调HIS的dll了,一直没实际用C++对接过读卡器。这次终于有了个读卡机会,还有厂商配合,哈哈。

采用C++包装读卡操作为一个读卡方法如下:

//检验读身份证包装
#include <iostream>
#include "windows.h"
using namespace std;

//读身份证对外函数
extern "C" __declspec(dllexport) const char* LISReadIDCard();

//方法定义
typedef int(__stdcall* _UCommand1)(unsigned char* pCmd, int* parg0, int* parg1, int* parg2);
typedef int(__stdcall* _GetAddr)(char* pbuf);
typedef int(__stdcall* _GetBegin)(char* pbuf);
typedef int(__stdcall* _GetName)(char* pbuf);
typedef int(__stdcall* _GetSex)(char* pbuf);
typedef int(__stdcall* _GetSexGB)(char* pbuf);
typedef int(__stdcall* _GetFolk)(char* pbuf);
typedef int(__stdcall* _GetFolkGB)(char* pbuf);
typedef int(__stdcall* _GetIDNum)(char* pbuf);
typedef int(__stdcall* _GetDep)(char* pbuf);
typedef int(__stdcall* _GetBirth)(char* pbuf);
typedef int(__stdcall* _GetEnd)(char* pbuf);
typedef int(__stdcall* _GetNewAddr)(char* pbuf);
typedef int(__stdcall* _FID_GetEnName)(char* pbuf);
typedef int(__stdcall* _FID_GetSex)(char* pbuf);
typedef int(__stdcall* _FID_GetSexGB)(char* pbuf);
typedef int(__stdcall* _FID_GetIDNum)(char* pbuf);
typedef int(__stdcall* _FID_GetNationality)(char* pbuf);
typedef int(__stdcall* _FID_GetChNationality)(char* pbuf);
typedef int(__stdcall* _FID_GetChName)(char* pbuf);
typedef int(__stdcall* _FID_GetBegin)(char* pbuf);
typedef int(__stdcall* _FID_GetEnd)(char* pbuf);
typedef int(__stdcall* _FID_GetBirth)(char* pbuf);
typedef int(__stdcall* _FID_GetVersion)(char* pbuf);
typedef int(__stdcall* _FID_GetDep)(char* pbuf);
typedef int(__stdcall* _GetSAMIDToStr)(char* id);
typedef int(__stdcall* _GID_GetName)(char* pbuf);
typedef int(__stdcall* _GID_GetSex)(char* pbuf);
typedef int(__stdcall* _GID_GetSexGB)(char* pbuf);
typedef int(__stdcall* _GID_GetBirth)(char* pbuf);
typedef int(__stdcall* _GID_GetAddr)(char* pbuf);
typedef int(__stdcall* _GID_GetIDNum)(char* pbuf);
typedef int(__stdcall* _GID_GetDep)(char* pbuf);
typedef int(__stdcall* _GID_GetBegin)(char* pbuf);
typedef int(__stdcall* _GID_GetEnd)(char* pbuf);
typedef int(__stdcall* _GID_GetPassPortId)(char* pbuf);
typedef int(__stdcall* _GID_SignCount)(char* pbuf);
typedef int(__stdcall* _GID_GetCardType)(char* pbuf);
_UCommand1				UCommand1;
_GetAddr				GetAddr;
_GetBegin				GetBegin;
_GetName				GetName;
_GetSex					GetSex;
_GetSexGB				GetSexGB;
_GetFolk				GetFolk;
_GetFolkGB				GetFolkGB;
_GetIDNum				GetIDNum;
_GetDep					GetDep;
_GetBirth				GetBirth;
_GetEnd					GetEnd;
_GetNewAddr				GetNewAddr;
_FID_GetEnName			FID_GetEnName;
_FID_GetSex				FID_GetSex;
_FID_GetSexGB			FID_GetSexGB;
_FID_GetIDNum			FID_GetIDNum;
_FID_GetNationality		FID_GetNationality;
_FID_GetChNationality	FID_GetChNationality;
_FID_GetChName			FID_GetChName;
_FID_GetBegin			FID_GetBegin;
_FID_GetEnd				FID_GetEnd;
_FID_GetBirth			FID_GetBirth;
_FID_GetVersion			FID_GetVersion;
_FID_GetDep				FID_GetDep;
_GetSAMIDToStr			GetSAMIDToStr;
_GID_GetName			GID_GetName;
_GID_GetSex				GID_GetSex;
_GID_GetSexGB			GID_GetSexGB;
_GID_GetBirth			GID_GetBirth;
_GID_GetAddr			GID_GetAddr;
_GID_GetIDNum			GID_GetIDNum;
_GID_GetDep				GID_GetDep;
_GID_GetBegin			GID_GetBegin;
_GID_GetEnd				GID_GetEnd;
_GID_GetPassPortId		GID_GetPassPortId;
_GID_SignCount			GID_SignCount;
_GID_GetCardType		GID_GetCardType;

/// <summary>
/// 字符串转宽字符串
/// </summary>
/// <param name="orig"></param>
/// <returns></returns>
LPCWSTR StringToLPCWSTR(std::string orig)
{
	size_t origsize = orig.length() + 1;
	const size_t newsize = 100;
	size_t convertedChars = 0;
	wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
	mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
	return wcstring;
}

/// <summary>
/// 初始化读卡
/// </summary>
/// <returns>返回空就成功,返回-1^失败原因</returns>
const char* InitFunc()
{
	HINSTANCE hDll = LoadLibraryEx(StringToLPCWSTR("RdCard.dll"), 0, LOAD_WITH_ALTERED_SEARCH_PATH);
	if (hDll == NULL)
	{
		return "-1^RdCard.dll加载失败";
	}
	UCommand1 = (_UCommand1)GetProcAddress(hDll, "UCommand1");
	GetAddr = (_GetAddr)GetProcAddress(hDll, "GetAddr");
	GetBegin = (_GetBegin)GetProcAddress(hDll, "GetBegin");
	GetName = (_GetName)GetProcAddress(hDll, "GetName");
	GetSex = (_GetSex)GetProcAddress(hDll, "GetSex");
	GetSexGB = (_GetSexGB)GetProcAddress(hDll, "GetSexGB");
	GetFolk = (_GetFolk)GetProcAddress(hDll, "GetFolk");
	GetFolkGB = (_GetFolkGB)GetProcAddress(hDll, "GetFolkGB");
	GetIDNum = (_GetIDNum)GetProcAddress(hDll, "GetIDNum");
	GetDep = (_GetDep)GetProcAddress(hDll, "GetDep");
	GetBirth = (_GetBirth)GetProcAddress(hDll, "GetBirth");
	GetEnd = (_GetEnd)GetProcAddress(hDll, "GetEnd");
	GetNewAddr = (_GetNewAddr)GetProcAddress(hDll, "GetNewAddr");
	FID_GetEnName = (_FID_GetEnName)GetProcAddress(hDll, "FID_GetEnName");
	FID_GetSex = (_FID_GetSex)GetProcAddress(hDll, "FID_GetSex");
	FID_GetSexGB = (_FID_GetSexGB)GetProcAddress(hDll, "FID_GetSexGB");
	FID_GetIDNum = (_FID_GetIDNum)GetProcAddress(hDll, "FID_GetIDNum");
	FID_GetNationality = (_FID_GetNationality)GetProcAddress(hDll, "FID_GetNationality");
	FID_GetChNationality = (_FID_GetChNationality)GetProcAddress(hDll, "FID_GetChNationality");
	FID_GetChName = (_FID_GetChName)GetProcAddress(hDll, "FID_GetChName");
	FID_GetBegin = (_FID_GetBegin)GetProcAddress(hDll, "FID_GetBegin");
	FID_GetEnd = (_FID_GetEnd)GetProcAddress(hDll, "FID_GetEnd");
	FID_GetBirth = (_FID_GetBirth)GetProcAddress(hDll, "FID_GetBirth");
	FID_GetVersion = (_FID_GetVersion)GetProcAddress(hDll, "FID_GetVersion");
	FID_GetDep = (_FID_GetDep)GetProcAddress(hDll, "FID_GetDep");
	GID_GetName = (_GID_GetName)GetProcAddress(hDll, "GID_GetName");
	GID_GetSex = (_GID_GetSex)GetProcAddress(hDll, "GID_GetSex");
	GID_GetSexGB = (_GID_GetSexGB)GetProcAddress(hDll, "GID_GetSexGB");
	GID_GetBirth = (_GID_GetBirth)GetProcAddress(hDll, "GID_GetBirth");
	GID_GetAddr = (_GID_GetAddr)GetProcAddress(hDll, "GID_GetAddr");
	GID_GetIDNum = (_GID_GetIDNum)GetProcAddress(hDll, "GID_GetIDNum");
	GID_GetDep = (_GID_GetDep)GetProcAddress(hDll, "GID_GetDep");
	GID_GetBegin = (_GID_GetBegin)GetProcAddress(hDll, "GID_GetBegin");
	GID_GetEnd = (_GID_GetEnd)GetProcAddress(hDll, "GID_GetEnd");
	GID_GetPassPortId = (_GID_GetPassPortId)GetProcAddress(hDll, "GID_GetPassPortId");
	GID_SignCount = (_GID_SignCount)GetProcAddress(hDll, "GID_SignCount");
	GID_GetCardType = (_GID_GetCardType)GetProcAddress(hDll, "GID_GetCardType");
	GetSAMIDToStr = (_GetSAMIDToStr)GetProcAddress(hDll, "GetSAMIDToStr");
	if (UCommand1 == NULL || GetAddr == NULL || GetBegin == NULL || GetName == NULL || GetSex == NULL || GetSexGB == NULL || GetFolk == NULL || GetFolkGB == NULL || GetIDNum == NULL || GetDep == NULL || GetBirth == NULL || GetEnd == NULL || GetNewAddr == NULL || FID_GetEnName == NULL || FID_GetSex == NULL || FID_GetSexGB == NULL || FID_GetIDNum == NULL || FID_GetNationality == NULL || FID_GetChNationality == NULL || FID_GetChName == NULL || FID_GetBegin == NULL || FID_GetEnd == NULL || FID_GetBirth == NULL || FID_GetVersion == NULL || FID_GetDep == NULL || GetSAMIDToStr == NULL || GID_GetName == NULL || GID_GetSex == NULL || GID_GetSexGB == NULL || GID_GetBirth == NULL || GID_GetAddr == NULL || GID_GetIDNum == NULL || GID_GetDep == NULL || GID_GetBegin == NULL || GID_GetEnd == NULL || GID_GetPassPortId == NULL || GID_SignCount == NULL || GID_GetCardType == NULL)
	{
		return "-1^RdCard.dll函数获取失败";
	}
	return "";
}

/// <summary>
/// 去除两边空白字符
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
char* TrimStr(char* str)
{
	char* head = str;
	while (*head == ' ')
	{
		++head;
	}

	char* end = head + strlen(head) - 1;
	while (*end == ' ')
	{
		--end;
	}
	*(end + 1) = 0;
	strcpy(str, head);
	return str;
}

/// <summary>
/// 读身份证方法
/// </summary>
/// <returns></returns>
const char* LISReadIDCard()
{
	//初始化方法
	const char* initret = InitFunc();
	if (initret != "")
	{
		return initret;
	}
	//连接设备
	unsigned char cmd = 0x41;
	int para0 = 0, para1 = 8811, para2 = 9986;
	int ret = UCommand1(&cmd, ¶0, ¶1, ¶2);
	if (ret != 62171)
	{
		return "-1^设备连接失败,请检查设备是否插好!";
	}
	//验证卡(寻卡)
	cmd = 0x43;
	ret = UCommand1(&cmd, ¶0, ¶1, ¶2);
	//读卡
	cmd = 0x49;
	ret = UCommand1(&cmd, ¶0, ¶1, ¶2);
	if (ret != 62171 && ret != 62172 && ret != 62173 && ret != 62174)
	{
		return "-1^读卡失败!";
	}
	char NameEn[250] = { 0 };
	char Name[250] = { 0 };
	char Gender[250] = { 0 };
	char Nation[250] = { 0 };
	char BirthDate[250] = { 0 };
	char Address[250] = { 0 };
	char IDNumber[250] = { 0 };
	char SignOrgan[250] = { 0 };
	char BeginTerm[250] = { 0 };
	char ValidTerm[250] = { 0 };
	char NewAddress[250] = { 0 };
	char PassPortId[250] = { 0 };
	char SignCount[250] = { 0 };
	char CardType[250] = { 0 };
	char CardVersion[250] = { 0 };
	switch (ret)
	{
		//二代证
		case 62171:
		{
			GetName(Name);
			GetSexGB(Gender);
			GetFolkGB(Nation);
			GetBirth(BirthDate);
			GetAddr(Address);
			GetIDNum(IDNumber);
			GetDep(SignOrgan);
			GetBegin(BeginTerm);
			GetEnd(ValidTerm);
			GetNewAddr(NewAddress);

			break;
		}
		//二代证带指纹
		case 62172:
		{
			GetName(Name);
			GetSexGB(Gender);
			GetFolkGB(Nation);
			GetBirth(BirthDate);
			GetAddr(Address);
			GetIDNum(IDNumber);
			GetDep(SignOrgan);
			GetBegin(BeginTerm);
			GetEnd(ValidTerm);
			GetNewAddr(NewAddress);
			break;
		}
		//外国人
		case 62173:
		{
			FID_GetEnName(NameEn);
			FID_GetChName(Name);
			FID_GetSexGB(Gender);
			if ((unsigned char)Gender[0] == 0xC4 && (unsigned char)Gender[1] == 0xD0)
			{
				strcat(Gender, " / M");
			}
			if ((unsigned char)Gender[0] == 0xC5 && (unsigned char)Gender[1] == 0xAE)
			{
				strcat(Gender, " / F");
			}
			char temp[256] = { 0 };
			FID_GetChNationality(temp);
			char temp1[256] = { 0 };
			FID_GetNationality(temp1);
			sprintf_s(Nation, "%s / %s", temp, temp1);
			FID_GetBirth(BirthDate);
			FID_GetIDNum(IDNumber);
			strcpy(SignOrgan, "公安部 / Ministry of Public Security");
			FID_GetBegin(BeginTerm);
			FID_GetEnd(ValidTerm);
			char version[32] = { 0 };
			FID_GetVersion(version);
			strcpy(CardVersion, version);
			break;
		}
		//港澳台
		case 62174:
		{
			GID_GetName(Name);
			GID_GetSexGB(Gender);
			GID_GetBirth(BirthDate);
			GID_GetAddr(Address);
			GID_GetIDNum(IDNumber);
			GID_GetDep(SignOrgan);
			GID_GetBegin(BeginTerm);
			GID_GetEnd(ValidTerm);
			GID_GetPassPortId(PassPortId);
			GID_SignCount(SignCount);
			GID_GetCardType(CardType);
			break;
		}
	}
	//关闭读卡器
	cmd = 0x42;
	UCommand1(&cmd, ¶0, ¶1, ¶2);
	//组串返回
	const char* retStr = (string(TrimStr(NameEn)) + "^" + string(TrimStr(Name)) + "^" + string(TrimStr(Gender)) + "^" + string(TrimStr(Nation)) + "^" + string(TrimStr(BirthDate)) + "^" + string(TrimStr(Address)) + "^" + string(TrimStr(IDNumber)) + "^" + string(TrimStr(SignOrgan)) + "^" + string(TrimStr(BeginTerm)) + "^" + string(TrimStr(ValidTerm)) + "^" + string(TrimStr(NewAddress)) + "^" + string(TrimStr(PassPortId)) + "^" + string(TrimStr(SignCount)) + "^" + string(TrimStr(CardType)) + "^" + string(TrimStr(CardVersion))).c_str();
	return retStr;
}

/// <summary>
/// 以exe编译时候测试的主方法
/// </summary>
/// <returns></returns>
int main()
{
	string info = LISReadIDCard();
	cout << info << endl;
	//停住
	getchar();
	return 0;
}

该项目先编译成exe控制台测试可不可以读身份证。如果控制台好使了再编译成dll供C#调用。这里碰到一个坑是我以Debug模式编译的exe到没开发环境的电脑就算安装VC2015运行时还报错。后面宏伟兄发现我是Debug编译导致的。由于之前一直用C#写,C#的Debug编译没影响,所以不知道还要这出(这里要记得以Release模式编译额)。

然后碰到的问题就是开始C++包装的方法返回string。然后在C#用String接返回值程序会崩溃。

这里给C++方法返回改为const char*(即字符串指针),然后C#用IntPtr接指针,然后从指针取字符串数据。

C#包装如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ReadCardService;


namespace HLJHYZYYReadCard
{
    

    public class ReadCard : IReadCard
    {
        /// <summary>
        /// 读身份证
        /// </summary>
        /// <returns></returns>
        [DllImport("LISReadCard.dll")]
        public static extern IntPtr LISReadIDCard();

        /// <summary>
        /// 读卡1-就诊卡,2-医保卡,3-身份证
        /// </summary>
        /// <param name="type">卡类型</param>
        /// <returns></returns>
        public string Read(string type)
        {
            IntPtr ret = LISReadIDCard();
            string str = Marshal.PtrToStringAnsi(ret);
            if (str.Substring(0, 3) == "-1^")
            {
                return "";
            }
            str = str.Replace("^","&");
            return str;
        }
    }
}

最终把接口实现配置到公共读卡程序里,让程序通过反射加载调用,程序再模拟条码枪,就把身份证读卡模拟为扫码操作了,界面只要实现回车事件处理即可。

最终刷一次就等于一次扫码

python身份证读卡器 身份证读卡器使用_c++

总算是把C++和C#结合用起来了,积累、成长、坚持