Biblioteca COM para manejo de e-CF DGII • Resúmenes • Envíos • QR Oficial
Desarrollada por: Natanael Sanchez M.
ECFDllVB6 es una biblioteca COM desarrollada en C# (.NET Framework 4.x), diseñada para ser utilizada desde Delphi, VB6, VB.NET, C++ y cualquier lenguaje compatible COM.
Permite procesar comprobantes electrónicos DGII (e-CF), generar resúmenes RFCE, enviar XML firmados y generar códigos QR oficiales.
1. Copie los archivos en una carpeta segura.
2. Abra una consola como Administrador.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe "ECFDllVB6.dll" /codebase /tlb
3. Importe la Type Library en Delphi:
Component → Import Component → Import a Type Library
Firmar Documento XML.
Generar el XML RFCE dinámico a partir de un e-CF firmado.
Guarda texto UTF-8 sin BOM.
Guarda texto UTF-8 sin BOM.
Genera la URL oficial DGII para el QR.
Descarga y guarda el QR como imagen PNG.
Extrae los primeros 6 caracteres del SignatureValue.
Extrae Fecha y Hora de la Firma XML.
Envía un XML firmado a DGII usando multipart/form-data.
Obtener respuesta de la DGII:
-Success -TrackId -Codigo -Estado -Rnc -Encf -SecuenciaUtilizada -FechaRecepcion -CodigoMensaje -Mensaje -Qr
Ejemplo equivalente en Oracle usando PL/SQL. Este ejemplo simula el comportamiento de la DLL utilizando herramientas nativas como UTL_HTTP y DBMS_CRYPTO.
SET SERVEROUTPUT ON;
DECLARE
-- Configuración
ruta_xml VARCHAR2(500) := '133398771E320000000875.xml';
url_envio VARCHAR2(500) := 'https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
token VARCHAR2(200) := 'token';
xml_content CLOB;
respuesta CLOB := EMPTY_CLOB();
codigo_seguridad VARCHAR2(200);
qr_text VARCHAR2(500);
req UTL_HTTP.req;
resp UTL_HTTP.resp;
buffer VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.PUT_LINE('--- INICIO PROCESO ---');
-------------------------------------------------
-- 1. Generar Resumen (Simulado)
-------------------------------------------------
xml_content := '<xml>Contenido de prueba</xml>';
DBMS_OUTPUT.PUT_LINE('Resumen generado correctamente');
-------------------------------------------------
-- 2. Guardar XML (Simulado)
-------------------------------------------------
DBMS_OUTPUT.PUT_LINE('XML guardado correctamente');
-------------------------------------------------
-- 3. Enviar XML (HTTP POST)
-------------------------------------------------
req := UTL_HTTP.begin_request(url_envio, 'POST');
UTL_HTTP.set_header(req, 'Content-Type', 'application/xml');
UTL_HTTP.set_header(req, 'Authorization', 'Bearer ' || token);
UTL_HTTP.write_text(req, xml_content);
resp := UTL_HTTP.get_response(req);
BEGIN
LOOP
UTL_HTTP.read_text(resp, buffer);
respuesta := respuesta || buffer;
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(resp);
END;
DBMS_OUTPUT.PUT_LINE('Resultado Envío: ' || SUBSTR(respuesta,1,200));
-------------------------------------------------
-- 4. Código de Seguridad
-------------------------------------------------
codigo_seguridad := RAWTOHEX(
DBMS_CRYPTO.HASH(
UTL_RAW.cast_to_raw(xml_content),
DBMS_CRYPTO.HASH_SH256
)
);
DBMS_OUTPUT.PUT_LINE('Código Seguridad: ' || codigo_seguridad);
-------------------------------------------------
-- 5. Generar QR (Simulado)
-------------------------------------------------
qr_text := 'QR|' || codigo_seguridad;
DBMS_OUTPUT.PUT_LINE('QR generado: ' || qr_text);
-------------------------------------------------
-- 6. Firma XML (Simulada)
-------------------------------------------------
DBMS_OUTPUT.PUT_LINE('XML firmado correctamente (simulado)');
DBMS_OUTPUT.PUT_LINE('--- FIN PROCESO ---');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR: ' || SQLERRM);
END;
/
Antes de ejecutar, debes habilitar permisos de red:
BEGIN
DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
host => '*',
ace => xs$ace_type(
privilege_list => xs$name_list('http'),
principal_name => 'TU_USUARIO',
principal_type => xs_acl.ptype_db
)
);
END;
/
Oracle no soporta de forma nativa:
Para producción se recomienda usar un API intermedio (C#, Node o Delphi) que consuma esta DLL y sea llamado desde Oracle.
unit HelloWorld;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.pngimage,
ECFDllVB6_TLB; // Librería de Facturación Electrónica
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
util : IUtilidades;
rutaXml : string;
rutaSalidaXml : string;
xmlName : string;
resultadoXml : string;
urlEnvio : string;
token : string;
resultadoEnvio : string;
codigoSeguridad : string;
qr : string;
qrPath : string;
xmlFirmado, rutaCert, passcert : string;
begin
// --- Configuración de parámetros ---
rutaCert := 'C:\Users\MeMeBigBoy\Desktop\certificado_identity.p12';
passCert := 'clave1234';
token := 'token';
urlEnvio := 'https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
xmlName := '133398771E320000000875.xml';
// Definición de rutas
rutaXml := 'C:\Users\MeMeBigBoy\Desktop\' + xmlName;
rutaSalidaXml := 'C:\Users\MeMeBigBoy\Desktop\resumen\' + xmlName;
qrPath := 'C:\Users\MeMeBigBoy\Desktop\qr\' + xmlName + '.png';
// --- Ejecución de procesos con la DLL ---
util := CoUtilidades.Create;
// 1. Generar y Guardar Resumen
resultadoXml := util.GenerarResumen(rutaXml);
ShowMessage('Resumen generado: ' + resultadoXml);
util.GuardarResumen(rutaSalidaXml, resultadoXml);
ShowMessage('XML guardado correctamente en: ' + rutaSalidaXml);
// 2. Enviar XML al Web Service
resultadoEnvio := util.EnviarXml(rutaXml, urlEnvio, token);
ShowMessage('Resultado Envío: ' + resultadoEnvio);
// 3. Obtener Código de Seguridad
codigoSeguridad := util.ObtenerCodigoSeguridad(rutaXml);
ShowMessage('Código de Seguridad: ' + codigoSeguridad);
// 4. Manejo de QR
qr := util.GenerarQr(rutaXml);
util.GenerarQRImagen(qr, qrPath);
// Cargar imagen en el formulario
Image1.Picture.LoadFromFile(qrPath);
Edit1.Text := qr;
// 5. Firma y Guardar XML
xmlName := '133398771E310000000004.xml';
rutaXml := 'C:\Users\MeMeBigBoy\Desktop\' + xmlName;
xmlFirmado := util.FirmarXml(rutaXml, rutaCert, passCert);
rutaXml := 'C:\Users\MeMeBigBoy\Desktop\firmados\' + xmlName;
util.GuardarXml(rutaXml, xmlFirmado);
end;
end.
unit ECFDllVB6_TLB;
// ************************************************************************ //
// WARNING
// -------
// The types declared in this file were generated from data read from a
// Type Library. If this type library is explicitly or indirectly (via
// another type library referring to this type library) re-imported, or the
// 'Refresh' command of the Type Library Editor activated while editing the
// Type Library, the contents of this file will be regenerated and all
// manual modifications will be lost.
// ************************************************************************ //
// $Rev: 98336 $
// File generated on 1/1/2026 7:01:37 PM from Type Library described below.
// ************************************************************************ //
// Type Lib: C:\Windows\SysWow64\ECFDllVB6.tlb (1)
// LIBID: {707DA5C4-0A53-33D5-8CD3-CDB703374C0E}
// LCID: 0
// Helpfile:
// HelpString:
// DepndLst:
// (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
// (2) v2.4 mscorlib, (C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb)
// SYS_KIND: SYS_WIN32
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
{$ALIGN 4}
interface
uses Winapi.Windows, mscorlib_TLB, System.Classes, System.Variants, System.Win.StdVCL, Vcl.Graphics, Vcl.OleServer, Winapi.ActiveX;
// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
// Type Libraries : LIBID_xxxx
// CoClasses : CLASS_xxxx
// DISPInterfaces : DIID_xxxx
// Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
// TypeLibrary Major and minor versions
ECFDllVB6MajorVersion = 1;
ECFDllVB6MinorVersion = 0;
LIBID_ECFDllVB6: TGUID = '{707DA5C4-0A53-33D5-8CD3-CDB703374C0E}';
DIID_IUtilidades: TGUID = '{E2F6C6F2-2C2A-4A9C-9F89-111111111111}';
CLASS_Utilidades: TGUID = '{A9B4FDC4-9C3F-4B8E-8A77-222222222222}';
type
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IUtilidades = dispinterface;
// *********************************************************************//
// Declaration of CoClasses defined in Type Library
// (NOTE: Here we map each CoClass to its Default Interface)
// *********************************************************************//
Utilidades = IUtilidades;
// *********************************************************************//
// DispIntf: IUtilidades
// Flags: (4096) Dispatchable
// GUID: {E2F6C6F2-2C2A-4A9C-9F89-111111111111}
// *********************************************************************//
IUtilidades = dispinterface
['{E2F6C6F2-2C2A-4A9C-9F89-111111111111}']
function GenerarResumen(const rutaXmlFirmado: WideString): WideString; dispid 1610743808;
function EnviarXml(const xmlFile: WideString; const urlEnvio: WideString;
const token: WideString): WideString; dispid 1610743809;
procedure GuardarResumen(const ruta: WideString; const texto: WideString); dispid 1610743810;
procedure GuardarXml(const ruta: WideString; const texto: WideString); dispid 1610743811;
function ObtenerCodigoSeguridad(const rutaXmlFirmado: WideString): WideString; dispid 1610743812;
function GenerarQr(const xmlInput: WideString): WideString; dispid 1610743813;
procedure GenerarQRImagen(const texto: WideString; const rutaImagenPng: WideString); dispid 1610743814;
function FirmarXml(const rutaXml: WideString; const rutaP12: WideString; const clave: WideString): WideString; dispid 1610743815;
end;
// *********************************************************************//
// The Class CoUtilidades provides a Create and CreateRemote method to
// create instances of the default interface IUtilidades exposed by
// the CoClass Utilidades. The functions are intended to be used by
// clients wishing to automate the CoClass objects exposed by the
// server of this typelibrary.
// *********************************************************************//
CoUtilidades = class
class function Create: IUtilidades;
class function CreateRemote(const MachineName: string): IUtilidades;
end;
implementation
uses System.Win.ComObj;
class function CoUtilidades.Create: IUtilidades;
begin
Result := CreateComObject(CLASS_Utilidades) as IUtilidades;
end;
class function CoUtilidades.CreateRemote(const MachineName: string): IUtilidades;
begin
Result := CreateRemoteComObject(MachineName, CLASS_Utilidades) as IUtilidades;
end;
end.
' Ejemplo de uso en VB6
Option Explicit
Private Sub Command1_Click()
' Declaración de variables (utilizando el objeto de la DLL)
Dim util As ECFDllVB6.Utilidades
Dim rutaXml As String
Dim rutaSalidaXml As String
Dim xmlName As String
Dim resultadoXml As String
Dim urlEnvio As String
Dim token As String
Dim resultadoEnvio As String
Dim codigoSeguridad As String
Dim qr As String
Dim qrPath As String
Dim cert As String
Dim pass As String
Dim xmlFirmado As String
Dim rutaXmlFirmado As String
Dim respuestaDGII As Object
cert = "C:\Users\MeMeBigBoy\Desktop\certificado_identity.p12"
pass = "clave1234"
On Error GoTo ErrorHandler
' --- Configuración de parámetros ---
token = "token"
urlEnvio = "https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
xmlName = "133398771E320000000875.xml"
' Definición de rutas (ajusta según tu entorno)
rutaXml = "C:\Users\MeMeBigBoy\Desktop\" & xmlName
rutaSalidaXml = "C:\Users\MeMeBigBoy\Desktop\resumen\" & xmlName
qrPath = "C:\Users\MeMeBigBoy\Desktop\qr\" & xmlName & ".png"
rutaXmlFirmado = "C:\Users\MeMeBigBoy\Desktop\firmados\" & xmlName
' --- Inicialización del objeto COM ---
Set util = New ECFDllVB6.Utilidades
' 1. Generar y Guardar Resumen
resultadoXml = util.GenerarResumen(rutaXml)
MsgBox "Resumen generado: " & resultadoXml, vbInformation
util.GuardarResumen rutaSalidaXml, resultadoXml
MsgBox "XML guardado correctamente en: " & rutaSalidaXml, vbInformation
' 2. Enviar XML al Web Service
resultadoEnvio = util.EnviarXml(rutaXml, urlEnvio, token)
MsgBox "Resultado Envío: " & resultadoEnvio, vbInformation
' Respuestad DGII
Set respuestaDGII = util.respuestaDGII(resultadoEnvio)
MsgBox (respuestaDGII.Estado & " / " & respuestaDGII.Trackid)
' 3. Obtener Código de Seguridad
codigoSeguridad = util.ObtenerCodigoSeguridad(rutaXml)
MsgBox "Código de Seguridad: " & codigoSeguridad, vbInformation
' 4. Manejo de QR
qr = util.GenerarQr(rutaXml)
util.GenerarQRImagen qr, qrPath
' Mostrar en los controles del formulario
' Nota: VB6 nativo no soporta PNG en el control Image estándar sin ayuda de GDI+
' pero si la DLL genera el archivo, intentamos cargarlo:
On Error Resume Next
Set Image1.Picture = LoadPicture(qrPath)
On Error GoTo ErrorHandler
Text1.Text = qr
' 5. Firmar y Guardar XML
xmlFirmado = u.FirmarXml(rutaXml, cert, pass)
u.GuardarXml rutaXmlFirmado, xmlFirmado
Debug.Print xmlFirmado
' Limpieza
Set util = Nothing
Exit Sub
ErrorHandler:
MsgBox "Error: " & Err.Description, vbCritical, "Error de Ejecución"
Set util = Nothing
End Sub
using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
// Asegúrate de agregar la referencia COM para que este namespace esté disponible
using ECFDllVB6;
namespace ProyectoFacturacion
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnProcesar_Click(object sender, EventArgs e)
{
// Declaración de variables
// En C#, la interfaz IUtilidades suele ser expuesta por la clase Utilidades
Utilidades util;
string rutaXml;
string rutaSalidaXml;
string xmlName;
string resultadoXml;
string urlEnvio;
string token;
string resultadoEnvio;
string codigoSeguridad;
string qr;
string qrPath;
try
{
// --- Configuración de parámetros ---
token = "token";
urlEnvio = "https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
xmlName = "133398771E320000000875.xml";
// Definición de rutas (Usando @ para ignorar caracteres de escape)
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
rutaXml = Path.Combine(desktopPath, xmlName);
rutaSalidaXml = Path.Combine(desktopPath, "resumen", xmlName);
qrPath = Path.Combine(desktopPath, "qr", xmlName + ".png");
// --- Ejecución de procesos con la DLL ---
util = new Utilidades();
// 1. Generar y Guardar Resumen
resultadoXml = util.GenerarResumen(rutaXml);
MessageBox.Show("Resumen generado: " + resultadoXml, "Información", MessageBoxButtons.OK, MessageBoxIcon.Information);
util.GuardarResumen(rutaSalidaXml, resultadoXml);
MessageBox.Show("XML guardado correctamente en: " + rutaSalidaXml);
// 2. Enviar XML al Web Service
resultadoEnvio = util.EnviarXml(rutaXml, urlEnvio, token);
MessageBox.Show("Resultado Envío: " + resultadoEnvio);
// 3. Obtener Código de Seguridad
codigoSeguridad = util.ObtenerCodigoSeguridad(rutaXml);
MessageBox.Show("Código de Seguridad: " + codigoSeguridad);
// 4. Manejo de QR
qr = util.GenerarQr(rutaXml);
util.GenerarQRImagen(qr, qrPath);
// Cargar imagen en el PictureBox (Equivalente a TImage)
if (File.Exists(qrPath))
{
pictureBox1.Image = Image.FromFile(qrPath);
}
// Mostrar texto del QR en un TextBox (Equivalente a TEdit)
txtQr.Text = qr;
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error de ejecución", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
using System;
using System.IO;
// Asegúrate de haber agregado la referencia COM a ECFDllVB6
using ECFDllVB6;
namespace FacturacionConsola
{
class Program
{
static void Main(string[] args)
{
// --- Declaración de variables ---
Utilidades util;
string rutaXml;
string rutaSalidaXml;
string xmlName;
string resultadoXml;
string urlEnvio;
string token;
string resultadoEnvio;
string codigoSeguridad;
string qr;
string qrPath;
Console.WriteLine("========================================");
Console.WriteLine(" PROCESAMIENTO DE E-CF (CONSOLA) ");
Console.WriteLine("========================================\n");
try
{
// --- Configuración de parámetros ---
token = "token";
urlEnvio = "https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
xmlName = "133398771E320000000875.xml";
// Definición de rutas dinámicas al Escritorio
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
rutaXml = Path.Combine(desktopPath, xmlName);
rutaSalidaXml = Path.Combine(desktopPath, "resumen", xmlName);
qrPath = Path.Combine(desktopPath, "qr", xmlName + ".png");
// --- Inicialización del objeto COM ---
util = new Utilidades();
// 1. Generar Resumen
Console.WriteLine("-> Generando Resumen...");
resultadoXml = util.GenerarResumen(rutaXml);
Console.WriteLine("Resumen generado con éxito.");
// Guardar Resumen
util.GuardarResumen(rutaSalidaXml, resultadoXml);
Console.WriteLine($"Archivo guardado en: {rutaSalidaXml}");
// 2. Enviar XML
Console.WriteLine("\n-> Enviando XML a DGII...");
resultadoEnvio = util.EnviarXml(rutaXml, urlEnvio, token);
Console.WriteLine($"Resultado Envío: {resultadoEnvio}");
// 3. Obtener Código de Seguridad
codigoSeguridad = util.ObtenerCodigoSeguridad(rutaXml);
Console.WriteLine($"\n-> Código de Seguridad: {codigoSeguridad}");
// 4. Manejo de QR
Console.WriteLine("\n-> Generando QR...");
qr = util.GenerarQr(rutaXml);
util.GenerarQRImagen(qr, qrPath);
Console.WriteLine($"URL del QR: {qr}");
Console.WriteLine($"Imagen QR guardada en: {qrPath}");
Console.WriteLine("\n========================================");
Console.WriteLine(" PROCESO FINALIZADO CORRECTAMENTE ");
Console.WriteLine("========================================");
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"\n[ERROR]: {ex.Message}");
Console.ResetColor();
}
Console.WriteLine("\nPresione cualquier tecla para salir...");
Console.ReadKey();
}
}
}
using System;
using System.IO;
using ECFDllVB6;
namespace FacturacionBatch
{
class Program
{
static void Main(string[] args)
{
// Instancia de la DLL
Utilidades util = new Utilidades();
// --- Configuración de Parámetros ---
string token = "token";
string urlEnvio = "https://portalmultiprod.com/api/certecf/enviar/2dbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// Rutas principales
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string carpetaEntrada = Path.Combine(desktopPath, "Facturas_Para_Procesar");
string carpetaResumen = Path.Combine(desktopPath, "resumen");
string carpetaQR = Path.Combine(desktopPath, "qr");
// Crear carpetas si no existen
Directory.CreateDirectory(carpetaEntrada);
Directory.CreateDirectory(carpetaResumen);
Directory.CreateDirectory(carpetaQR);
Console.WriteLine("========================================");
Console.WriteLine(" BATCH PROCESSOR - e-CF DGII");
Console.WriteLine("========================================\n");
Console.WriteLine($"Buscando XMLs en: {carpetaEntrada}\n");
// Obtener todos los archivos XML de la carpeta de entrada
string[] archivos = Directory.GetFiles(carpetaEntrada, "*.xml");
if (archivos.Length == 0)
{
Console.WriteLine("No se encontraron archivos XML para procesar.");
}
else
{
foreach (string rutaXml in archivos)
{
string nombreArchivo = Path.GetFileName(rutaXml);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"--- Procesando: {nombreArchivo} ---");
Console.ResetColor();
try
{
// 1. Generar y Guardar Resumen
string resultadoXml = util.GenerarResumen(rutaXml);
string rutaSalidaXml = Path.Combine(carpetaResumen, nombreArchivo);
util.GuardarResumen(rutaSalidaXml, resultadoXml);
Console.WriteLine("[OK] Resumen generado y guardado.");
// 2. Enviar XML
string resultadoEnvio = util.EnviarXml(rutaXml, urlEnvio, token);
Console.WriteLine($"[OK] Envío DGII: {resultadoEnvio}");
// 3. Obtener Código de Seguridad
string codigoSeguridad = util.ObtenerCodigoSeguridad(rutaXml);
Console.WriteLine($"[OK] Código Seguridad: {codigoSeguridad}");
// 4. Generar Imagen QR
string qrUrl = util.GenerarQr(rutaXml);
string rutaImagenQr = Path.Combine(carpetaQR, nombreArchivo + ".png");
util.GenerarQRImagen(qrUrl, rutaImagenQr);
Console.WriteLine("[OK] Imagen QR generada.");
Console.WriteLine("----------------------------------------\n");
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"[ERROR] en {nombreArchivo}: {ex.Message}");
Console.ResetColor();
}
}
}
Console.WriteLine("========================================");
Console.WriteLine(" PROCESAMIENTO FINALIZADO");
Console.WriteLine("========================================");
Console.WriteLine("Presione cualquier tecla para salir...");
Console.ReadKey();
}
}
}