Anonymous
Проблема многопоточности C# при одновременной печати смарт-карт на нескольких принтерах Evolis Primacy 2
Сообщение
Anonymous » 15 ноя 2025, 06:54
Я разрабатываю приложение C#, которое должно обеспечивать одновременную печать на четырех принтерах Evolis Primacy 2 с кодировщиками Elyctis. Каждый принтер должен записывать данные в чипы карт и печатать на картах совершенно независимо и без конфликтов.
Текущая реализация использует Task.Run для обработки пакетов, но у меня возникают конфликты ресурсов, взаимоблокировки и проблемы с памятью, когда несколько принтеров работают одновременно.
Основной метод пакетной обработки:
Код: Выделить всё
private async void BTNPERSOLOT_Click(object sender, EventArgs e)
{
BTNPERSOLOT.Enabled = false;
List
cartesAImprimer = listeFiltreePerso
.Take(FenêtrePrincipale.nombreCarteToPrint)
.Select(c => (Personnalisation)c.Clone())
.ToList();
string nomImprimante = CB_Printerss.SelectedItem.ToString();
listeFiltreePerso = listeFiltreePerso.Skip(FenêtrePrincipale.nombreCarteToPrint).ToList();
bindingSource.DataSource = listeFiltreePerso;
InitialiserDataGridViewPersonnalisation(listeFiltreePerso);
_ = Task.Run(async () =>
{
try
{
Device device = Evolis.Evolis.GetDevices().FirstOrDefault(p =>
string.Equals(p.name, nomImprimante, StringComparison.OrdinalIgnoreCase));
string[] lecteursEnregistres = ws.getReadersByPrinter(nomImprimante);
string[] lecteursValides = await GetValidPCSCReaders(lecteursEnregistres);
await impressionSimultanee(cartesAImprimer, device, lecteursValides);
this.Invoke((MethodInvoker)delegate
{
MessageBox.Show(this,
$"Lot of {cartesAImprimer.Count} cards for printer {nomImprimante} completed.",
"Batch Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
});
}
catch (Exception ex)
{
this.Invoke((MethodInvoker)delegate
{
MessageBox.Show(this,
$"Error in batch for printer {nomImprimante}:\n\n{ex.Message}",
"Batch Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
});
}
});
await Task.Delay(500);
BTNPERSOLOT.Enabled = true;
}
Логика одновременной печати:
Код: Выделить всё
private async Task impressionSimultanee(List
cartesAImprimer, Device imprimante, string[] lecteursImprim)
{
int successCount = 0;
List lignesNonEcrites = new List();
foreach (var carte in cartesAImprimer)
{
PrintTools printTool = null;
ConnexionResult resultatConnexionPuce = null;
try
{
printTool = new PrintTools();
resultatConnexionPuce = await PrintTools.connexionPuceAsync(imprimante, lecteursImprim);
if (!resultatConnexionPuce.IsSuccess)
{
throw new Exception("Chip connection failed.");
}
ModelResponse isWrite = PrintTools.ecriturePuce(carte, imprimante, lecteursImprim, resultatConnexionPuce.ConnectionObject);
if (!isWrite.status)
{
throw new Exception($"Chip writing failed: {isWrite.message}");
}
if (DateTime.TryParse(carte.dateNaissance, out DateTime dateN))
carte.dateNaissance = dateN.ToString("dd/MM/yyyy");
if (imprimante.model.ToString().Equals("Evolis_Primacy_2"))
{
Retour retour = await printTool.imprimerCarteEvolis(carte, imprimante.name);
if (!retour.IsSuccess)
{
throw new Exception("Graphic printing error!");
}
}
Personnalisation perso = await carteLocalRepository.GetPersonnalisationByNinLocalAsync(carte.nin);
if (perso != null)
{
perso.statutCarte = 2;
perso.numeroSerie = resultatConnexionPuce.NumeroSerie;
perso.login = User.login;
perso.dateDelivrance = DateTime.Now.ToString("yyyy-MM-dd");
perso.dateExpiration = DateTime.Now.AddYears(10).ToString("yyyy-MM-dd");
ResponseData reponsePersonnalisation = await ConnexionDB.updatePersonnalisation(perso);
if (reponsePersonnalisation != null && !reponsePersonnalisation.status)
{
lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (DB Update Error: {reponsePersonnalisation.msg})");
}
}
successCount++;
}
catch (Exception ex)
{
lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (Error: {ex.Message})");
PrintTools.execPrinterCmd("Se");
}
finally
{
if (printTool != null)
{
try { printTool = null; } catch { }
}
if (resultatConnexionPuce != null)
{
try { resultatConnexionPuce.ConnectionObject?.Disconnect(DISCONNECT.Leave); } catch { }
try { resultatConnexionPuce.m_iResMan?.ReleaseContext(); } catch { }
}
}
}
if (lignesNonEcrites.Count > 0)
{
string erreurs = string.Join("\n", lignesNonEcrites);
throw new Exception($"Batch completed with {lignesNonEcrites.Count} failure(s):\n{erreurs}");
}
}
Способ подключения чипа:
Код: Выделить всё
public static async Task connexionPuceAsync(Device choosenPrinter, string[] lecteursImprimante)
{
var result = new ConnexionResult { IsSuccess = false };
ISCardManager m_iResMan = null;
ISCardConnection m_iCard = null;
bool connected = false;
if (choosenPrinter.model.ToString().Equals("Evolis_Primacy_2"))
{
Connection co = null;
try
{
co = new Connection(choosenPrinter.name);
if (co.IsOpen())
{
string retour = co.SendCommand("Sis");
}
}
finally
{
co?.Close();
}
}
try
{
m_iResMan = new SCardManager();
m_iResMan.EstablishContext(SCOPE.System);
foreach (string lecteur in lecteursImprimante)
{
try
{
m_iCard = m_iResMan.CreateConnection(lecteur);
m_iCard.Connect(SHARE.Shared, PROTOCOL.T0orT1);
if (m_iCard.Connected)
{
connected = true;
break;
}
}
catch
{
try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { }
m_iCard = null;
}
}
if (!connected)
{
throw new Exception("No card found in printer readers");
}
ReadService readService = new ReadService(m_iCard);
ModelResponse reponseNumeroSerie = readService.getSerailNumber();
if (string.IsNullOrEmpty(reponseNumeroSerie?.message))
{
throw new Exception("Cannot read card serial number");
}
result.NumeroSerie = reponseNumeroSerie.message;
bool isCardPresente = await carteLocalRepository.CheckCarteExistsByNumeroSerieLocalAsync(result.NumeroSerie);
if (!isCardPresente)
{
Webservice ws = new Webservice();
isCardPresente = ws.CheckCarteExistsByNumeroSerie(result.NumeroSerie);
}
if (!isCardPresente)
{
throw new Exception($"Card (No. {result.NumeroSerie}) not found in database!");
}
result.IsSuccess = true;
result.ConnectionObject = m_iCard;
result.m_iResMan = m_iResMan;
return result;
}
catch (Exception exc)
{
try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { }
try { m_iResMan?.ReleaseContext(); } catch { }
throw new Exception("Error during card reading/verification: " + exc.Message, exc);
}
}
Метод записи чипа:
Код: Выделить всё
public static ModelResponse ecriturePuce(Personnalisation personnalisation, Device choosenPrinter, string[] lecteursImprimante, ISCardConnection ConnectionObject)
{
ModelResponse valeurRetour = new ModelResponse { status = false, message = "Error!", code = 0 };
try
{
if (personnalisation?.visage != null)
{
string compressedBase64 = compressImage(personnalisation.visage);
if (Base64ToHex(personnalisation.visage).Length > 75534)
{
personnalisation.visage = compressedBase64;
}
}
if (personnalisation?.signature != null)
{
string compressedBase64 = compressSignature(personnalisation.signature);
if (Base64ToHex(personnalisation.signature).Length > 34000)
{
personnalisation.signature = compressedBase64;
}
}
if (!ConnectionObject.Connected)
{
throw new Exception("Writing impossible: No card found.");
}
if (validation(personnalisation))
{
WriteService writeService = new WriteService(ConnectionObject);
ModelResponse isInitialize = writeService.initialisationStructure();
if (isInitialize.status)
{
DG1File dg1 = new DG1File();
dg1.registrationNumber = personnalisation.numeroEnregistrement ?? " ";
dg1.pensionerNumber = personnalisation.numeroPensionnaire ?? " ";
dg1.phoneNumber = personnalisation.telephone ?? " ";
dg1.idNumber = personnalisation.nin ?? " ";
dg1.prenom = personnalisation.prenom ?? " ";
dg1.nom = personnalisation.nom ?? " ";
dg1.sexe = !string.IsNullOrEmpty(personnalisation.sexe) ? personnalisation.sexe[0].ToString() : " ";
dg1.prenomPere = personnalisation.prenomPere ?? " ";
dg1.prenomMere = personnalisation.prenomMere ?? " ";
dg1.nomMere = personnalisation.nomMere ?? " ";
dg1.dateNaissance = dateFormated(personnalisation.dateNaissance) ?? " ";
dg1.lieuNaissance = personnalisation.lieuNaissance ?? " ";
DG2File dg2 = new DG2File();
dg2.dateDelivrance = DateTime.Now.ToString("dd-MM-yyyy");
personnalisation.dateDelivrance = dg2.dateDelivrance;
dg2.dateExpiration = DateTime.Now.AddYears(10).ToString("dd-MM-yyyy");
personnalisation.dateExpiration = dg2.dateExpiration;
dg2.dateDerniereLecture = DateTime.Now.ToString("dd-MM-yyyy");
DG3File dg3 = new DG3File();
if (personnalisation.signature == null)
{
dg3.neSaitPasSigner = true;
dg2.tailleSignature = "0";
}
else
{
dg3.neSaitPasSigner = false;
dg3.signature = personnalisation.signature;
dg2.tailleSignature = Base64ToHex(personnalisation.signature).Length.ToString();
}
DG4File dg4 = new DG4File();
dg4.etatMainGauche = true;
dg4.etatMainDroite = true;
Finger[] fingers = new Finger[10];
for (int i = 1; i
{
using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString))
{
await connection.OpenAsync();
string query = @"SELECT * FROM impression WHERE LOWER(nin) = LOWER(@nin) LIMIT 1";
using (var cmd = new SQLiteCommand(query, connection))
{
cmd.Parameters.AddWithValue("@nin", nin);
using (var reader = await cmd.ExecuteReaderAsync())
{
bool hasRecord = await reader.ReadAsync();
if (!hasRecord) return null;
return new Personnalisation
{
id = reader["id"] != DBNull.Value ? Convert.ToInt32(reader["id"]) : 0,
numeroEnregistrement = GetSafeString(reader, "numero_enregistrement"),
statutCarte = GetSafeInt(reader, "statut_carte"),
numeroSerie = GetSafeString(reader, "numero_serie"),
nin = GetSafeString(reader, "nin"),
numeroPensionnaire = GetSafeString(reader, "numero_pensionnaire"),
prenom = GetSafeString(reader, "prenom"),
nom = GetSafeString(reader, "nom"),
dateNaissance = GetSafeString(reader, "date_naissance"),
lieuNaissance = GetSafeString(reader, "lieu_naissance"),
sexe = GetSafeString(reader, "sexe"),
nomMere = GetSafeString(reader, "nom_mere"),
prenomMere = GetSafeString(reader, "prenom_mere"),
prenomPere = GetSafeString(reader, "prenom_pere"),
telephone = GetSafeString(reader, "telephone"),
dateDelivrance = GetSafeString(reader, "date_delivrance"),
dateExpiration = GetSafeString(reader, "date_expiration"),
visage = GetSafeString(reader, "visage"),
signature = GetSafeString(reader, "signature")
};
}
}
}
});
return result;
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] GetPersonnalisationByNinLocalAsync: {ex.Message}");
return null;
}
}
public static Task updatePersonnalisation(Personnalisation p)
{
return DbTaskQueue.Enqueue(async () =>
{
var response = new ResponseData();
try
{
using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString))
{
await connection.OpenAsync();
using (var transaction = connection.BeginTransaction())
{
try
{
using (var checkCmd = new SQLiteCommand(
"SELECT COUNT(*) FROM impression WHERE id=@id", connection))
{
checkCmd.Parameters.AddWithValue("@id", p.id);
if (Convert.ToInt32(await checkCmd.ExecuteScalarAsync()) == 0)
{
response.status = false;
response.msg = "Record not found.";
return response;
}
}
if (string.IsNullOrWhiteSpace(p.numeroSerie))
{
response.status = false;
response.msg = "Missing serial number.";
return response;
}
using (var updateCmd = new SQLiteCommand(
@"UPDATE impression SET
statut_carte=@statut,
date_modification=@modification,
login=@login,
date_delivrance=@delivrance,
date_expiration=@expiration,
numero_serie=@serie
WHERE id=@id", connection))
{
updateCmd.Parameters.AddWithValue("@statut", p.statutCarte);
updateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow);
updateCmd.Parameters.AddWithValue("@login", p.login);
updateCmd.Parameters.AddWithValue("@delivrance", p.dateDelivrance);
updateCmd.Parameters.AddWithValue("@expiration", p.dateExpiration);
updateCmd.Parameters.AddWithValue("@serie", p.numeroSerie);
updateCmd.Parameters.AddWithValue("@id", p.id);
await updateCmd.ExecuteNonQueryAsync();
}
using (var cardUpdateCmd = new SQLiteCommand(
@"UPDATE carte_local SET
login=@login,
etat=@etat,
dateModification=@modification
WHERE numeroSerie=@serie", connection))
{
cardUpdateCmd.Parameters.AddWithValue("@login", p.login);
cardUpdateCmd.Parameters.AddWithValue("@etat", p.statutCarte);
cardUpdateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow);
cardUpdateCmd.Parameters.AddWithValue("@serie", p.numeroSerie);
await cardUpdateCmd.ExecuteNonQueryAsync();
}
transaction.Commit();
response.status = true;
response.msg = "Update successful.";
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
catch (Exception ex)
{
response.status = false;
response.msg = ex.Message;
}
return response;
});
}
public static class DbTaskQueue
{
private static readonly SemaphoreSlim _dbSemaphore = new SemaphoreSlim(4, 4); // 4 accès concurrents
public static async Task Enqueue(Func taskGenerator)
{
await _dbSemaphore.WaitAsync();
try
{
Console.WriteLine($"[DbTaskQueue] Sémaphore acquis - Restant: {_dbSemaphore.CurrentCount}");
var result = await taskGenerator();
return result;
}
finally
{
_dbSemaphore.Release();
Console.WriteLine($"[DbTaskQueue] Sémaphore libéré - Restant: {_dbSemaphore.CurrentCount}");
}
}
public static async Task Enqueue(Func action)
{
await _dbSemaphore.WaitAsync();
try
{
await action();
}
finally
{
_dbSemaphore.Release();
}
}
}
Основные проблемы:
Конфликты ресурсов между соединениями PCSC на нескольких принтерах.
Утечки памяти при обработке изображений и подключениях карт.
Одновременный доступ к аппаратным ресурсам принтера.
Проблемы безопасности потоков с общим контекстом PCSC.
Правильное использование подключений карт и ресурсов изображений.>
Подробнее здесь:
https://stackoverflow.com/questions/798 ... multiple-e
1763178865
Anonymous
Я разрабатываю приложение C#, которое должно обеспечивать одновременную печать на четырех принтерах Evolis Primacy 2 с кодировщиками Elyctis. Каждый принтер должен записывать данные в чипы карт и печатать на картах совершенно независимо и без конфликтов. Текущая реализация использует Task.Run для обработки пакетов, но у меня возникают конфликты ресурсов, взаимоблокировки и проблемы с памятью, когда несколько принтеров работают одновременно. Основной метод пакетной обработки: [code]private async void BTNPERSOLOT_Click(object sender, EventArgs e) { BTNPERSOLOT.Enabled = false; List cartesAImprimer = listeFiltreePerso .Take(FenêtrePrincipale.nombreCarteToPrint) .Select(c => (Personnalisation)c.Clone()) .ToList(); string nomImprimante = CB_Printerss.SelectedItem.ToString(); listeFiltreePerso = listeFiltreePerso.Skip(FenêtrePrincipale.nombreCarteToPrint).ToList(); bindingSource.DataSource = listeFiltreePerso; InitialiserDataGridViewPersonnalisation(listeFiltreePerso); _ = Task.Run(async () => { try { Device device = Evolis.Evolis.GetDevices().FirstOrDefault(p => string.Equals(p.name, nomImprimante, StringComparison.OrdinalIgnoreCase)); string[] lecteursEnregistres = ws.getReadersByPrinter(nomImprimante); string[] lecteursValides = await GetValidPCSCReaders(lecteursEnregistres); await impressionSimultanee(cartesAImprimer, device, lecteursValides); this.Invoke((MethodInvoker)delegate { MessageBox.Show(this, $"Lot of {cartesAImprimer.Count} cards for printer {nomImprimante} completed.", "Batch Done", MessageBoxButtons.OK, MessageBoxIcon.Information); }); } catch (Exception ex) { this.Invoke((MethodInvoker)delegate { MessageBox.Show(this, $"Error in batch for printer {nomImprimante}:\n\n{ex.Message}", "Batch Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }); } }); await Task.Delay(500); BTNPERSOLOT.Enabled = true; } [/code] Логика одновременной печати: [code]private async Task impressionSimultanee(List cartesAImprimer, Device imprimante, string[] lecteursImprim) { int successCount = 0; List lignesNonEcrites = new List(); foreach (var carte in cartesAImprimer) { PrintTools printTool = null; ConnexionResult resultatConnexionPuce = null; try { printTool = new PrintTools(); resultatConnexionPuce = await PrintTools.connexionPuceAsync(imprimante, lecteursImprim); if (!resultatConnexionPuce.IsSuccess) { throw new Exception("Chip connection failed."); } ModelResponse isWrite = PrintTools.ecriturePuce(carte, imprimante, lecteursImprim, resultatConnexionPuce.ConnectionObject); if (!isWrite.status) { throw new Exception($"Chip writing failed: {isWrite.message}"); } if (DateTime.TryParse(carte.dateNaissance, out DateTime dateN)) carte.dateNaissance = dateN.ToString("dd/MM/yyyy"); if (imprimante.model.ToString().Equals("Evolis_Primacy_2")) { Retour retour = await printTool.imprimerCarteEvolis(carte, imprimante.name); if (!retour.IsSuccess) { throw new Exception("Graphic printing error!"); } } Personnalisation perso = await carteLocalRepository.GetPersonnalisationByNinLocalAsync(carte.nin); if (perso != null) { perso.statutCarte = 2; perso.numeroSerie = resultatConnexionPuce.NumeroSerie; perso.login = User.login; perso.dateDelivrance = DateTime.Now.ToString("yyyy-MM-dd"); perso.dateExpiration = DateTime.Now.AddYears(10).ToString("yyyy-MM-dd"); ResponseData reponsePersonnalisation = await ConnexionDB.updatePersonnalisation(perso); if (reponsePersonnalisation != null && !reponsePersonnalisation.status) { lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (DB Update Error: {reponsePersonnalisation.msg})"); } } successCount++; } catch (Exception ex) { lignesNonEcrites.Add($"{carte.prenom} {carte.nom} (Error: {ex.Message})"); PrintTools.execPrinterCmd("Se"); } finally { if (printTool != null) { try { printTool = null; } catch { } } if (resultatConnexionPuce != null) { try { resultatConnexionPuce.ConnectionObject?.Disconnect(DISCONNECT.Leave); } catch { } try { resultatConnexionPuce.m_iResMan?.ReleaseContext(); } catch { } } } } if (lignesNonEcrites.Count > 0) { string erreurs = string.Join("\n", lignesNonEcrites); throw new Exception($"Batch completed with {lignesNonEcrites.Count} failure(s):\n{erreurs}"); } } [/code] Способ подключения чипа: [code]public static async Task connexionPuceAsync(Device choosenPrinter, string[] lecteursImprimante) { var result = new ConnexionResult { IsSuccess = false }; ISCardManager m_iResMan = null; ISCardConnection m_iCard = null; bool connected = false; if (choosenPrinter.model.ToString().Equals("Evolis_Primacy_2")) { Connection co = null; try { co = new Connection(choosenPrinter.name); if (co.IsOpen()) { string retour = co.SendCommand("Sis"); } } finally { co?.Close(); } } try { m_iResMan = new SCardManager(); m_iResMan.EstablishContext(SCOPE.System); foreach (string lecteur in lecteursImprimante) { try { m_iCard = m_iResMan.CreateConnection(lecteur); m_iCard.Connect(SHARE.Shared, PROTOCOL.T0orT1); if (m_iCard.Connected) { connected = true; break; } } catch { try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { } m_iCard = null; } } if (!connected) { throw new Exception("No card found in printer readers"); } ReadService readService = new ReadService(m_iCard); ModelResponse reponseNumeroSerie = readService.getSerailNumber(); if (string.IsNullOrEmpty(reponseNumeroSerie?.message)) { throw new Exception("Cannot read card serial number"); } result.NumeroSerie = reponseNumeroSerie.message; bool isCardPresente = await carteLocalRepository.CheckCarteExistsByNumeroSerieLocalAsync(result.NumeroSerie); if (!isCardPresente) { Webservice ws = new Webservice(); isCardPresente = ws.CheckCarteExistsByNumeroSerie(result.NumeroSerie); } if (!isCardPresente) { throw new Exception($"Card (No. {result.NumeroSerie}) not found in database!"); } result.IsSuccess = true; result.ConnectionObject = m_iCard; result.m_iResMan = m_iResMan; return result; } catch (Exception exc) { try { m_iCard?.Disconnect(DISCONNECT.Leave); } catch { } try { m_iResMan?.ReleaseContext(); } catch { } throw new Exception("Error during card reading/verification: " + exc.Message, exc); } } [/code] Метод записи чипа: [code]public static ModelResponse ecriturePuce(Personnalisation personnalisation, Device choosenPrinter, string[] lecteursImprimante, ISCardConnection ConnectionObject) { ModelResponse valeurRetour = new ModelResponse { status = false, message = "Error!", code = 0 }; try { if (personnalisation?.visage != null) { string compressedBase64 = compressImage(personnalisation.visage); if (Base64ToHex(personnalisation.visage).Length > 75534) { personnalisation.visage = compressedBase64; } } if (personnalisation?.signature != null) { string compressedBase64 = compressSignature(personnalisation.signature); if (Base64ToHex(personnalisation.signature).Length > 34000) { personnalisation.signature = compressedBase64; } } if (!ConnectionObject.Connected) { throw new Exception("Writing impossible: No card found."); } if (validation(personnalisation)) { WriteService writeService = new WriteService(ConnectionObject); ModelResponse isInitialize = writeService.initialisationStructure(); if (isInitialize.status) { DG1File dg1 = new DG1File(); dg1.registrationNumber = personnalisation.numeroEnregistrement ?? " "; dg1.pensionerNumber = personnalisation.numeroPensionnaire ?? " "; dg1.phoneNumber = personnalisation.telephone ?? " "; dg1.idNumber = personnalisation.nin ?? " "; dg1.prenom = personnalisation.prenom ?? " "; dg1.nom = personnalisation.nom ?? " "; dg1.sexe = !string.IsNullOrEmpty(personnalisation.sexe) ? personnalisation.sexe[0].ToString() : " "; dg1.prenomPere = personnalisation.prenomPere ?? " "; dg1.prenomMere = personnalisation.prenomMere ?? " "; dg1.nomMere = personnalisation.nomMere ?? " "; dg1.dateNaissance = dateFormated(personnalisation.dateNaissance) ?? " "; dg1.lieuNaissance = personnalisation.lieuNaissance ?? " "; DG2File dg2 = new DG2File(); dg2.dateDelivrance = DateTime.Now.ToString("dd-MM-yyyy"); personnalisation.dateDelivrance = dg2.dateDelivrance; dg2.dateExpiration = DateTime.Now.AddYears(10).ToString("dd-MM-yyyy"); personnalisation.dateExpiration = dg2.dateExpiration; dg2.dateDerniereLecture = DateTime.Now.ToString("dd-MM-yyyy"); DG3File dg3 = new DG3File(); if (personnalisation.signature == null) { dg3.neSaitPasSigner = true; dg2.tailleSignature = "0"; } else { dg3.neSaitPasSigner = false; dg3.signature = personnalisation.signature; dg2.tailleSignature = Base64ToHex(personnalisation.signature).Length.ToString(); } DG4File dg4 = new DG4File(); dg4.etatMainGauche = true; dg4.etatMainDroite = true; Finger[] fingers = new Finger[10]; for (int i = 1; i { using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString)) { await connection.OpenAsync(); string query = @"SELECT * FROM impression WHERE LOWER(nin) = LOWER(@nin) LIMIT 1"; using (var cmd = new SQLiteCommand(query, connection)) { cmd.Parameters.AddWithValue("@nin", nin); using (var reader = await cmd.ExecuteReaderAsync()) { bool hasRecord = await reader.ReadAsync(); if (!hasRecord) return null; return new Personnalisation { id = reader["id"] != DBNull.Value ? Convert.ToInt32(reader["id"]) : 0, numeroEnregistrement = GetSafeString(reader, "numero_enregistrement"), statutCarte = GetSafeInt(reader, "statut_carte"), numeroSerie = GetSafeString(reader, "numero_serie"), nin = GetSafeString(reader, "nin"), numeroPensionnaire = GetSafeString(reader, "numero_pensionnaire"), prenom = GetSafeString(reader, "prenom"), nom = GetSafeString(reader, "nom"), dateNaissance = GetSafeString(reader, "date_naissance"), lieuNaissance = GetSafeString(reader, "lieu_naissance"), sexe = GetSafeString(reader, "sexe"), nomMere = GetSafeString(reader, "nom_mere"), prenomMere = GetSafeString(reader, "prenom_mere"), prenomPere = GetSafeString(reader, "prenom_pere"), telephone = GetSafeString(reader, "telephone"), dateDelivrance = GetSafeString(reader, "date_delivrance"), dateExpiration = GetSafeString(reader, "date_expiration"), visage = GetSafeString(reader, "visage"), signature = GetSafeString(reader, "signature") }; } } } }); return result; } catch (Exception ex) { Console.WriteLine($"[ERROR] GetPersonnalisationByNinLocalAsync: {ex.Message}"); return null; } } public static Task updatePersonnalisation(Personnalisation p) { return DbTaskQueue.Enqueue(async () => { var response = new ResponseData(); try { using (var connection = new SQLiteConnection(ConnexionDB.localConnectionString)) { await connection.OpenAsync(); using (var transaction = connection.BeginTransaction()) { try { using (var checkCmd = new SQLiteCommand( "SELECT COUNT(*) FROM impression WHERE id=@id", connection)) { checkCmd.Parameters.AddWithValue("@id", p.id); if (Convert.ToInt32(await checkCmd.ExecuteScalarAsync()) == 0) { response.status = false; response.msg = "Record not found."; return response; } } if (string.IsNullOrWhiteSpace(p.numeroSerie)) { response.status = false; response.msg = "Missing serial number."; return response; } using (var updateCmd = new SQLiteCommand( @"UPDATE impression SET statut_carte=@statut, date_modification=@modification, login=@login, date_delivrance=@delivrance, date_expiration=@expiration, numero_serie=@serie WHERE id=@id", connection)) { updateCmd.Parameters.AddWithValue("@statut", p.statutCarte); updateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow); updateCmd.Parameters.AddWithValue("@login", p.login); updateCmd.Parameters.AddWithValue("@delivrance", p.dateDelivrance); updateCmd.Parameters.AddWithValue("@expiration", p.dateExpiration); updateCmd.Parameters.AddWithValue("@serie", p.numeroSerie); updateCmd.Parameters.AddWithValue("@id", p.id); await updateCmd.ExecuteNonQueryAsync(); } using (var cardUpdateCmd = new SQLiteCommand( @"UPDATE carte_local SET login=@login, etat=@etat, dateModification=@modification WHERE numeroSerie=@serie", connection)) { cardUpdateCmd.Parameters.AddWithValue("@login", p.login); cardUpdateCmd.Parameters.AddWithValue("@etat", p.statutCarte); cardUpdateCmd.Parameters.AddWithValue("@modification", DateTime.UtcNow); cardUpdateCmd.Parameters.AddWithValue("@serie", p.numeroSerie); await cardUpdateCmd.ExecuteNonQueryAsync(); } transaction.Commit(); response.status = true; response.msg = "Update successful."; } catch { transaction.Rollback(); throw; } } } } catch (Exception ex) { response.status = false; response.msg = ex.Message; } return response; }); } public static class DbTaskQueue { private static readonly SemaphoreSlim _dbSemaphore = new SemaphoreSlim(4, 4); // 4 accès concurrents public static async Task Enqueue(Func taskGenerator) { await _dbSemaphore.WaitAsync(); try { Console.WriteLine($"[DbTaskQueue] Sémaphore acquis - Restant: {_dbSemaphore.CurrentCount}"); var result = await taskGenerator(); return result; } finally { _dbSemaphore.Release(); Console.WriteLine($"[DbTaskQueue] Sémaphore libéré - Restant: {_dbSemaphore.CurrentCount}"); } } public static async Task Enqueue(Func action) { await _dbSemaphore.WaitAsync(); try { await action(); } finally { _dbSemaphore.Release(); } } } [/code] Основные проблемы: Конфликты ресурсов между соединениями PCSC на нескольких принтерах. Утечки памяти при обработке изображений и подключениях карт. Одновременный доступ к аппаратным ресурсам принтера. Проблемы безопасности потоков с общим контекстом PCSC. Правильное использование подключений карт и ресурсов изображений.> Подробнее здесь: [url]https://stackoverflow.com/questions/79820565/c-sharp-multithreading-issue-with-simultaneous-smart-card-printing-on-multiple-e[/url]