HuaShiIdCardBusiness.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. using Newtonsoft.Json.Linq;
  2. using System;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. namespace ThCardReader
  7. {
  8. /// <summary>
  9. /// 华视电子身份证读卡器业务类
  10. /// 独立于现有的SSCard.dll和NationECCode.dll
  11. /// </summary>
  12. class HuaShiIdCardBusiness
  13. {
  14. #region 华视SDK P/Invoke声明
  15. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  16. private extern static int CVR_InitComm(int Port);
  17. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  18. private extern static int CVR_Authenticate();
  19. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  20. private extern static int CVR_AuthenticateForNoJudge();
  21. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  22. private extern static int CVR_Read_Content(int active);
  23. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  24. private extern static int CVR_Read_FPContent();
  25. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  26. private extern static int CVR_CloseComm();
  27. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  28. private extern static int CVR_GetStatus();
  29. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  30. private extern static int GetPeopleName(StringBuilder strTmp, ref int strLen);
  31. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  32. private extern static int GetPeopleSex(StringBuilder strTmp, ref int strLen);
  33. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  34. private extern static int GetPeopleNation(StringBuilder strTmp, ref int strLen);
  35. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  36. private extern static int GetPeopleBirthday(StringBuilder strTmp, ref int strLen);
  37. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  38. private extern static int GetPeopleIDCode(StringBuilder strTmp, ref int strLen);
  39. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  40. private extern static int GetPeopleAddress(StringBuilder strTmp, ref int strLen);
  41. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  42. private extern static int GetDepartment(StringBuilder strTmp, ref int strLen);
  43. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  44. private extern static int GetStartDate(StringBuilder strTmp, ref int strLen);
  45. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  46. private extern static int GetEndDate(StringBuilder strTmp, ref int strLen);
  47. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  48. private extern static int GetBMPData(byte[] pData, ref int pLen);
  49. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  50. private extern static int GetJpgData(byte[] jpgData, ref int pLen);
  51. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  52. private extern static int GetSexCode(byte[] sexData, ref int pLen);
  53. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  54. private extern static int GetNationCode(byte[] nationData, ref int pLen);
  55. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  56. private extern static int GetCertType(byte[] certData, ref int pLen);
  57. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  58. private extern static int CVR_GetSAMID(StringBuilder SAMID);
  59. // 新增:获取照片BASE64编码的函数
  60. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  61. private extern static int Getbase64BMPData(byte[] pData, ref int pLen);
  62. [DllImport("Termb.dll", CallingConvention = CallingConvention.StdCall)]
  63. private extern static int Getbase64JpgData(byte[] pData, ref int pLen);
  64. #endregion
  65. #region 静态变量
  66. private static bool initialized = false;
  67. private static int currentPort = 1001; // 默认USB端口1
  68. #endregion
  69. #region 公共方法
  70. /// <summary>
  71. /// 初始化华视读卡器
  72. /// </summary>
  73. /// <param name="port">端口号:1-16为串口COM1-COM16,1001-1016为USB口1-16</param>
  74. /// <returns>初始化结果</returns>
  75. public static JObject Initialize(int port = 1001)
  76. {
  77. JObject result = new JObject();
  78. try
  79. {
  80. currentPort = port;
  81. int initResult = CVR_InitComm(port);
  82. if (initResult == 1)
  83. {
  84. initialized = true;
  85. result.Add("code", 200);
  86. result.Add("message", $"华视读卡器初始化成功,端口: {GetPortDescription(port)}");
  87. result.Add("port", port);
  88. result.Add("device", "华视电子身份证读卡器");
  89. }
  90. else
  91. {
  92. initialized = false;
  93. result.Add("code", 1001);
  94. result.Add("message", GetInitErrorMessage(initResult));
  95. result.Add("errorCode", initResult);
  96. }
  97. }
  98. catch (Exception ex)
  99. {
  100. initialized = false;
  101. result.Add("code", 1001);
  102. result.Add("message", $"初始化异常: {ex.Message}");
  103. }
  104. return result;
  105. }
  106. /// <summary>
  107. /// 读取身份证信息
  108. /// </summary>
  109. /// <param name="savePath">照片保存路径,为空则不保存照片</param>
  110. /// <param name="autoClose">读卡后是否自动关闭连接(默认false,但URL调用时建议true)</param>
  111. /// <returns>身份证信息</returns>
  112. public static JObject ReadIdCard(string savePath = "", bool autoClose = false)
  113. {
  114. if (!initialized)
  115. {
  116. var initResult = Initialize();
  117. if ((int)initResult["code"] != 200)
  118. {
  119. return initResult;
  120. }
  121. }
  122. JObject result = new JObject();
  123. try
  124. {
  125. // 1. 检测设备状态
  126. int status = CVR_GetStatus();
  127. if (status != 1)
  128. {
  129. result.Add("code", 1001);
  130. result.Add("message", "读卡器设备状态异常");
  131. return result;
  132. }
  133. // 2. 卡认证
  134. int authResult = CVR_Authenticate();
  135. if (authResult != 1)
  136. {
  137. result.Add("code", 1001);
  138. result.Add("message", GetAuthErrorMessage(authResult));
  139. result.Add("errorCode", authResult);
  140. return result;
  141. }
  142. // 3. 读取身份证
  143. int readResult = CVR_Read_FPContent();
  144. if (readResult != 1)
  145. {
  146. result.Add("code", 1001);
  147. result.Add("message", GetReadErrorMessage(readResult));
  148. result.Add("errorCode", readResult);
  149. return result;
  150. }
  151. // 4. 获取身份证信息
  152. var idCardInfo = GetIdCardInformation();
  153. // 5. 如果需要照片且上面读取失败,尝试使用CVR_Read_Content读取照片
  154. if (idCardInfo["photoBase64"] == null || string.IsNullOrEmpty(idCardInfo["photoBase64"].ToString()))
  155. {
  156. Console.WriteLine("使用CVR_Read_Content重新读取以获取照片数据...");
  157. int contentReadResult = CVR_Read_Content(1); // active=1表示读取照片
  158. if (contentReadResult == 1)
  159. {
  160. Console.WriteLine("CVR_Read_Content读取成功,重新获取照片数据");
  161. // 重新获取照片信息
  162. string newPhotoBase64 = GetIdCardPhotoBase64();
  163. if (!string.IsNullOrEmpty(newPhotoBase64) && newPhotoBase64.Length > 50)
  164. {
  165. idCardInfo["photoBase64"] = newPhotoBase64;
  166. idCardInfo["hasPhoto"] = true;
  167. // 重新判断格式
  168. if (newPhotoBase64.StartsWith("/9j/") || newPhotoBase64.StartsWith("iVBORw0KGgo"))
  169. {
  170. idCardInfo["photoFormat"] = "JPEG";
  171. }
  172. else if (newPhotoBase64.StartsWith("Qk") || newPhotoBase64.StartsWith("BM"))
  173. {
  174. idCardInfo["photoFormat"] = "BMP";
  175. }
  176. else
  177. {
  178. idCardInfo["photoFormat"] = "UNKNOWN";
  179. }
  180. idCardInfo["photoSize"] = newPhotoBase64.Length;
  181. }
  182. }
  183. }
  184. // 6. 保存照片(如果指定了路径)
  185. if (!string.IsNullOrEmpty(savePath))
  186. {
  187. SaveIdCardPhoto(savePath);
  188. }
  189. result.Add("code", 200);
  190. result.Add("type", "huashi_idcard");
  191. result.Add("message", "华视读卡器读取身份证成功");
  192. result.Add("data", idCardInfo);
  193. result.Add("device", "华视电子身份证读卡器");
  194. // 7. 自动关闭连接(如果启用)
  195. if (autoClose)
  196. {
  197. Close();
  198. result.Add("autoClose", true);
  199. result.Add("closeMessage", "读卡完成后已自动关闭连接");
  200. }
  201. }
  202. catch (Exception ex)
  203. {
  204. // 异常时重置初始化状态,下次自动重新初始化(模仿SSCard.dll的自愈机制)
  205. initialized = false;
  206. result.Add("code", 1001);
  207. result.Add("message", $"读取身份证异常: {ex.Message}(设备将在下次使用时自动重新初始化)");
  208. }
  209. return result;
  210. }
  211. /// <summary>
  212. /// 连续读卡模式(不需要每次拿起放下)
  213. /// </summary>
  214. /// <param name="savePath">照片保存路径</param>
  215. /// <param name="autoClose">读卡后是否自动关闭连接(默认false,但URL调用时建议true)</param>
  216. /// <returns>身份证信息</returns>
  217. public static JObject ReadIdCardContinuous(string savePath = "", bool autoClose = false)
  218. {
  219. if (!initialized)
  220. {
  221. var initResult = Initialize();
  222. if ((int)initResult["code"] != 200)
  223. {
  224. return initResult;
  225. }
  226. }
  227. JObject result = new JObject();
  228. try
  229. {
  230. // 使用连续读卡认证
  231. int authResult = CVR_AuthenticateForNoJudge();
  232. if (authResult != 1)
  233. {
  234. result.Add("code", 1001);
  235. result.Add("message", "连续读卡认证失败");
  236. return result;
  237. }
  238. // 读取身份证
  239. int readResult = CVR_Read_FPContent();
  240. if (readResult != 1)
  241. {
  242. result.Add("code", 1001);
  243. result.Add("message", GetReadErrorMessage(readResult));
  244. return result;
  245. }
  246. // 获取信息
  247. var idCardInfo = GetIdCardInformation();
  248. // 保存照片
  249. if (!string.IsNullOrEmpty(savePath))
  250. {
  251. SaveIdCardPhoto(savePath);
  252. }
  253. result.Add("code", 200);
  254. result.Add("type", "huashi_idcard_continuous");
  255. result.Add("message", "华视读卡器连续读取身份证成功");
  256. result.Add("data", idCardInfo);
  257. result.Add("device", "华视电子身份证读卡器");
  258. // 自动关闭连接(如果启用)
  259. if (autoClose)
  260. {
  261. Close();
  262. result.Add("autoClose", true);
  263. result.Add("closeMessage", "读卡完成后已自动关闭连接");
  264. }
  265. }
  266. catch (Exception ex)
  267. {
  268. // 异常时重置初始化状态,下次自动重新初始化(模仿SSCard.dll的自愈机制)
  269. initialized = false;
  270. result.Add("code", 1001);
  271. result.Add("message", $"连续读取身份证异常: {ex.Message}(设备将在下次使用时自动重新初始化)");
  272. }
  273. return result;
  274. }
  275. /// <summary>
  276. /// 获取设备状态
  277. /// </summary>
  278. /// <returns>设备状态信息</returns>
  279. public static JObject GetDeviceStatus()
  280. {
  281. JObject result = new JObject();
  282. try
  283. {
  284. if (!initialized)
  285. {
  286. // 支持自动初始化,模仿SSCard.dll的行为
  287. var initResult = Initialize();
  288. if ((int)initResult["code"] != 200)
  289. {
  290. result.Add("code", 1001);
  291. result.Add("message", "设备未初始化且初始化失败");
  292. result.Add("initialized", false);
  293. result.Add("initError", initResult["message"].ToString());
  294. return result;
  295. }
  296. }
  297. int status = CVR_GetStatus();
  298. result.Add("code", 200);
  299. result.Add("initialized", true);
  300. result.Add("port", currentPort);
  301. result.Add("portDescription", GetPortDescription(currentPort));
  302. result.Add("deviceStatus", status == 1 ? "正常" : "异常");
  303. result.Add("statusCode", status);
  304. result.Add("device", "华视电子身份证读卡器");
  305. // 获取安全模块号
  306. StringBuilder samId = new StringBuilder(256);
  307. int samResult = CVR_GetSAMID(samId);
  308. if (samResult == 1)
  309. {
  310. result.Add("samId", samId.ToString().Trim());
  311. }
  312. }
  313. catch (Exception ex)
  314. {
  315. // 异常时重置初始化状态
  316. initialized = false;
  317. result.Add("code", 1001);
  318. result.Add("message", $"获取设备状态异常: {ex.Message}(设备将在下次使用时自动重新初始化)");
  319. }
  320. return result;
  321. }
  322. /// <summary>
  323. /// 关闭华视读卡器连接
  324. /// </summary>
  325. /// <returns>关闭结果</returns>
  326. public static JObject Close()
  327. {
  328. JObject result = new JObject();
  329. try
  330. {
  331. if (initialized)
  332. {
  333. int closeResult = CVR_CloseComm();
  334. initialized = false;
  335. result.Add("code", 200);
  336. result.Add("message", "华视读卡器连接已关闭");
  337. result.Add("closeResult", closeResult);
  338. }
  339. else
  340. {
  341. result.Add("code", 200);
  342. result.Add("message", "华视读卡器未初始化,无需关闭");
  343. }
  344. }
  345. catch (Exception ex)
  346. {
  347. result.Add("code", 1001);
  348. result.Add("message", $"关闭连接异常: {ex.Message}");
  349. }
  350. return result;
  351. }
  352. #endregion
  353. #region 私有辅助方法
  354. /// <summary>
  355. /// 获取身份证完整信息
  356. /// </summary>
  357. /// <returns>身份证信息对象</returns>
  358. private static JObject GetIdCardInformation()
  359. {
  360. JObject info = new JObject();
  361. // 基本信息
  362. info.Add("name", GetStringData("name"));
  363. info.Add("sex", GetStringData("sex"));
  364. info.Add("nation", GetStringData("nation"));
  365. info.Add("birthday", GetStringData("birthday"));
  366. info.Add("idCode", GetStringData("idCode"));
  367. info.Add("address", GetStringData("address"));
  368. info.Add("department", GetStringData("department"));
  369. info.Add("startDate", GetStringData("startDate"));
  370. info.Add("endDate", GetStringData("endDate"));
  371. // 代码信息
  372. info.Add("sexCode", GetCodeData("sexCode"));
  373. info.Add("nationCode", GetCodeData("nationCode"));
  374. info.Add("certType", GetCodeData("certType"));
  375. // 照片信息(BASE64编码)
  376. string photoBase64 = GetIdCardPhotoBase64();
  377. info.Add("photoBase64", photoBase64);
  378. info.Add("hasPhoto", !string.IsNullOrEmpty(photoBase64));
  379. // 如果有照片,添加照片格式信息
  380. if (!string.IsNullOrEmpty(photoBase64))
  381. {
  382. // 根据BASE64前缀判断图片格式
  383. if (photoBase64.StartsWith("/9j/") || photoBase64.StartsWith("iVBORw0KGgo"))
  384. {
  385. info.Add("photoFormat", "JPEG");
  386. }
  387. else if (photoBase64.StartsWith("Qk") || photoBase64.StartsWith("BM"))
  388. {
  389. info.Add("photoFormat", "BMP");
  390. }
  391. else
  392. {
  393. info.Add("photoFormat", "UNKNOWN");
  394. }
  395. info.Add("photoSize", photoBase64.Length);
  396. }
  397. return info;
  398. }
  399. /// <summary>
  400. /// 安全获取字符串数据
  401. /// </summary>
  402. /// <param name="fieldName">字段名</param>
  403. /// <returns>获取到的字符串</returns>
  404. private static string GetStringData(string fieldName)
  405. {
  406. try
  407. {
  408. StringBuilder buffer = new StringBuilder(512);
  409. int len = 512;
  410. int result = 0;
  411. switch (fieldName)
  412. {
  413. case "name":
  414. result = GetPeopleName(buffer, ref len);
  415. break;
  416. case "sex":
  417. result = GetPeopleSex(buffer, ref len);
  418. break;
  419. case "nation":
  420. result = GetPeopleNation(buffer, ref len);
  421. break;
  422. case "birthday":
  423. result = GetPeopleBirthday(buffer, ref len);
  424. break;
  425. case "idCode":
  426. result = GetPeopleIDCode(buffer, ref len);
  427. break;
  428. case "address":
  429. result = GetPeopleAddress(buffer, ref len);
  430. break;
  431. case "department":
  432. result = GetDepartment(buffer, ref len);
  433. break;
  434. case "startDate":
  435. result = GetStartDate(buffer, ref len);
  436. break;
  437. case "endDate":
  438. result = GetEndDate(buffer, ref len);
  439. break;
  440. default:
  441. return "";
  442. }
  443. return result == 1 ? buffer.ToString().Trim() : "";
  444. }
  445. catch
  446. {
  447. return "";
  448. }
  449. }
  450. /// <summary>
  451. /// 安全获取代码数据
  452. /// </summary>
  453. /// <param name="fieldName">字段名</param>
  454. /// <returns>获取到的代码</returns>
  455. private static string GetCodeData(string fieldName)
  456. {
  457. try
  458. {
  459. byte[] buffer = new byte[32];
  460. int len = 32;
  461. int result = 0;
  462. switch (fieldName)
  463. {
  464. case "sexCode":
  465. result = GetSexCode(buffer, ref len);
  466. break;
  467. case "nationCode":
  468. result = GetNationCode(buffer, ref len);
  469. break;
  470. case "certType":
  471. result = GetCertType(buffer, ref len);
  472. break;
  473. default:
  474. return "";
  475. }
  476. if (result == 1)
  477. {
  478. return Encoding.Default.GetString(buffer, 0, len).Trim('\0');
  479. }
  480. return "";
  481. }
  482. catch
  483. {
  484. return "";
  485. }
  486. }
  487. /// <summary>
  488. /// 获取身份证照片的BASE64编码
  489. /// </summary>
  490. /// <returns>照片的BASE64编码字符串</returns>
  491. private static string GetIdCardPhotoBase64()
  492. {
  493. try
  494. {
  495. Console.WriteLine("开始获取照片BASE64编码...");
  496. // 首先尝试备用方法(获取二进制数据然后手动转换)
  497. Console.WriteLine("优先尝试备用方法获取二进制数据...");
  498. string fallbackResult = GetPhotoBase64Fallback();
  499. if (!string.IsNullOrEmpty(fallbackResult) && fallbackResult.Length > 50 && !fallbackResult.All(c => c == 'A'))
  500. {
  501. Console.WriteLine($"备用方法成功,返回长度: {fallbackResult.Length}");
  502. return fallbackResult;
  503. }
  504. // 如果备用方法失败,尝试直接获取BASE64(可能某些SDK版本支持)
  505. Console.WriteLine("备用方法失败,尝试直接获取BASE64...");
  506. // 尝试获取JPG格式的BASE64编码
  507. byte[] jpgBase64Data = new byte[38862 * 2]; // 文档说明:不超过38862*2字节
  508. int jpgLen = jpgBase64Data.Length;
  509. Console.WriteLine($"尝试调用Getbase64JpgData,缓冲区大小: {jpgLen}");
  510. int jpgResult = Getbase64JpgData(jpgBase64Data, ref jpgLen);
  511. Console.WriteLine($"Getbase64JpgData返回: {jpgResult}, 数据长度: {jpgLen}");
  512. if (jpgResult == 1 && jpgLen > 0)
  513. {
  514. // 转换为字符串并去除多余的空字节
  515. string jpgBase64 = Encoding.Default.GetString(jpgBase64Data, 0, jpgLen).Trim('\0');
  516. Console.WriteLine($"JPG BASE64长度: {jpgBase64.Length}, 前20字符: {(jpgBase64.Length > 20 ? jpgBase64.Substring(0, 20) : jpgBase64)}");
  517. // 检查是否是有效数据(不全是相同字符)
  518. if (!string.IsNullOrEmpty(jpgBase64) && jpgBase64.Length > 10 && !jpgBase64.All(c => c == jpgBase64[0]))
  519. {
  520. return jpgBase64;
  521. }
  522. }
  523. // 尝试获取BMP格式的BASE64编码
  524. byte[] bmpBase64Data = new byte[38862 * 2]; // 文档说明:不超过38862*2字节
  525. int bmpLen = bmpBase64Data.Length;
  526. Console.WriteLine($"尝试调用Getbase64BMPData,缓冲区大小: {bmpLen}");
  527. int bmpResult = Getbase64BMPData(bmpBase64Data, ref bmpLen);
  528. Console.WriteLine($"Getbase64BMPData返回: {bmpResult}, 数据长度: {bmpLen}");
  529. if (bmpResult == 1 && bmpLen > 0)
  530. {
  531. // 转换为字符串并去除多余的空字节
  532. string bmpBase64 = Encoding.Default.GetString(bmpBase64Data, 0, bmpLen).Trim('\0');
  533. Console.WriteLine($"BMP BASE64长度: {bmpBase64.Length}, 前20字符: {(bmpBase64.Length > 20 ? bmpBase64.Substring(0, 20) : bmpBase64)}");
  534. // 检查是否是有效数据(不全是相同字符)
  535. if (!string.IsNullOrEmpty(bmpBase64) && bmpBase64.Length > 10 && !bmpBase64.All(c => c == bmpBase64[0]))
  536. {
  537. return bmpBase64;
  538. }
  539. }
  540. Console.WriteLine("所有方法都未能获取到有效的照片数据");
  541. return "";
  542. }
  543. catch (Exception ex)
  544. {
  545. Console.WriteLine($"获取照片BASE64编码异常: {ex.Message}");
  546. return "";
  547. }
  548. }
  549. /// <summary>
  550. /// 备用方法:获取二进制照片数据并手动转换为BASE64
  551. /// </summary>
  552. /// <returns>照片的BASE64编码字符串</returns>
  553. private static string GetPhotoBase64Fallback()
  554. {
  555. try
  556. {
  557. Console.WriteLine("开始备用照片获取方法...");
  558. // 尝试获取JPG二进制数据
  559. byte[] jpgData = new byte[38862];
  560. int jpgLen = jpgData.Length;
  561. Console.WriteLine($"尝试调用GetJpgData,缓冲区大小: {jpgLen}");
  562. int jpgResult = GetJpgData(jpgData, ref jpgLen);
  563. Console.WriteLine($"GetJpgData返回: {jpgResult}, 数据长度: {jpgLen}");
  564. if (jpgResult == 1 && jpgLen > 0)
  565. {
  566. byte[] actualJpgData = new byte[jpgLen];
  567. Array.Copy(jpgData, actualJpgData, jpgLen);
  568. string jpgBase64 = Convert.ToBase64String(actualJpgData);
  569. Console.WriteLine($"JPG备用方法成功,BASE64长度: {jpgBase64.Length}, 前20字符: {(jpgBase64.Length > 20 ? jpgBase64.Substring(0, 20) : jpgBase64)}");
  570. return jpgBase64;
  571. }
  572. // 如果JPG失败,尝试获取BMP二进制数据
  573. byte[] bmpData = new byte[38862];
  574. int bmpLen = bmpData.Length;
  575. Console.WriteLine($"尝试调用GetBMPData,缓冲区大小: {bmpLen}");
  576. int bmpResult = GetBMPData(bmpData, ref bmpLen);
  577. Console.WriteLine($"GetBMPData返回: {bmpResult}, 数据长度: {bmpLen}");
  578. if (bmpResult == 1 && bmpLen > 0)
  579. {
  580. byte[] actualBmpData = new byte[bmpLen];
  581. Array.Copy(bmpData, actualBmpData, bmpLen);
  582. string bmpBase64 = Convert.ToBase64String(actualBmpData);
  583. Console.WriteLine($"BMP备用方法成功,BASE64长度: {bmpBase64.Length}, 前20字符: {(bmpBase64.Length > 20 ? bmpBase64.Substring(0, 20) : bmpBase64)}");
  584. return bmpBase64;
  585. }
  586. Console.WriteLine("备用照片获取方法:所有尝试都失败");
  587. return "";
  588. }
  589. catch (Exception ex)
  590. {
  591. Console.WriteLine($"备用照片获取方法异常: {ex.Message}");
  592. return "";
  593. }
  594. }
  595. /// <summary>
  596. /// 保存身份证照片
  597. /// </summary>
  598. /// <param name="savePath">保存路径</param>
  599. private static void SaveIdCardPhoto(string savePath)
  600. {
  601. try
  602. {
  603. if (!System.IO.Directory.Exists(savePath))
  604. {
  605. System.IO.Directory.CreateDirectory(savePath);
  606. }
  607. // 尝试获取BMP格式照片
  608. byte[] bmpData = new byte[38862];
  609. int bmpLen = 38862;
  610. int bmpResult = GetBMPData(bmpData, ref bmpLen);
  611. if (bmpResult == 1 && bmpLen > 0)
  612. {
  613. string bmpPath = System.IO.Path.Combine(savePath, $"idcard_photo_{DateTime.Now:yyyyMMddHHmmss}.bmp");
  614. byte[] actualBmpData = new byte[bmpLen];
  615. Array.Copy(bmpData, actualBmpData, bmpLen);
  616. System.IO.File.WriteAllBytes(bmpPath, actualBmpData);
  617. }
  618. // 尝试获取JPG格式照片
  619. byte[] jpgData = new byte[38862];
  620. int jpgLen = 38862;
  621. int jpgResult = GetJpgData(jpgData, ref jpgLen);
  622. if (jpgResult == 1 && jpgLen > 0)
  623. {
  624. string jpgPath = System.IO.Path.Combine(savePath, $"idcard_photo_{DateTime.Now:yyyyMMddHHmmss}.jpg");
  625. byte[] actualJpgData = new byte[jpgLen];
  626. Array.Copy(jpgData, actualJpgData, jpgLen);
  627. System.IO.File.WriteAllBytes(jpgPath, actualJpgData);
  628. }
  629. }
  630. catch (Exception ex)
  631. {
  632. // 照片保存失败不影响主要功能
  633. System.Diagnostics.Debug.WriteLine($"保存照片失败: {ex.Message}");
  634. }
  635. }
  636. /// <summary>
  637. /// 获取端口描述
  638. /// </summary>
  639. /// <param name="port">端口号</param>
  640. /// <returns>端口描述</returns>
  641. private static string GetPortDescription(int port)
  642. {
  643. if (port >= 1 && port <= 16)
  644. {
  645. return $"COM{port}";
  646. }
  647. else if (port >= 1001 && port <= 1016)
  648. {
  649. return $"USB{port - 1000}";
  650. }
  651. else
  652. {
  653. return $"端口{port}";
  654. }
  655. }
  656. /// <summary>
  657. /// 获取初始化错误信息
  658. /// </summary>
  659. /// <param name="errorCode">错误代码</param>
  660. /// <returns>错误信息</returns>
  661. private static string GetInitErrorMessage(int errorCode)
  662. {
  663. switch (errorCode)
  664. {
  665. case 1: return "初始化成功";
  666. case 2: return "端口打开失败,请检查读卡器连接";
  667. case -1: return "未知错误";
  668. case -2: return "动态库加载失败,请检查termb.dll文件";
  669. default: return $"初始化失败,错误代码: {errorCode}";
  670. }
  671. }
  672. /// <summary>
  673. /// 获取认证错误信息
  674. /// </summary>
  675. /// <param name="errorCode">错误代码</param>
  676. /// <returns>错误信息</returns>
  677. private static string GetAuthErrorMessage(int errorCode)
  678. {
  679. switch (errorCode)
  680. {
  681. case 1: return "卡片认证成功";
  682. case 2: return "寻卡失败,请重新放置卡片";
  683. case 3: return "选卡失败,请重新放置卡片";
  684. case 4: return "未连接读卡器,请检查设备";
  685. case 0: return "动态库未加载";
  686. default: return $"卡片认证失败,错误代码: {errorCode}";
  687. }
  688. }
  689. /// <summary>
  690. /// 获取读卡错误信息
  691. /// </summary>
  692. /// <param name="errorCode">错误代码</param>
  693. /// <returns>错误信息</returns>
  694. private static string GetReadErrorMessage(int errorCode)
  695. {
  696. switch (errorCode)
  697. {
  698. case 1: return "读卡成功";
  699. case 0: return "读身份证失败,请重新尝试";
  700. case 4: return "身份证读卡器未连接";
  701. case 99: return "动态库未加载";
  702. default: return $"读卡失败,错误代码: {errorCode}";
  703. }
  704. }
  705. #endregion
  706. }
  707. }
  708. // 为了支持LINQ扩展方法
  709. namespace System.Linq
  710. {
  711. internal static class EnumerableExtensions
  712. {
  713. public static T[] Take<T>(this T[] source, int count)
  714. {
  715. if (count >= source.Length) return source;
  716. T[] result = new T[count];
  717. Array.Copy(source, result, count);
  718. return result;
  719. }
  720. }
  721. }