using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Net; using System.Threading; using System.Runtime.InteropServices; namespace DigitalPersonaServiceDirect { // Clase helper para forzar foco en ventanas public static class WindowHelper { [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId(); public const int SW_SHOW = 5; public const int SW_RESTORE = 9; public static void ForceWindowToFront(Form form) { if (form.InvokeRequired) { form.Invoke(new Action(() => ForceWindowToFront(form))); return; } IntPtr hWnd = form.Handle; // Obtener el thread del foreground window actual IntPtr foregroundWnd = GetForegroundWindow(); uint foregroundThreadId = GetWindowThreadProcessId(foregroundWnd, IntPtr.Zero); uint currentThreadId = GetCurrentThreadId(); // Attach input threads if (foregroundThreadId != currentThreadId) { AttachThreadInput(foregroundThreadId, currentThreadId, true); } // Forzar ventana al frente ShowWindow(hWnd, SW_SHOW); SetForegroundWindow(hWnd); // Detach input threads if (foregroundThreadId != currentThreadId) { AttachThreadInput(foregroundThreadId, currentThreadId, false); } // Métodos adicionales de Form para asegurar foco form.TopMost = true; form.TopMost = false; form.Activate(); form.BringToFront(); form.Focus(); } } // Formulario de captura base (igual al oficial) public partial class CaptureForm : Form, DPFP.Capture.EventHandler { public CaptureForm() { InitializeComponent(); } protected virtual void Init() { try { Capturer = new DPFP.Capture.Capture(); if (null != Capturer) Capturer.EventHandler = this; else SetPrompt("Can't initiate capture operation!"); } catch { MessageBox.Show("Can't initiate capture operation!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } protected virtual void Process(DPFP.Sample Sample) { DrawPicture(ConvertSampleToBitmap(Sample)); } protected void Start() { if (null != Capturer) { try { Capturer.StartCapture(); SetPrompt("Using the fingerprint reader, scan your fingerprint."); } catch { SetPrompt("Can't initiate capture!"); } } } protected void Stop() { if (null != Capturer) { try { Capturer.StopCapture(); } catch { SetPrompt("Can't terminate capture!"); } } } private void CaptureForm_Load(object sender, EventArgs e) { Init(); Start(); } private void CaptureForm_FormClosed(object sender, FormClosedEventArgs e) { Stop(); } // DPFP.Capture.EventHandler implementation public void OnComplete(object Capture, string ReaderSerialNumber, DPFP.Sample Sample) { MakeReport("The fingerprint sample was captured."); SetPrompt("Scan the same fingerprint again."); Process(Sample); } public void OnFingerGone(object Capture, string ReaderSerialNumber) { MakeReport("The finger was removed from the fingerprint reader."); } public void OnFingerTouch(object Capture, string ReaderSerialNumber) { MakeReport("The fingerprint reader was touched."); } public void OnReaderConnect(object Capture, string ReaderSerialNumber) { MakeReport("The fingerprint reader was connected."); } public void OnReaderDisconnect(object Capture, string ReaderSerialNumber) { MakeReport("The fingerprint reader was disconnected."); } public void OnSampleQuality(object Capture, string ReaderSerialNumber, DPFP.Capture.CaptureFeedback CaptureFeedback) { if (CaptureFeedback == DPFP.Capture.CaptureFeedback.Good) MakeReport("The quality of the fingerprint sample is good."); else MakeReport("The quality of the fingerprint sample is poor."); } protected Bitmap ConvertSampleToBitmap(DPFP.Sample Sample) { DPFP.Capture.SampleConversion Convertor = new DPFP.Capture.SampleConversion(); Bitmap bitmap = null; Convertor.ConvertToPicture(Sample, ref bitmap); return bitmap; } protected DPFP.FeatureSet ExtractFeatures(DPFP.Sample Sample, DPFP.Processing.DataPurpose Purpose) { DPFP.Processing.FeatureExtraction Extractor = new DPFP.Processing.FeatureExtraction(); DPFP.Capture.CaptureFeedback feedback = DPFP.Capture.CaptureFeedback.None; DPFP.FeatureSet features = new DPFP.FeatureSet(); Extractor.CreateFeatureSet(Sample, Purpose, ref feedback, ref features); if (feedback == DPFP.Capture.CaptureFeedback.Good) return features; else return null; } protected void SetStatus(string status) { this.Invoke(new MethodInvoker(delegate() { StatusLine.Text = status; })); } protected void SetPrompt(string prompt) { this.Invoke(new MethodInvoker(delegate() { Prompt.Text = prompt; })); } protected void MakeReport(string message) { this.Invoke(new MethodInvoker(delegate() { StatusText.AppendText(message + "\r\n"); })); } private void DrawPicture(Bitmap bitmap) { this.Invoke(new MethodInvoker(delegate() { Picture.Image = new Bitmap(bitmap, Picture.Size); })); } private DPFP.Capture.Capture Capturer; // Componentes del Form private void InitializeComponent() { this.Picture = new System.Windows.Forms.PictureBox(); this.Prompt = new System.Windows.Forms.Label(); this.StatusLine = new System.Windows.Forms.Label(); this.StatusText = new System.Windows.Forms.TextBox(); ((System.ComponentModel.ISupportInitialize)(this.Picture)).BeginInit(); this.SuspendLayout(); // Picture this.Picture.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.Picture.Location = new System.Drawing.Point(12, 50); this.Picture.Size = new System.Drawing.Size(258, 200); this.Picture.TabStop = false; // Prompt this.Prompt.Location = new System.Drawing.Point(276, 50); this.Prompt.Size = new System.Drawing.Size(300, 50); this.Prompt.Text = "Prompt:"; // StatusLine this.StatusLine.Location = new System.Drawing.Point(276, 110); this.StatusLine.Size = new System.Drawing.Size(300, 20); this.StatusLine.Text = "Status:"; // StatusText this.StatusText.Location = new System.Drawing.Point(276, 140); this.StatusText.Multiline = true; this.StatusText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.StatusText.Size = new System.Drawing.Size(300, 110); // Form this.ClientSize = new System.Drawing.Size(590, 270); this.Controls.Add(this.Picture); this.Controls.Add(this.Prompt); this.Controls.Add(this.StatusLine); this.Controls.Add(this.StatusText); this.Text = "Fingerprint Capture"; this.Load += new System.EventHandler(this.CaptureForm_Load); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.CaptureForm_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.Picture)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } protected System.Windows.Forms.PictureBox Picture; protected System.Windows.Forms.Label Prompt; protected System.Windows.Forms.Label StatusLine; protected System.Windows.Forms.TextBox StatusText; } // Formulario de enrollment (igual al oficial) public class EnrollmentForm : CaptureForm { public delegate void OnTemplateEventHandler(DPFP.Template template); public event OnTemplateEventHandler OnTemplate; private int CurrentUserId; private string CurrentDedo; public EnrollmentForm(int userId, string dedo = null) { CurrentUserId = userId; CurrentDedo = dedo; } protected override void Init() { base.Init(); base.Text = "Fingerprint Enrollment"; Enroller = new DPFP.Processing.Enrollment(); UpdateStatus(); } protected override void Process(DPFP.Sample Sample) { base.Process(Sample); DPFP.FeatureSet features = ExtractFeatures(Sample, DPFP.Processing.DataPurpose.Enrollment); if (features != null) { try { MakeReport("The fingerprint feature set was created."); Enroller.AddFeatures(features); } finally { UpdateStatus(); switch (Enroller.TemplateStatus) { case DPFP.Processing.Enrollment.Status.Ready: // Template listo - guardar y cerrar SaveTemplate(Enroller.Template); if (OnTemplate != null) OnTemplate.Invoke(Enroller.Template); SetPrompt("Enrollment completed successfully!"); Stop(); // Cerrar automáticamente después de 2 segundos System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Interval = 2000; timer.Tick += (s, e) => { timer.Stop(); this.Close(); }; timer.Start(); break; case DPFP.Processing.Enrollment.Status.Failed: Enroller.Clear(); Stop(); UpdateStatus(); if (OnTemplate != null) OnTemplate.Invoke(null); Start(); break; } } } } private void SaveTemplate(DPFP.Template template) { try { // Ya no creamos directorio ni guardamos archivos locales // Solo convertimos a Base64 y guardamos en la base de datos // Convertir template a Base64 string base64Template = ConvertTemplateToBase64(template); // Guardar en base de datos (llamada a API) SaveTemplateToDatabase(CurrentUserId, base64Template); MakeReport("Template saved to database for user " + CurrentUserId); MakeReport("Base64 template: " + base64Template.Substring(0, Math.Min(50, base64Template.Length)) + "..."); } catch (Exception ex) { MakeReport("Error saving template: " + ex.Message); } } private string ConvertTemplateToBase64(DPFP.Template template) { try { using (MemoryStream ms = new MemoryStream()) { template.Serialize(ms); byte[] templateBytes = ms.ToArray(); return Convert.ToBase64String(templateBytes); } } catch (Exception ex) { MakeReport("Error converting template to Base64: " + ex.Message); return null; } } private void SaveTemplateToDatabase(int userId, string base64Template) { try { // Llamar a la API PHP para guardar en base de datos using (var client = new System.Net.WebClient()) { client.Headers["Content-Type"] = "application/json"; string apiUrl = Program.ApiUrl; string dedoPart = ""; if (!string.IsNullOrEmpty(CurrentDedo)) { dedoPart = ",\"dedo\":\"" + CurrentDedo + "\""; } string jsonData = string.Format("{{\"tipo\":\"saveFingerprintTemplate\",\"idUsuario\":{0},\"templateBase64\":\"{1}\"{2}}}", userId, base64Template, dedoPart); string response = client.UploadString(apiUrl, "POST", jsonData); MakeReport("Template saved to database: " + response); } } catch (Exception ex) { MakeReport("Error saving to database: " + ex.Message); } } private void UpdateStatus() { SetStatus(String.Format("Fingerprint samples needed: {0}", Enroller.FeaturesNeeded)); } private DPFP.Processing.Enrollment Enroller; } // Formulario de verificación oficial usando DPFP.Gui.Verification.VerificationControl public partial class VerificationForm : Form { public delegate void OnVerificationResultHandler(bool success, int userId); public event OnVerificationResultHandler OnVerificationResult; private Dictionary UserTemplates; private DPFP.Gui.Verification.VerificationControl VerificationControl; private Button CloseButton; private Label lblPrompt; private bool verificationCompleted = false; public VerificationForm() { UserTemplates = new Dictionary(); InitializeComponent(); LoadAllTemplates(); } private void InitializeComponent() { this.VerificationControl = new DPFP.Gui.Verification.VerificationControl(); this.CloseButton = new Button(); this.lblPrompt = new Label(); this.SuspendLayout(); // CloseButton this.CloseButton.Anchor = ((AnchorStyles)((AnchorStyles.Bottom | AnchorStyles.Right))); this.CloseButton.DialogResult = DialogResult.OK; this.CloseButton.Location = new Point(220, 75); this.CloseButton.Name = "CloseButton"; this.CloseButton.Size = new Size(75, 23); this.CloseButton.TabIndex = 2; this.CloseButton.Text = "Close"; this.CloseButton.UseVisualStyleBackColor = true; // lblPrompt this.lblPrompt.Anchor = ((AnchorStyles)((((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left) | AnchorStyles.Right))); this.lblPrompt.Location = new Point(67, 13); this.lblPrompt.Name = "lblPrompt"; this.lblPrompt.Size = new Size(228, 43); this.lblPrompt.TabIndex = 3; this.lblPrompt.Text = "Using the fingerprint reader, scan your fingerprint."; // VerificationControl this.VerificationControl.Active = true; this.VerificationControl.AutoSizeMode = AutoSizeMode.GrowAndShrink; this.VerificationControl.Location = new Point(13, 13); this.VerificationControl.Name = "VerificationControl"; this.VerificationControl.ReaderSerialNumber = "00000000-0000-0000-0000-000000000000"; this.VerificationControl.Size = new Size(48, 47); this.VerificationControl.TabIndex = 4; this.VerificationControl.OnComplete += new DPFP.Gui.Verification.VerificationControl._OnComplete(this.OnComplete); // VerificationForm this.AcceptButton = this.CloseButton; this.AutoScaleDimensions = new SizeF(6F, 13F); this.AutoScaleMode = AutoScaleMode.Font; this.CancelButton = this.CloseButton; this.ClientSize = new Size(307, 110); this.Controls.Add(this.VerificationControl); this.Controls.Add(this.lblPrompt); this.Controls.Add(this.CloseButton); this.FormBorderStyle = FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "VerificationForm"; this.StartPosition = FormStartPosition.CenterScreen; this.Text = "Fingerprint Verification"; this.ResumeLayout(false); } private void LoadAllTemplates() { try { Console.WriteLine("[VERIFY] Loading user templates from database Base64..."); UserTemplates.Clear(); // Hacer llamada HTTP para obtener templates desde la base de datos using (var client = new System.Net.Http.HttpClient()) { var response = client.PostAsync(Program.ApiUrl, new System.Net.Http.StringContent( "{\"tipo\":\"getUsuariosConHuella\"}", System.Text.Encoding.UTF8, "application/json")).Result; if (response.IsSuccessStatusCode) { var jsonResult = response.Content.ReadAsStringAsync().Result; // Parse JSON simple (sin usar bibliotecas externas) var usuarios = ParseUsuariosJson(jsonResult); foreach (var usuario in usuarios) { if (!string.IsNullOrEmpty(usuario.Value)) { try { // Convertir Base64 a template byte[] templateBytes = Convert.FromBase64String(usuario.Value); DPFP.Template template = new DPFP.Template(); using (var ms = new System.IO.MemoryStream(templateBytes)) { template.DeSerialize(ms); UserTemplates[usuario.Key] = template; Console.WriteLine("[VERIFY] Loaded template from DB for user " + usuario.Key); } } catch (Exception ex) { Console.WriteLine("[ERROR] Error loading template from DB for user " + usuario.Key + ": " + ex.Message); } } } } else { Console.WriteLine("[ERROR] Failed to load templates from database: " + response.StatusCode); } } Console.WriteLine("[VERIFY] Total templates loaded from DB: " + UserTemplates.Count); } catch (Exception ex) { Console.WriteLine("[ERROR] Error loading templates from database: " + ex.Message); // Fallback: cargar desde archivos .fpt si falla la base de datos LoadTemplatesFromFiles(); } } private Dictionary ParseUsuariosJson(string json) { var usuarios = new Dictionary(); try { // Parse JSON simple para obtener usuarios con huellas var dataStart = json.IndexOf("\"data\":"); if (dataStart > -1) { var arrayStart = json.IndexOf("[", dataStart); var arrayEnd = json.LastIndexOf("]"); if (arrayStart > -1 && arrayEnd > arrayStart) { var dataSection = json.Substring(arrayStart + 1, arrayEnd - arrayStart - 1); var userObjects = dataSection.Split(new string[] { "},{" }, StringSplitOptions.RemoveEmptyEntries); foreach (var userObj in userObjects) { var cleanObj = userObj.Replace("{", "").Replace("}", ""); int userId = 0; string base64Template = ""; // Extraer idUsuario var idMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"idUsuario\":\"?([^,\"]+)\"?"); if (idMatch.Success && int.TryParse(idMatch.Groups[1].Value, out userId)) { // Extraer fingerprint_base64 var base64Match = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"fingerprint_base64\":\"([^\"]+)\""); if (base64Match.Success) { base64Template = base64Match.Groups[1].Value; // Decodificar escapes JSON comunes base64Template = base64Template.Replace("\\/", "/"); usuarios[userId] = base64Template; } } } } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error parsing usuarios JSON: " + ex.Message); } return usuarios; } private void LoadTemplatesFromFiles() { try { Console.WriteLine("[VERIFY] Fallback: Loading templates from .fpt files..."); string templatesDir = Path.Combine(Application.StartupPath, "..", "..", "database", "fingerprints"); if (Directory.Exists(templatesDir)) { string[] files = Directory.GetFiles(templatesDir, "*.fpt"); Console.WriteLine("[VERIFY] Found " + files.Length + " template files"); foreach (string file in files) { try { string fileName = Path.GetFileNameWithoutExtension(file); int userId; if (int.TryParse(fileName, out userId)) { using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { DPFP.Template template = new DPFP.Template(); template.DeSerialize(fs); UserTemplates[userId] = template; Console.WriteLine("[VERIFY] Loaded template from file for user " + userId); } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error loading template file " + file + ": " + ex.Message); } } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error in fallback template loading: " + ex.Message); } } public void OnComplete(object Control, DPFP.FeatureSet FeatureSet, ref DPFP.Gui.EventHandlerStatus Status) { if (verificationCompleted) return; DPFP.Verification.Verification ver = new DPFP.Verification.Verification(); DPFP.Verification.Verification.Result res = new DPFP.Verification.Verification.Result(); Console.WriteLine("[VERIFY] Processing fingerprint for verification..."); // Verificar contra todos los templates almacenados foreach (var kvp in UserTemplates) { if (kvp.Value != null) { // Comparar con el template específico ver.Verify(FeatureSet, kvp.Value, ref res); if (res.Verified) { Console.WriteLine("[VERIFY] ¡Verificado! Usuario: " + kvp.Key + " (FAR: " + res.FARAchieved + ")"); this.lblPrompt.Text = "¡Verificación exitosa! Usuario: " + kvp.Key; verificationCompleted = true; if (OnVerificationResult != null) OnVerificationResult.Invoke(true, kvp.Key); // Cerrar ventana después de un breve delay System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Interval = 2000; timer.Tick += (s, e) => { timer.Stop(); this.Close(); }; timer.Start(); return; } } } if (!res.Verified) { Status = DPFP.Gui.EventHandlerStatus.Failure; this.lblPrompt.Text = "Huella no reconocida. Intente nuevamente."; Console.WriteLine("[VERIFY] Huella no reconocida - intente nuevamente"); } } } // Servicio principal con HTTP API public partial class FingerprintService : Form { private static HttpListener listener; private static Thread listenerThread; private static bool isRunning = true; private static FingerprintService serviceInstance; public FingerprintService() { InitializeComponent(); serviceInstance = this; // Form oculto pero con handle creado this.WindowState = FormWindowState.Minimized; this.ShowInTaskbar = false; this.Visible = false; // Forzar creación del handle var handle = this.Handle; } private void InitializeComponent() { this.SuspendLayout(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(284, 261); this.Name = "FingerprintService"; this.Text = "DigitalPersona Service Direct"; this.ResumeLayout(false); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); Console.WriteLine("=== DigitalPersona Service Direct v1.0 ==="); Console.WriteLine("[INFO] Servicio con ventana de enrollment directa"); StartHttpServer(); Console.WriteLine("[INFO] Servicio listo - http://localhost:8585"); } private static void StartHttpServer() { try { listener = new HttpListener(); listener.Prefixes.Add("http://localhost:8585/"); listener.Start(); listenerThread = new Thread(HandleRequests); listenerThread.Start(); Console.WriteLine("[INFO] Servidor HTTP activo en http://localhost:8585"); } catch (Exception ex) { Console.WriteLine("[ERROR] Error iniciando servidor: " + ex.Message); } } private static void HandleRequests() { while (listener.IsListening && isRunning) { try { var context = listener.GetContext(); ThreadPool.QueueUserWorkItem(o => ProcessRequest(context)); } catch { } } } static void ProcessRequest(HttpListenerContext context) { string response = ""; try { // Configurar headers CORS antes de procesar context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type"); context.Response.ContentType = "application/json"; string path = context.Request.Url.LocalPath; if (context.Request.HttpMethod == "OPTIONS") { context.Response.StatusCode = 200; response = ""; } else if (path == "/status") { response = "{\"status\":\"ready\",\"device\":\"connected\",\"version\":\"direct-enrollment\"}"; } else if (path == "/" || path == "") { response = "{\"success\":true,\"message\":\"Service running\",\"version\":\"direct-enrollment\"}"; } else if (path == "/enroll" && context.Request.HttpMethod == "POST") { using (var reader = new StreamReader(context.Request.InputStream)) { string body = reader.ReadToEnd(); int userId = ExtractUserId(body); string dedo = ExtractStringField(body, "dedo"); Console.WriteLine("[ENROLL] Abriendo ventana de enrollment para usuario: " + userId + (dedo != null ? " dedo: " + dedo : "")); // Mostrar ventana de enrollment en el hilo principal serviceInstance.Invoke(new MethodInvoker(() => { EnrollmentForm enrollmentForm = new EnrollmentForm(userId, dedo); // Configurar ventana para aparecer en primer plano enrollmentForm.WindowState = FormWindowState.Normal; enrollmentForm.StartPosition = FormStartPosition.CenterScreen; enrollmentForm.OnTemplate += (template) => { if (template != null) { Console.WriteLine("[SUCCESS] Template capturado para usuario: " + userId); } }; // Evento Shown para forzar foco cuando se muestre enrollmentForm.Shown += (sender, e) => { WindowHelper.ForceWindowToFront(enrollmentForm); }; // Mostrar ventana enrollmentForm.Show(); // Forzar foco inmediatamente WindowHelper.ForceWindowToFront(enrollmentForm); })); response = "{\"success\":true,\"message\":\"Enrollment window opened\"}"; } } else if (path == "/verify" && context.Request.HttpMethod == "POST") { Console.WriteLine("[VERIFY] Abriendo ventana oficial de verificación1"); try { // Variables para recibir el resultado de la verificación bool verificationSuccess = false; int verifiedUserId = -1; bool verificationCompleted = false; var resetEvent = new System.Threading.ManualResetEvent(false); // Abrir ventana de verificación oficial en hilo UI var thread = new Thread(() => { VerificationForm verificationForm = new VerificationForm(); // Configurar ventana para aparecer en primer plano verificationForm.TopMost = true; verificationForm.WindowState = FormWindowState.Normal; verificationForm.StartPosition = FormStartPosition.CenterScreen; // Manejar el resultado de la verificación verificationForm.OnVerificationResult += (success, userId) => { verificationSuccess = success; verifiedUserId = userId; verificationCompleted = true; resetEvent.Set(); // Señalar que la verificación se completó Console.WriteLine("[VERIFY] Resultado recibido - Success: " + success + ", UserId: " + userId); }; // Manejar cuando se cierra la ventana sin resultado verificationForm.FormClosed += (sender, e) => { if (!verificationCompleted) { verificationCompleted = true; resetEvent.Set(); // Señalar que el proceso terminó Console.WriteLine("[VERIFY] Ventana cerrada sin resultado"); } }; // Evento Shown para forzar foco cuando se muestre verificationForm.Shown += (sender, e) => { WindowHelper.ForceWindowToFront(verificationForm); }; // Mostrar ventana modal en primer plano verificationForm.ShowDialog(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); Console.WriteLine("[INFO] Esperando resultado de verificación (máximo 30 segundos)..."); // Esperar hasta que la verificación se complete o timeout (30 segundos) bool completed = resetEvent.WaitOne(30000); if (completed && verificationSuccess && verifiedUserId > 0) { Console.WriteLine("[SUCCESS] Huella verificada para usuario: " + verifiedUserId); UpdateLastAuth(verifiedUserId); response = "{\"success\":true,\"userId\":" + verifiedUserId + ",\"message\":\"Verification successful\"}"; } else if (completed && verificationCompleted) { Console.WriteLine("[INFO] Verificación completada sin éxito"); response = "{\"success\":false,\"message\":\"Fingerprint not recognized or verification cancelled\"}"; } else { Console.WriteLine("[TIMEOUT] Timeout en verificación - cerrando ventana"); response = "{\"success\":false,\"message\":\"Verification timeout - please try again\"}"; } } catch (Exception ex) { Console.WriteLine("[ERROR] Error en verificación: " + ex.Message); response = "{\"success\":false,\"message\":\"Verification error: " + ex.Message + "\"}"; } } else if (path == "/enroll-candidato" && context.Request.HttpMethod == "POST") { using (var reader = new StreamReader(context.Request.InputStream)) { string body = reader.ReadToEnd(); int candidatoId = ExtractCandidatoId(body); Console.WriteLine("[ENROLL-CANDIDATO] Enrollment sincrono para candidato: " + candidatoId); string capturedTemplate = null; bool enrollmentDone = false; var enrollEvent = new System.Threading.ManualResetEvent(false); var thread = new Thread(() => { EnrollmentReturnForm enrollForm = new EnrollmentReturnForm(); enrollForm.TopMost = true; enrollForm.WindowState = FormWindowState.Normal; enrollForm.StartPosition = FormStartPosition.CenterScreen; enrollForm.OnTemplateReady += (base64Template) => { capturedTemplate = base64Template; enrollmentDone = true; enrollEvent.Set(); Console.WriteLine("[ENROLL-CANDIDATO] Template capturado para candidato: " + candidatoId); }; enrollForm.FormClosed += (sender, e) => { if (!enrollmentDone) { enrollmentDone = true; enrollEvent.Set(); Console.WriteLine("[ENROLL-CANDIDATO] Ventana cerrada sin template"); } }; enrollForm.Shown += (sender, e) => { WindowHelper.ForceWindowToFront(enrollForm); }; enrollForm.ShowDialog(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); Console.WriteLine("[ENROLL-CANDIDATO] Esperando captura de huella (max 60s)..."); bool completed = enrollEvent.WaitOne(60000); if (completed && !string.IsNullOrEmpty(capturedTemplate)) { response = "{\"success\":true,\"template\":\"" + capturedTemplate + "\",\"candidatoId\":" + candidatoId + "}"; } else { response = "{\"success\":false,\"message\":\"Enrollment cancelled or timeout\"}"; } } } else if (path == "/verify-candidato" && context.Request.HttpMethod == "POST") { Console.WriteLine("[VERIFY-CANDIDATO] Verificacion contra candidatos..."); try { bool verificationSuccess = false; int verifiedCandidatoId = -1; string verifiedNombre = ""; bool verificationCompleted = false; var resetEvent = new System.Threading.ManualResetEvent(false); // Cargar templates de candidatos desde la API var candidatoTemplates = LoadCandidatoTemplates(); if (candidatoTemplates.Count == 0) { response = "{\"success\":false,\"message\":\"No hay candidatos con huella registrada\"}"; } else { var thread = new Thread(() => { CandidatoVerificationForm verForm = new CandidatoVerificationForm(candidatoTemplates); verForm.TopMost = true; verForm.WindowState = FormWindowState.Normal; verForm.StartPosition = FormStartPosition.CenterScreen; verForm.OnVerificationResult += (success, candidatoId, nombre) => { verificationSuccess = success; verifiedCandidatoId = candidatoId; verifiedNombre = nombre; verificationCompleted = true; resetEvent.Set(); }; verForm.FormClosed += (sender, e) => { if (!verificationCompleted) { verificationCompleted = true; resetEvent.Set(); } }; verForm.Shown += (sender, e) => { WindowHelper.ForceWindowToFront(verForm); }; verForm.ShowDialog(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); bool completed = resetEvent.WaitOne(30000); if (completed && verificationSuccess && verifiedCandidatoId > 0) { response = "{\"success\":true,\"candidatoId\":" + verifiedCandidatoId + ",\"nombre\":\"" + verifiedNombre.Replace("\"", "\\\"") + "\",\"message\":\"Candidato verificado por huella\"}"; } else { response = "{\"success\":false,\"message\":\"Huella no reconocida o verificacion cancelada\"}"; } } } catch (Exception ex) { response = "{\"success\":false,\"message\":\"Error: " + ex.Message.Replace("\"", "\\\"") + "\"}"; } } else { context.Response.StatusCode = 404; response = "{\"error\":\"Not found\"}"; } } catch (Exception ex) { Console.WriteLine("[ERROR] Error en request: " + ex.Message); response = "{\"error\":\"" + ex.Message.Replace("\"", "\\\"") + "\"}"; context.Response.StatusCode = 500; } try { // Convertir respuesta a bytes byte[] buffer = Encoding.UTF8.GetBytes(response); // Establecer Content-Length ANTES de escribir context.Response.ContentLength64 = buffer.Length; // Escribir la respuesta if (buffer.Length > 0) { context.Response.OutputStream.Write(buffer, 0, buffer.Length); } // Cerrar el stream de salida context.Response.OutputStream.Close(); } catch (Exception ex) { Console.WriteLine("[ERROR] Error al enviar respuesta: " + ex.Message); try { context.Response.Close(); } catch { } } } private static int ExtractCandidatoId(string json) { try { int index = json.IndexOf("\"candidatoId\":"); if (index != -1) { int colonIndex = json.IndexOf(":", index); int start = colonIndex + 1; int end = json.IndexOfAny(new char[] { ',', '}' }, start); if (end == -1) end = json.Length; string idStr = json.Substring(start, end - start).Trim().Replace("\"", ""); return int.Parse(idStr); } } catch (Exception ex) { Console.WriteLine("[ERROR] Error extrayendo candidatoId: " + ex.Message); } return 0; } private static Dictionary> LoadCandidatoTemplates() { var templates = new Dictionary>(); try { Console.WriteLine("[CANDIDATOS] Cargando templates de candidatos..."); using (var client = new System.Net.Http.HttpClient()) { var response = client.GetAsync("http://localhost/ApiNUevosCandidatos/api/candidatos/con-huella").Result; if (response.IsSuccessStatusCode) { var jsonResult = response.Content.ReadAsStringAsync().Result; Console.WriteLine("[CANDIDATOS] Respuesta recibida, parseando..."); // Parse JSON para obtener candidatos con huella var dataStart = jsonResult.IndexOf("\"data\":"); if (dataStart > -1) { var arrayStart = jsonResult.IndexOf("[", dataStart); var arrayEnd = jsonResult.LastIndexOf("]"); if (arrayStart > -1 && arrayEnd > arrayStart) { var dataSection = jsonResult.Substring(arrayStart + 1, arrayEnd - arrayStart - 1); var objects = dataSection.Split(new string[] { "},{" }, StringSplitOptions.RemoveEmptyEntries); foreach (var obj in objects) { var cleanObj = obj.Replace("{", "").Replace("}", ""); // Extraer id var idMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"id\":\"?([^,\"]+)\"?"); // Extraer nombre + apellidos var nombreMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"nombre\":\"([^\"]+)\""); var apellidosMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"apellidos\":\"([^\"]+)\""); // Extraer fingerprint_template var fpMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"fingerprint_template\":\"([^\"]+)\""); if (idMatch.Success && fpMatch.Success) { int candidatoId = int.Parse(idMatch.Groups[1].Value); string nombre = (nombreMatch.Success ? nombreMatch.Groups[1].Value : "") + " " + (apellidosMatch.Success ? apellidosMatch.Groups[1].Value : ""); string base64Template = fpMatch.Groups[1].Value.Replace("\\/", "/"); try { byte[] templateBytes = Convert.FromBase64String(base64Template); DPFP.Template template = new DPFP.Template(); using (var ms = new System.IO.MemoryStream(templateBytes)) { template.DeSerialize(ms); templates[candidatoId] = new KeyValuePair(nombre.Trim(), template); Console.WriteLine("[CANDIDATOS] Template cargado para candidato " + candidatoId + ": " + nombre.Trim()); } } catch (Exception ex) { Console.WriteLine("[ERROR] Error cargando template candidato " + candidatoId + ": " + ex.Message); } } } } } } } Console.WriteLine("[CANDIDATOS] Total templates cargados: " + templates.Count); } catch (Exception ex) { Console.WriteLine("[ERROR] Error cargando templates de candidatos: " + ex.Message); } return templates; } private static int ExtractUserId(string json) { try { int index = json.IndexOf("\"userId\":"); if (index == -1) index = json.IndexOf("\"idUsuario\":"); if (index != -1) { int colonIndex = json.IndexOf(":", index); int start = colonIndex + 1; int end = json.IndexOfAny(new char[] { ',', '}' }, start); if (end == -1) end = json.Length; string userIdStr = json.Substring(start, end - start).Trim().Replace("\"", ""); return int.Parse(userIdStr); } } catch (Exception ex) { Console.WriteLine("[ERROR] Error extrayendo userId: " + ex.Message); } return 1; } private static string ExtractStringField(string json, string fieldName) { try { var match = System.Text.RegularExpressions.Regex.Match(json, "\"" + fieldName + "\":\"([^\"]+)\""); if (match.Success) { return match.Groups[1].Value; } } catch { } return null; } private static int PerformSilentVerification() { try { Console.WriteLine("[VERIFY] Iniciando verificación silenciosa..."); // Cargar templates desde la base de datos Dictionary templates = LoadTemplatesFromDatabase(); if (templates.Count == 0) { Console.WriteLine("[ERROR] No se encontraron templates en la base de datos"); return -1; } Console.WriteLine("[VERIFY] " + templates.Count + " templates cargados, iniciando captura silenciosa..."); // Captura directa sin ventana DPFP.Capture.Capture capturer = new DPFP.Capture.Capture(); DPFP.Sample sample = null; bool captureComplete = false; var captureEvent = new System.Threading.ManualResetEvent(false); // Event handler para captura capturer.EventHandler = new DirectCaptureHandler((capturedSample) => { sample = capturedSample; captureComplete = true; captureEvent.Set(); }); // Iniciar captura capturer.StartCapture(); Console.WriteLine("[VERIFY] Lector activo - esperando huella (10 segundos)..."); // Esperar captura con timeout reducido (10 segundos para ser más rápido) bool captured = captureEvent.WaitOne(10000); capturer.StopCapture(); if (!captured || sample == null) { Console.WriteLine("[TIMEOUT] No se detectó huella en 10 segundos"); return -1; } Console.WriteLine("[VERIFY] Huella capturada, procesando..."); // Extraer features para verificación DPFP.Processing.FeatureExtraction extractor = new DPFP.Processing.FeatureExtraction(); DPFP.Capture.CaptureFeedback feedback = DPFP.Capture.CaptureFeedback.None; DPFP.FeatureSet features = new DPFP.FeatureSet(); extractor.CreateFeatureSet(sample, DPFP.Processing.DataPurpose.Verification, ref feedback, ref features); if (feedback != DPFP.Capture.CaptureFeedback.Good) { Console.WriteLine("[ERROR] Calidad de huella insuficiente: " + feedback.ToString()); return -1; } // Verificar contra todos los templates DPFP.Verification.Verification verification = new DPFP.Verification.Verification(); foreach (var kvp in templates) { int userId = kvp.Key; DPFP.Template template = kvp.Value; DPFP.Verification.Verification.Result result = new DPFP.Verification.Verification.Result(); verification.Verify(features, template, ref result); if (result.Verified) { Console.WriteLine("[SUCCESS] ✓ Huella verificada para usuario ID: " + userId); return userId; } } Console.WriteLine("[FAILED] Huella no coincide con ningún usuario registrado"); return -1; } catch (Exception ex) { Console.WriteLine("[ERROR] Error en verificación silenciosa: " + ex.Message); return -1; } } private static Dictionary LoadTemplatesFromDatabase() { var templates = new Dictionary(); try { Console.WriteLine("[DB] Cargando templates desde base de datos..."); using (var client = new System.Net.WebClient()) { client.Headers["Content-Type"] = "application/json"; string apiUrl = Program.ApiUrl; string jsonData = "{\"tipo\":\"getUsuariosConHuella\"}"; string response = client.UploadString(apiUrl, "POST", jsonData); // Parse simple del JSON var usuarios = ParseUsuariosFromJson(response); foreach (var usuario in usuarios) { if (!string.IsNullOrEmpty(usuario.Value)) { try { byte[] templateBytes = Convert.FromBase64String(usuario.Value); DPFP.Template template = new DPFP.Template(); using (var ms = new System.IO.MemoryStream(templateBytes)) { template.DeSerialize(ms); templates[usuario.Key] = template; } } catch (Exception ex) { Console.WriteLine("[ERROR] Error cargando template para usuario " + usuario.Key + ": " + ex.Message); } } } } Console.WriteLine("[DB] Templates cargados: " + templates.Count); } catch (Exception ex) { Console.WriteLine("[ERROR] Error cargando templates desde DB: " + ex.Message); } return templates; } private static Dictionary ParseUsuariosFromJson(string json) { var usuarios = new Dictionary(); try { var dataStart = json.IndexOf("\"data\":"); if (dataStart > -1) { var arrayStart = json.IndexOf("[", dataStart); var arrayEnd = json.LastIndexOf("]"); if (arrayStart > -1 && arrayEnd > arrayStart) { var dataSection = json.Substring(arrayStart + 1, arrayEnd - arrayStart - 1); var userObjects = dataSection.Split(new string[] { "},{" }, StringSplitOptions.RemoveEmptyEntries); foreach (var userObj in userObjects) { var cleanObj = userObj.Replace("{", "").Replace("}", ""); int userId = 0; string base64Template = ""; var idMatch = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"idUsuario\":\"?([^,\"]+)\"?"); if (idMatch.Success && int.TryParse(idMatch.Groups[1].Value, out userId)) { var base64Match = System.Text.RegularExpressions.Regex.Match(cleanObj, "\"fingerprint_base64\":\"([^\"]+)\""); if (base64Match.Success) { base64Template = base64Match.Groups[1].Value.Replace("\\/", "/"); usuarios[userId] = base64Template; } } } } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error parseando JSON: " + ex.Message); } return usuarios; } private static int PerformDirectVerification() { // Método legacy - redirigir a la versión silenciosa return PerformSilentVerification(); } private static Dictionary LoadAllUserTemplates() { Dictionary templates = new Dictionary(); try { string templatesDir = Path.Combine(Application.StartupPath, "..", "..", "database", "fingerprints"); if (!Directory.Exists(templatesDir)) { Console.WriteLine("[ERROR] Directorio de templates no encontrado: " + templatesDir); return templates; } string[] files = Directory.GetFiles(templatesDir, "*.fpt"); Console.WriteLine("[INFO] Encontrados " + files.Length + " archivos de templates"); foreach (string file in files) { try { string fileName = Path.GetFileNameWithoutExtension(file); int userId; if (int.TryParse(fileName, out userId)) { using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { DPFP.Template template = new DPFP.Template(); template.DeSerialize(fs); templates[userId] = template; Console.WriteLine("[INFO] Template cargado para usuario " + userId); } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error cargando template " + file + ": " + ex.Message); } } } catch (Exception ex) { Console.WriteLine("[ERROR] Error general cargando templates: " + ex.Message); } return templates; } private static void UpdateLastAuth(int userId) { try { using (var client = new System.Net.WebClient()) { client.Headers["Content-Type"] = "application/json"; string apiUrl = Program.ApiUrl; string jsonData = string.Format("{{\"tipo\":\"updateLastAuth\",\"idUsuario\":{0}}}", userId); string response = client.UploadString(apiUrl, "POST", jsonData); Console.WriteLine("[AUTH] Last auth updated for user " + userId + ": " + response); } } catch (Exception ex) { Console.WriteLine("[ERROR] Error updating last auth: " + ex.Message); } } } // Handler directo para captura sin formularios public class DirectCaptureHandler : DPFP.Capture.EventHandler { private Action OnSampleCaptured; public DirectCaptureHandler(Action onSampleCaptured) { OnSampleCaptured = onSampleCaptured; } public void OnComplete(object Capture, string ReaderSerialNumber, DPFP.Sample Sample) { Console.WriteLine("[CAPTURE] Huella capturada, procesando..."); if (OnSampleCaptured != null) OnSampleCaptured(Sample); } public void OnFingerGone(object Capture, string ReaderSerialNumber) { Console.WriteLine("[CAPTURE] Dedo retirado del lector"); } public void OnFingerTouch(object Capture, string ReaderSerialNumber) { Console.WriteLine("[CAPTURE] Dedo detectado en el lector"); } public void OnReaderConnect(object Capture, string ReaderSerialNumber) { Console.WriteLine("[CAPTURE] Lector conectado: " + ReaderSerialNumber); } public void OnReaderDisconnect(object Capture, string ReaderSerialNumber) { Console.WriteLine("[CAPTURE] Lector desconectado: " + ReaderSerialNumber); } public void OnSampleQuality(object Capture, string ReaderSerialNumber, DPFP.Capture.CaptureFeedback CaptureFeedback) { if (CaptureFeedback == DPFP.Capture.CaptureFeedback.Good) Console.WriteLine("[CAPTURE] Calidad de huella: Buena"); else Console.WriteLine("[CAPTURE] Calidad de huella: Insuficiente - " + CaptureFeedback.ToString()); } } // Formulario de enrollment que retorna el template Base64 (no guarda en BD) public class EnrollmentReturnForm : CaptureForm { public delegate void OnTemplateReadyHandler(string base64Template); public event OnTemplateReadyHandler OnTemplateReady; private DPFP.Processing.Enrollment Enroller; public EnrollmentReturnForm() { } protected override void Init() { base.Init(); base.Text = "Captura de Huella - Candidato"; Enroller = new DPFP.Processing.Enrollment(); UpdateStatus(); } protected override void Process(DPFP.Sample Sample) { base.Process(Sample); DPFP.FeatureSet features = ExtractFeatures(Sample, DPFP.Processing.DataPurpose.Enrollment); if (features != null) { try { MakeReport("Muestra de huella capturada."); Enroller.AddFeatures(features); } finally { UpdateStatus(); switch (Enroller.TemplateStatus) { case DPFP.Processing.Enrollment.Status.Ready: string base64 = ConvertTemplateToBase64(Enroller.Template); SetPrompt("Huella capturada exitosamente!"); Stop(); if (OnTemplateReady != null && base64 != null) OnTemplateReady.Invoke(base64); System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Interval = 1500; timer.Tick += (s, e) => { timer.Stop(); this.Close(); }; timer.Start(); break; case DPFP.Processing.Enrollment.Status.Failed: Enroller.Clear(); Stop(); UpdateStatus(); Start(); break; } } } } private string ConvertTemplateToBase64(DPFP.Template template) { try { using (MemoryStream ms = new MemoryStream()) { template.Serialize(ms); return Convert.ToBase64String(ms.ToArray()); } } catch (Exception ex) { MakeReport("Error convirtiendo template: " + ex.Message); return null; } } private void UpdateStatus() { SetStatus(String.Format("Muestras necesarias: {0}", Enroller.FeaturesNeeded)); } } // Formulario de verificacion contra candidatos public partial class CandidatoVerificationForm : Form { public delegate void OnVerificationResultHandler(bool success, int candidatoId, string nombre); public event OnVerificationResultHandler OnVerificationResult; private Dictionary> CandidatoTemplates; private DPFP.Gui.Verification.VerificationControl VerificationControl; private Button CloseButton; private Label lblPrompt; private bool verificationCompleted = false; public CandidatoVerificationForm(Dictionary> templates) { CandidatoTemplates = templates; InitializeComponent(); } private void InitializeComponent() { this.VerificationControl = new DPFP.Gui.Verification.VerificationControl(); this.CloseButton = new Button(); this.lblPrompt = new Label(); this.SuspendLayout(); this.CloseButton.Anchor = ((AnchorStyles)((AnchorStyles.Bottom | AnchorStyles.Right))); this.CloseButton.DialogResult = DialogResult.OK; this.CloseButton.Location = new Point(220, 75); this.CloseButton.Size = new Size(75, 23); this.CloseButton.Text = "Cerrar"; this.lblPrompt.Location = new Point(67, 13); this.lblPrompt.Size = new Size(228, 43); this.lblPrompt.Text = "Coloca tu dedo en el lector para verificar."; this.VerificationControl.Active = true; this.VerificationControl.AutoSizeMode = AutoSizeMode.GrowAndShrink; this.VerificationControl.Location = new Point(13, 13); this.VerificationControl.Size = new Size(48, 47); this.VerificationControl.OnComplete += new DPFP.Gui.Verification.VerificationControl._OnComplete(this.OnComplete); this.AcceptButton = this.CloseButton; this.ClientSize = new Size(307, 110); this.Controls.Add(this.VerificationControl); this.Controls.Add(this.lblPrompt); this.Controls.Add(this.CloseButton); this.FormBorderStyle = FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; this.Text = "Verificacion de Huella - Candidatos"; this.StartPosition = FormStartPosition.CenterScreen; this.ResumeLayout(false); } public void OnComplete(object Control, DPFP.FeatureSet FeatureSet, ref DPFP.Gui.EventHandlerStatus Status) { if (verificationCompleted) return; DPFP.Verification.Verification ver = new DPFP.Verification.Verification(); DPFP.Verification.Verification.Result res = new DPFP.Verification.Verification.Result(); foreach (var kvp in CandidatoTemplates) { if (kvp.Value.Value != null) { ver.Verify(FeatureSet, kvp.Value.Value, ref res); if (res.Verified) { int candidatoId = kvp.Key; string nombre = kvp.Value.Key; Console.WriteLine("[VERIFY-CANDIDATO] Verificado! Candidato: " + candidatoId + " - " + nombre); this.lblPrompt.Text = "Verificado: " + nombre; verificationCompleted = true; if (OnVerificationResult != null) OnVerificationResult.Invoke(true, candidatoId, nombre); System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); timer.Interval = 2000; timer.Tick += (s, e) => { timer.Stop(); this.Close(); }; timer.Start(); return; } } } Status = DPFP.Gui.EventHandlerStatus.Failure; this.lblPrompt.Text = "Huella no reconocida. Intenta de nuevo."; } } // Programa principal class Program { public static string ApiUrl = "http://127.0.0.1/api_pos/reportes/funciones_php/api.php"; [STAThread] static void Main(string[] args) { // Aceptar URL del API como primer argumento if (args.Length > 0 && !string.IsNullOrEmpty(args[0])) { ApiUrl = args[0]; } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Console.WriteLine("=== DigitalPersona Service Direct v1.0 ==="); Console.WriteLine("[INFO] API URL: " + ApiUrl); Console.WriteLine("[INFO] Abrirá ventana de enrollment directa como en los ejemplos oficiales"); FingerprintService form = new FingerprintService(); Application.Run(form); } } }