From f75517d8fb6e4d7e925f17d4fab663b88ce16927 Mon Sep 17 00:00:00 2001 From: Miguel Robiras Date: Mon, 6 Oct 2025 19:07:30 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Integraci=C3=B3n=20completa=20con=20API=20d?= =?UTF-8?q?e=20Atera:=20detecci=C3=B3n=20autom=C3=A1tica=20de=20soporte.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Añadida verificación automática al iniciar la aplicación. - Lectura del AgentId desde el registro de Windows. - Implementada búsqueda por hostname en la API de Atera (/api/v3/agents/machine/{hostname}). - Añadida validación por dirección MAC cuando existen hostnames duplicados. - Indicador visual de estado de soporte (verde/rojo) en el panel principal. - Depuración temporal con MessageBox para host local, API response y coincidencias. - Corrección de eventos duplicados en Form1.Designer.cs. - Limpieza de código y mejora de control de errores. - Versión estable funcional: muestra correctamente cliente Atera asociado cuando la MAC coincide. --- soporteKM/Form1.Designer.cs | 18 +- soporteKM/Form1.cs | 423 ++++++++++++++++----------- soporteKM/Properties/AssemblyInfo.cs | 4 +- soporteKM/packages.config | 4 + soporteKM/soporteKM.csproj | 4 + 5 files changed, 273 insertions(+), 180 deletions(-) create mode 100644 soporteKM/packages.config diff --git a/soporteKM/Form1.Designer.cs b/soporteKM/Form1.Designer.cs index df3cc3b..26a847a 100644 --- a/soporteKM/Form1.Designer.cs +++ b/soporteKM/Form1.Designer.cs @@ -55,7 +55,6 @@ private void InitializeComponent() this.labelSerial.TabIndex = 0; this.labelSerial.Text = "serial"; this.labelSerial.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.labelSerial.Click += new System.EventHandler(this.Label1_Click); // // btnSolicitarSoporte // @@ -65,13 +64,12 @@ private void InitializeComponent() this.btnSolicitarSoporte.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnSolicitarSoporte.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnSolicitarSoporte.ForeColor = System.Drawing.Color.White; - this.btnSolicitarSoporte.Location = new System.Drawing.Point(17, 243); + this.btnSolicitarSoporte.Location = new System.Drawing.Point(17, 286); this.btnSolicitarSoporte.Name = "btnSolicitarSoporte"; this.btnSolicitarSoporte.Size = new System.Drawing.Size(331, 32); this.btnSolicitarSoporte.TabIndex = 2; this.btnSolicitarSoporte.Text = "Espera a que se abra la aplicación de soporte."; this.btnSolicitarSoporte.UseVisualStyleBackColor = false; - this.btnSolicitarSoporte.Click += new System.EventHandler(this.BtnSolicitarSoporte_Click); // // labelversion // @@ -81,7 +79,6 @@ private void InitializeComponent() this.labelversion.Size = new System.Drawing.Size(52, 13); this.labelversion.TabIndex = 4; this.labelversion.Text = "lblVersion"; - this.labelversion.Click += new System.EventHandler(this.label1_Click_1); // // btnComprobarVersion // @@ -90,7 +87,7 @@ private void InitializeComponent() this.btnComprobarVersion.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnComprobarVersion.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnComprobarVersion.ForeColor = System.Drawing.Color.White; - this.btnComprobarVersion.Location = new System.Drawing.Point(17, 197); + this.btnComprobarVersion.Location = new System.Drawing.Point(17, 240); this.btnComprobarVersion.Margin = new System.Windows.Forms.Padding(0); this.btnComprobarVersion.Name = "btnComprobarVersion"; this.btnComprobarVersion.Padding = new System.Windows.Forms.Padding(3); @@ -111,7 +108,6 @@ private void InitializeComponent() this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureBox1.TabIndex = 6; this.pictureBox1.TabStop = false; - this.pictureBox1.Click += new System.EventHandler(this.PictureBox1_Click); // // label1 // @@ -125,7 +121,7 @@ private void InitializeComponent() // // progressBar1 // - this.progressBar1.Location = new System.Drawing.Point(17, 230); + this.progressBar1.Location = new System.Drawing.Point(17, 273); this.progressBar1.Margin = new System.Windows.Forms.Padding(0); this.progressBar1.Name = "progressBar1"; this.progressBar1.Size = new System.Drawing.Size(331, 5); @@ -142,7 +138,6 @@ private void InitializeComponent() this.labelHostname.TabIndex = 9; this.labelHostname.Text = "hostname"; this.labelHostname.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.labelHostname.Click += new System.EventHandler(this.LabelHostname_Click); // // label2 // @@ -153,14 +148,13 @@ private void InitializeComponent() this.label2.Size = new System.Drawing.Size(76, 17); this.label2.TabIndex = 10; this.label2.Text = "Hostname:"; - this.label2.Click += new System.EventHandler(this.label2_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(367, 287); + this.ClientSize = new System.Drawing.Size(367, 326); this.Controls.Add(this.label2); this.Controls.Add(this.labelHostname); this.Controls.Add(this.progressBar1); @@ -180,10 +174,6 @@ private void InitializeComponent() } - private void label1_Click_1(object sender, EventArgs e) - { - throw new NotImplementedException(); - } #endregion diff --git a/soporteKM/Form1.cs b/soporteKM/Form1.cs index 92365f2..8cc5370 100644 --- a/soporteKM/Form1.cs +++ b/soporteKM/Form1.cs @@ -1,7 +1,8 @@ -using System; +using Microsoft.Win32; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; using System.Configuration; -//using System.Collections.Generic; -//using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; @@ -9,94 +10,90 @@ using System.Linq; using System.Management; using System.Net; -//using System.Reflection.Emit; -//using System.Text; -//using System.Threading.Tasks; -using System.Windows.Forms; +using System.Net.Http; using System.Reflection; -//using static System.Windows.Forms.VisualStyles.VisualStyleElement; -using Microsoft.Win32; - +using System.Threading.Tasks; +using System.Windows.Forms; namespace soporteKM { public partial class Form1 : Form { - private readonly System.Windows.Forms.ProgressBar progressBar; + // Indicador de soporte + private Panel panelEstadoSoporte; + private Label labelEstadoSoporte; public Form1() { InitializeComponent(); - // Configuración para ocultar la barra de título + // Manejo global de errores no controlados + Application.ThreadException += (s, e) => + { + MessageBox.Show("Error inesperado: " + e.Exception.Message, + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + }; + ControlBox = true; - FormBorderStyle = FormBorderStyle.FixedSingle; // o cualquier otro estilo de borde que desees - MaximizeBox = false; // Deshabilitar el botón de maximizar + FormBorderStyle = FormBorderStyle.FixedSingle; + MaximizeBox = false; // Crear y configurar la barra de progreso progressBar = new System.Windows.Forms.ProgressBar { - Size = new Size(331, 5), // Ajustar el tamaño - Location = new Point(17, 230), // Ajustar las coordenadas + Size = new Size(331, 5), + Location = new Point(17, 230), Visible = false }; + // Crear panel indicador de estado + panelEstadoSoporte = new Panel + { + Size = new Size(20, 20), + Location = new Point(20, 190), + BackColor = Color.Gray, + BorderStyle = BorderStyle.FixedSingle + }; + labelEstadoSoporte = new Label + { + Text = "Verificando soporte...", + Location = new Point(50, 190), + AutoSize = true, + Font = new Font("Segoe UI", 9, FontStyle.Bold), + ForeColor = Color.Black + }; - - // Agregar el botón al formulario Controls.Add(btnComprobarVersion); - - // Agregar la barra de progreso al formulario Controls.Add(progressBar); + Controls.Add(panelEstadoSoporte); + Controls.Add(labelEstadoSoporte); - // Suscribir el evento Click del botón al método auxiliar + // Eventos btnComprobarVersion.Click += BtnComprobarVersion_Click; - - // Suscribir el evento Click del Label al método para copiar el serial labelSerial.Click += LabelSerial_Click; - - // Suscribir el evento Click del Label al método para copiar el nombre del host al portapapeles - //labelHostname.Click += LabelHostname_Click; - - + labelHostname.Click += LabelHostname_Click; } private void LabelSerial_Click(object sender, EventArgs e) { - // Copiar el serial al portapapeles cuando se hace clic en el Label Clipboard.SetText(labelSerial.Text); - MessageBox.Show("Serial copiado al portapapeles."); + MessageBox.Show("Número de serie copiado al portapapeles.", "Copiado", + MessageBoxButtons.OK, MessageBoxIcon.Information); } private void LabelHostname_Click(object sender, EventArgs e) { - - // Copiar el nombre del host al portapapeles cuando se hace clic en el Label Clipboard.SetText(labelHostname.Text); - MessageBox.Show("Nombre del host copiado al portapapeles."); - + MessageBox.Show("Nombre de host copiado al portapapeles.", "Copiado", + MessageBoxButtons.OK, MessageBoxIcon.Information); } - - private void Label1_Click(object sender, EventArgs e) - { - // Obtener el serial del ordenador - string serial = ObtenerSerialOrdenador(); - - - // Mostrar el serial en el Label - labelSerial.Text = serial; - } - - - private string ObtenerSerialOrdenador() { string serial = ""; - try { ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_BIOS"); @@ -105,115 +102,272 @@ private string ObtenerSerialOrdenador() foreach (ManagementObject obj in collection.Cast()) { serial = obj["SerialNumber"].ToString(); - break; // Solo obtenemos el primer resultado + break; } - - } catch (Exception ex) { - MessageBox.Show("Error al obtener el serial del ordenador: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("Error al obtener el número de serie: " + ex.Message, + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } - return serial; } private string ObtenerNombreHost() { - // Obtener y retornar el nombre del host del equipo - return System.Net.Dns.GetHostName(); + try + { + string host = System.Net.Dns.GetHostName().ToUpper(); - } + // Si tiene dominio (por ejemplo SRV-SSH00.DOMINIO.LOCAL), lo cortamos + if (host.Contains(".")) + host = host.Split('.')[0]; - private void Form1_Load(object sender, EventArgs e) + return host; + } + catch + { + return "DESCONOCIDO"; + } + } + private async void Form1_Load(object sender, EventArgs e) { - - // Verificar si .NET Framework 4.7.2 está instalado if (!VerificarNETFramework472()) { - // Si no está instalado, ejecutar el instalador incluido en la carpeta de la aplicación string netFrameworkInstallerPath = Environment.CurrentDirectory + "\\App\\ndp472-kb4054530-x86-x64-allos-enu.exe"; Process.Start(netFrameworkInstallerPath); - return; // No es necesario continuar cargando la aplicación + return; } - // Al cargar el formulario, comprueba si hay un archivo en la URL y ajusta la visibilidad del botón - string baseUrl = ConfigurationManager.AppSettings["UrlUpdate"]; // Declaramos la variable baseURL extraida del fichero de configuración - string installUrl = $"{baseUrl}/downloads/soporteKM/installsoporteKM.msi"; // Declaramos la ruta y nombre del fichero de instalación - string versionUrl = $"{baseUrl}/downloads/soporteKM/version"; // Declaramos la ruta y nombre del fichero que indica que version esta publicada + string baseUrl = ConfigurationManager.AppSettings["UrlUpdate"]; - - btnComprobarVersion.Visible = EsNuevaVersionDisponible(versionUrl) && ExisteArchivoEnURL(installUrl); + if (string.IsNullOrEmpty(baseUrl)) + { + MessageBox.Show("No se ha configurado la URL de actualización en App.config.", + "Configuración incompleta", MessageBoxButtons.OK, MessageBoxIcon.Warning); + baseUrl = "https://robirasdesign.com"; // fallback temporal + } + string installUrl = $"{baseUrl}/downloads/soporteKM/installsoporteKM.msi"; + string versionUrl = $"{baseUrl}/downloads/soporteKM/version"; - // Obtén la versión desde el archivo de configuración - string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + btnComprobarVersion.Visible = EsNuevaVersionDisponible(versionUrl) && ExisteArchivoEnURL(installUrl); - // Muestra la versión en el Label + string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); labelversion.Text = "v" + version; - // Obtener el serial del ordenador - string serial = ObtenerSerialOrdenador(); - - // Mostrar el serial en el Label - labelSerial.Text = serial; + labelSerial.Text = ObtenerSerialOrdenador(); + labelHostname.Text = ObtenerNombreHost(); - // Obtener el hostname del ordenador - string hostname = ObtenerNombreHost(); + // Verificar soporte Atera automáticamente - // Mostrar el hostname en el label - labelHostname.Text = hostname; + await VerificarSoporteAteraAsync(); - // Ruta completa al archivo ejecutable de TeamViewerQS.exe string teamViewerPath = Environment.CurrentDirectory + "\\App\\TeamViewerQS.exe"; - - // Verifica si el archivo existe antes de intentar ejecutarlo if (System.IO.File.Exists(teamViewerPath)) { - // Crea un proceso para ejecutar el programa externo Process.Start(teamViewerPath); } else { - MessageBox.Show("El programa TeamViewerQS.exe no se encuentra en la ubicación especificada.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("El programa TeamViewerQS.exe no se encuentra en la ubicación especificada.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private string ObtenerMacPrincipal() + { + try + { + var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); + foreach (var ni in interfaces) + { + if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && + ni.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) + { + return ni.GetPhysicalAddress().ToString(); // Ejemplo: "A1B2C3D4E5F6" + } + } + } + catch (Exception ex) + { + MessageBox.Show("Error al obtener MAC: " + ex.Message); } + return null; } - private void BtnSolicitarSoporte_Click(object sender, EventArgs e) + // 🔍 Verificación de soporte Atera + private async Task VerificarSoporteAteraAsync() { try { - // Ruta completa al archivo ejecutable de TeamViewerQS.exe - string teamViewerPath = Environment.CurrentDirectory +"\\App\\TeamViewerQS.exe"; + string hostname = ObtenerNombreHost(); + string macLocal = ObtenerMacPrincipal(); - // Verifica si el archivo existe antes de intentar ejecutarlo - if (System.IO.File.Exists(teamViewerPath)) + MessageBox.Show($"🖥 Hostname local: {hostname}\n🔌 MAC local: {macLocal}", "Debug Local"); + + if (string.IsNullOrEmpty(macLocal)) { - // Crea un proceso para ejecutar el programa externo - Process.Start(teamViewerPath); + MostrarEstado(false, "No en soporte (No se pudo obtener la MAC local)"); + return; } - else + + using (HttpClient client = new HttpClient()) { - MessageBox.Show("El programa TeamViewerQS.exe no se encuentra en la ubicación especificada.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + client.BaseAddress = new Uri("https://app.atera.com"); + client.DefaultRequestHeaders.Add("X-API-KEY", "5cc02a420cf54be6a511429cfb6be3da"); + + // ✅ Nueva llamada directa por nombre de máquina + var response = await client.GetAsync($"/api/v3/agents/machine/{hostname}"); + if (!response.IsSuccessStatusCode) + { + MostrarEstado(false, $"Error API Atera ({response.StatusCode})"); + return; + } + + string json = await response.Content.ReadAsStringAsync(); + dynamic result = JsonConvert.DeserializeObject(json); + + // Mostrar resultado bruto + MessageBox.Show($"Respuesta de Atera para {hostname}:\n{json}", "Debug API Atera"); + + if (result == null || result.items == null || ((IEnumerable)result.items).Count() == 0) + { + MostrarEstado(false, "No en soporte (no encontrado en Atera)"); + return; + } + + bool encontrado = false; + string cliente = ""; + + foreach (var agent in result.items) + { + var macs = agent.MacAddresses != null ? agent.MacAddresses : agent.MacAddress; + + if (macs != null) + { + MessageBox.Show($"Analizando MACs del agente:\n{string.Join(", ", macs)}", "Debug MAC Atera"); + + foreach (var mac in macs) + { + string macAtera = mac.ToString().Replace(":", "").Replace("-", "").ToUpper(); + string macLocalClean = macLocal.Replace(":", "").Replace("-", "").ToUpper(); + + if (macAtera == macLocalClean) + { + encontrado = true; + cliente = agent.CustomerName?.ToString() ?? "Cliente desconocido"; + MessageBox.Show($"✅ Coincidencia encontrada:\nCliente: {cliente}\nMAC Atera: {macAtera}\nMAC Local: {macLocalClean}", "Debug Coincidencia"); + break; + } + } + } + + if (encontrado) + break; + } + + if (encontrado) + MostrarEstado(true, $"🟢 En soporte ({cliente})"); + else + MostrarEstado(false, "🔴 No en soporte (MAC no coincide)"); } } catch (Exception ex) { - MessageBox.Show("Error al intentar ejecutar TeamViewer: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MostrarEstado(false, "Error: " + ex.Message); } } + + + + private string ObtenerDeviceGuidLocal() + { + try + { + string guid = null; + + // 🔹 Forzamos lectura de la clave en 64 bits + using (var baseKey64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)) + { + using (var key64 = baseKey64.OpenSubKey(@"SOFTWARE\ATERA Networks\AlphaAgent", false)) + { + if (key64 != null) + { + foreach (var valueName in key64.GetValueNames()) + Console.WriteLine($"[64bit] {valueName} = {key64.GetValue(valueName)}"); + + guid = key64.GetValue("AgentId") as string; + } + } + } + + // 🔹 Si no lo encuentra, busca en la vista de 32 bits + if (string.IsNullOrEmpty(guid)) + { + using (var baseKey32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) + { + using (var key32 = baseKey32.OpenSubKey(@"SOFTWARE\ATERA Networks\AlphaAgent", false)) + { + if (key32 != null) + { + foreach (var valueName in key32.GetValueNames()) + Console.WriteLine($"[32bit] {valueName} = {key32.GetValue(valueName)}"); + + guid = key32.GetValue("AgentId") as string; + } + } + } + } + + MessageBox.Show($"GUID local leído: {(guid ?? "NULO")}", "Debug Atera"); + + return guid; + } + catch (Exception ex) + { + MessageBox.Show($"Error leyendo registro: {ex.Message}"); + return null; + } + } + + + + private void MostrarEstado(bool enSoporte, string texto) + { + if (InvokeRequired) + { + Invoke(new Action(() => MostrarEstado(enSoporte, texto))); + return; + } + + labelEstadoSoporte.Text = texto; + if (enSoporte) + { + panelEstadoSoporte.BackColor = Color.LimeGreen; + labelEstadoSoporte.ForeColor = Color.Green; + } + else + { + panelEstadoSoporte.BackColor = Color.Red; + labelEstadoSoporte.ForeColor = Color.Red; + } + } + + // ----------------------------------------- + // Métodos auxiliares + // ----------------------------------------- + private bool ExisteArchivoEnURL(string url) { try { - // Intenta realizar una solicitud HEAD para verificar la existencia del archivo var request = (HttpWebRequest)WebRequest.Create(url); request.Method = "HEAD"; - using (var response = (HttpWebResponse)request.GetResponse()) { return response.StatusCode == HttpStatusCode.OK; @@ -221,18 +375,13 @@ private bool ExisteArchivoEnURL(string url) } catch (WebException) { - // Si ocurre una excepción, asumimos que el archivo no existe return false; } } private bool EsNuevaVersionDisponible(string versionUrl) { - // Ruta local del archivo "version" - //string downloadFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Downloads"; string rutaArchivoVersion = Path.Combine(Path.GetTempPath(), "version"); - - // Descargar el archivo "version" desde la URL try { using (WebClient client = new WebClient()) @@ -242,25 +391,16 @@ private bool EsNuevaVersionDisponible(string versionUrl) } catch (WebException) { - // Si ocurre una excepción, asumimos que el archivo "version" no existe return false; } - // Leer el contenido del archivo "version" string versionEnArchivo = File.ReadAllText(rutaArchivoVersion); - - // Obtener la versión actual de la aplicación Version versionActual = new Version(Application.ProductVersion); - - - // Obtener la versión del archivo "version" if (Version.TryParse(versionEnArchivo, out Version versionEnArchivoObjeto)) { - // Comparar las versiones return versionEnArchivoObjeto > versionActual; } - return false; } @@ -281,10 +421,16 @@ private bool VerificarNETFramework472() } } - private async void BtnComprobarVersion_Click(object sender, EventArgs e) { string baseUrl = ConfigurationManager.AppSettings["UrlUpdate"]; + if (string.IsNullOrEmpty(baseUrl)) + { + MessageBox.Show("No se ha configurado la URL de actualización.", "Error", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + string installUrl = $"{baseUrl}/downloads/soporteKM/installsoporteKM.msi"; string destino = Path.Combine(Path.GetTempPath(), "installsoporteKM.msi"); @@ -292,75 +438,24 @@ private async void BtnComprobarVersion_Click(object sender, EventArgs e) { using (WebClient client = new WebClient()) { - // Mostrar la barra de progreso progressBar.Visible = true; - - // Asociar el evento DownloadProgressChanged client.DownloadProgressChanged += WebClientDownloadProgressChanged; - - // Intenta descargar el archivo a la carpeta temporal await client.DownloadFileTaskAsync(new Uri(installUrl), destino); - - // Restablecer la visibilidad de la barra de progreso después de la descarga progressBar.Visible = false; - - // Si llega a este punto, la descarga fue exitosa - - // Ejecutar el archivo descargado Process.Start(destino); - - // Cerrar la aplicación actual Application.Exit(); } } - catch (WebException webEx) - { - // Si ocurre una excepción, significa que la descarga falló - if (webEx.Response is HttpWebResponse response) - { - if (response.StatusCode == HttpStatusCode.NotFound) - { - // El archivo no existe en la URL especificada - MessageBox.Show("No hay nuevas versiones disponibles.", "Información", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - else - { - // Mostrar detalles adicionales sobre la excepción específica de WebException - MessageBox.Show($" 1 Error al intentar comprobar la versión: {webEx.Message}\nDetalles: {webEx.StackTrace}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - else - { - // Mostrar detalles adicionales sobre la excepción específica de WebException - //MessageBox.Show($"2 Error al intentar comprobar la versión: {webEx.Message}\nDetalles: {webEx.StackTrace}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } catch (Exception ex) { - // Mostrar detalles adicionales sobre la excepción general - MessageBox.Show($"3 Error general al intentar comprobar la versión: {ex.Message}\nDetalles: {ex.StackTrace}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show($"Error al comprobar la versión: {ex.Message}", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } - } - - - - private void WebClientDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { - // Actualizar la barra de progreso durante la descarga progressBar.Value = e.ProgressPercentage; } - - private void PictureBox1_Click(object sender, EventArgs e) - { - - } - - private void label2_Click(object sender, EventArgs e) - { - - } } } diff --git a/soporteKM/Properties/AssemblyInfo.cs b/soporteKM/Properties/AssemblyInfo.cs index 432e995..3e23839 100644 --- a/soporteKM/Properties/AssemblyInfo.cs +++ b/soporteKM/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Puede especificar todos los valores o usar los valores predeterminados de número de compilación y de revisión // utilizando el carácter "*", como se muestra a continuación: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.3.2")] -[assembly: AssemblyFileVersion("1.0.3.2")] +[assembly: AssemblyVersion("1.0.4.0")] +[assembly: AssemblyFileVersion("1.0.4.0")] diff --git a/soporteKM/packages.config b/soporteKM/packages.config new file mode 100644 index 0000000..4b1297f --- /dev/null +++ b/soporteKM/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/soporteKM/soporteKM.csproj b/soporteKM/soporteKM.csproj index c86eb5e..295a177 100644 --- a/soporteKM/soporteKM.csproj +++ b/soporteKM/soporteKM.csproj @@ -51,6 +51,9 @@ icon_Konica.ico + + ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + @@ -89,6 +92,7 @@ Resources.resx True + SettingsSingleFileGenerator Settings.Designer.cs From 8eefaee94d07d9c24f8a3e400c86bd7d428467cb Mon Sep 17 00:00:00 2001 From: Miguel Robiras Date: Mon, 13 Oct 2025 16:41:15 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Se=20a=C3=B1ade=20y=20mejora=20la=20l=C3=B3?= =?UTF-8?q?gica=20de=20verificaci=C3=B3n=20Atera=20para=20que=20la=20aplic?= =?UTF-8?q?aci=C3=B3n:=20-=20Normalice=20MACs=20(quita=20":"=20"-"=20"."?= =?UTF-8?q?=20y=20pasa=20a=20may=C3=BAsculas).=20-=20Recoja=20todas=20las?= =?UTF-8?q?=20MACs=20locales=20v=C3=A1lidas=20(GetAllLocalMacs).=20-=20Ext?= =?UTF-8?q?raiga=20todas=20las=20MACs=20de=20cada=20agente=20(ExtractMacsF?= =?UTF-8?q?romAgent),=20soportando=20MacAddresses=20(array),=20MacAddress?= =?UTF-8?q?=20(string),=20Macs=20y=20NetworkInterfaces.=20-=20Compare=20to?= =?UTF-8?q?das=20las=20MACs=20locales=20contra=20todas=20las=20MACs=20del/?= =?UTF-8?q?los=20agentes:=20si=20existe=20alguna=20coincidencia,=20se=20ma?= =?UTF-8?q?rca=20el=20equipo=20como=20"En=20soporte".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit También: - Se añade parámetro 'debug' en VerificarSoporteAteraAsync para facilitar pruebas (muestra MACs detectadas). - Se corrige el error CS1977 al evitar usar una lambda sobre un objeto dinámico (forzado cast a object / lista tipada). - Se mantiene el método ObtenerMacPrincipal para compatibilidad. Notas: - En máquinas con clientes VPN que ocultan o sustituyen la MAC (p. ej. NordLynx/WireGuard) puede no encontrarse coincidencia; se puede añadir posteriormente un fallback por AgentId leído del registro. - El proyecto del instalador requiere WiX Toolset v3.11 para compilar el instalador, esto no afecta a la ejecución de la app. --- soporteKM/Form1.cs | 143 +++++++++++++++++++++++++++++++++------------ 1 file changed, 107 insertions(+), 36 deletions(-) diff --git a/soporteKM/Form1.cs b/soporteKM/Form1.cs index 8cc5370..54cb971 100644 --- a/soporteKM/Form1.cs +++ b/soporteKM/Form1.cs @@ -177,41 +177,121 @@ private async void Form1_Load(object sender, EventArgs e) } } - private string ObtenerMacPrincipal() + // Normaliza una MAC: quita separadores y devuelve en mayúsculas (o null si inválida) + private string NormalizeMac(string mac) { + if (string.IsNullOrWhiteSpace(mac)) return null; + var clean = mac.Replace(":", "").Replace("-", "").Replace(".", "").Trim().ToUpperInvariant(); + // Aceptamos >=12 hex-digits; si vienen más (p. ej. con prefijos) las dejamos tal cual + if (clean.Length < 12) return null; + return clean; + } + + // Devuelve todas las MACs del equipo (sin duplicados, ya normalizadas y sin vacíos) + private List GetAllLocalMacs(bool debug = false) + { + var result = new HashSet(); try { var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); foreach (var ni in interfaces) { - if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && - ni.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) + try + { + // Omitir loopback/tunnel si quieres (opcional). Aquí los incluimos pero dependerá de lo que quieras comparar. + // if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel) continue; + + var mac = ni.GetPhysicalAddress()?.ToString(); + var nm = NormalizeMac(mac); + if (!string.IsNullOrEmpty(nm)) + result.Add(nm); + } + catch { - return ni.GetPhysicalAddress().ToString(); // Ejemplo: "A1B2C3D4E5F6" + // ignorar } } + + if (debug) + { + var dbg = result.Count == 0 ? "(ninguna)" : string.Join(", ", result); + MessageBox.Show($"MACs locales detectadas: {dbg}", "Debug MACs Locales"); + } } catch (Exception ex) { - MessageBox.Show("Error al obtener MAC: " + ex.Message); + if (debug) MessageBox.Show($"Error leyendo interfaces: {ex.Message}", "Error"); } - return null; + + return result.ToList(); } + // Extrae todas las MACs posibles de un objeto 'agent' devuelto por la API (maneja arrays y strings) + private List ExtractMacsFromAgent(dynamic agent) + { + var macs = new HashSet(); + + try + { + // MacAddresses (array) - muy común + if (agent.MacAddresses != null) + { + foreach (var m in agent.MacAddresses) + { + string s = Convert.ToString(m); + var nm = NormalizeMac(s); + if (!string.IsNullOrEmpty(nm)) macs.Add(nm); + } + } + + // MacAddress (string) + if (agent.MacAddress != null) + { + var nm = NormalizeMac(Convert.ToString(agent.MacAddress)); + if (!string.IsNullOrEmpty(nm)) macs.Add(nm); + } + + // Macs (otro posible nombre) + if (agent.Macs != null) + { + var nm = NormalizeMac(Convert.ToString(agent.Macs)); + if (!string.IsNullOrEmpty(nm)) macs.Add(nm); + } + + // Otros campos menos comunes que podrías querer comprobar (opcional) + if (agent.NetworkInterfaces != null) + { + // Si la API devuelve detalles de interfaces, intentar extraer + foreach (var ni in agent.NetworkInterfaces) + { + if (ni.MacAddress != null) + { + var nm = NormalizeMac(Convert.ToString(ni.MacAddress)); + if (!string.IsNullOrEmpty(nm)) macs.Add(nm); + } + } + } + } + catch + { + // ignorar errores en el parseo de un agente concreto + } - // 🔍 Verificación de soporte Atera - private async Task VerificarSoporteAteraAsync() + return macs.ToList(); + } + + // Versión actualizada de VerificarSoporteAteraAsync que compara todas las MACs locales contra TODAS las MACs de la API + private async Task VerificarSoporteAteraAsync(bool debug = false) { try { string hostname = ObtenerNombreHost(); - string macLocal = ObtenerMacPrincipal(); - - MessageBox.Show($"🖥 Hostname local: {hostname}\n🔌 MAC local: {macLocal}", "Debug Local"); + // Obtener TODAS las macs locales + var localMacs = GetAllLocalMacs(debug).Select(m => m.ToUpperInvariant()).ToHashSet(); - if (string.IsNullOrEmpty(macLocal)) + if (localMacs == null || localMacs.Count == 0) { - MostrarEstado(false, "No en soporte (No se pudo obtener la MAC local)"); + MostrarEstado(false, "No en soporte (no se encontraron MACs locales)"); return; } @@ -220,7 +300,6 @@ private async Task VerificarSoporteAteraAsync() client.BaseAddress = new Uri("https://app.atera.com"); client.DefaultRequestHeaders.Add("X-API-KEY", "5cc02a420cf54be6a511429cfb6be3da"); - // ✅ Nueva llamada directa por nombre de máquina var response = await client.GetAsync($"/api/v3/agents/machine/{hostname}"); if (!response.IsSuccessStatusCode) { @@ -231,9 +310,6 @@ private async Task VerificarSoporteAteraAsync() string json = await response.Content.ReadAsStringAsync(); dynamic result = JsonConvert.DeserializeObject(json); - // Mostrar resultado bruto - MessageBox.Show($"Respuesta de Atera para {hostname}:\n{json}", "Debug API Atera"); - if (result == null || result.items == null || ((IEnumerable)result.items).Count() == 0) { MostrarEstado(false, "No en soporte (no encontrado en Atera)"); @@ -245,35 +321,29 @@ private async Task VerificarSoporteAteraAsync() foreach (var agent in result.items) { - var macs = agent.MacAddresses != null ? agent.MacAddresses : agent.MacAddress; + // Guardamos en una lista tipada usando un cast explícito a List + var lista = (List)ExtractMacsFromAgent((object)agent); // o simplemente ExtractMacsFromAgent((object)agent) + var agentMacs = new HashSet(StringComparer.OrdinalIgnoreCase); - if (macs != null) + foreach (var m in lista) { - MessageBox.Show($"Analizando MACs del agente:\n{string.Join(", ", macs)}", "Debug MAC Atera"); - - foreach (var mac in macs) - { - string macAtera = mac.ToString().Replace(":", "").Replace("-", "").ToUpper(); - string macLocalClean = macLocal.Replace(":", "").Replace("-", "").ToUpper(); - - if (macAtera == macLocalClean) - { - encontrado = true; - cliente = agent.CustomerName?.ToString() ?? "Cliente desconocido"; - MessageBox.Show($"✅ Coincidencia encontrada:\nCliente: {cliente}\nMAC Atera: {macAtera}\nMAC Local: {macLocalClean}", "Debug Coincidencia"); - break; - } - } + if (!string.IsNullOrWhiteSpace(m)) + agentMacs.Add(m.ToUpperInvariant()); } - if (encontrado) + if (localMacs.Overlaps(agentMacs)) + { + encontrado = true; + cliente = (agent.CustomerName != null) ? Convert.ToString(agent.CustomerName) : "Cliente desconocido"; break; + } } + if (encontrado) MostrarEstado(true, $"🟢 En soporte ({cliente})"); else - MostrarEstado(false, "🔴 No en soporte (MAC no coincide)"); + MostrarEstado(false, "🔴 No en soporte (MACs no coinciden)"); } } catch (Exception ex) @@ -285,6 +355,7 @@ private async Task VerificarSoporteAteraAsync() + private string ObtenerDeviceGuidLocal() { try From 44772cb0d249f43dfe14d24aca64c5e95e236c33 Mon Sep 17 00:00:00 2001 From: Miguel Robiras Date: Fri, 13 Feb 2026 11:58:52 +0100 Subject: [PATCH 3/3] Nueva version, lista para publicar --- installsoporteKM/Product.wxs | 7 +++++-- soporteKM/Properties/AssemblyInfo.cs | 4 ++-- soporteKM/packages.config | 1 + soporteKM/soporteKM.csproj | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/installsoporteKM/Product.wxs b/installsoporteKM/Product.wxs index b6236da..e1b5af3 100644 --- a/installsoporteKM/Product.wxs +++ b/installsoporteKM/Product.wxs @@ -1,11 +1,11 @@ - @@ -53,6 +53,9 @@ + + + diff --git a/soporteKM/Properties/AssemblyInfo.cs b/soporteKM/Properties/AssemblyInfo.cs index 3e23839..a7d3beb 100644 --- a/soporteKM/Properties/AssemblyInfo.cs +++ b/soporteKM/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Puede especificar todos los valores o usar los valores predeterminados de número de compilación y de revisión // utilizando el carácter "*", como se muestra a continuación: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.4.0")] -[assembly: AssemblyFileVersion("1.0.4.0")] +[assembly: AssemblyVersion("1.0.4.2")] +[assembly: AssemblyFileVersion("1.0.4.2")] diff --git a/soporteKM/packages.config b/soporteKM/packages.config index 4b1297f..80374a3 100644 --- a/soporteKM/packages.config +++ b/soporteKM/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/soporteKM/soporteKM.csproj b/soporteKM/soporteKM.csproj index 295a177..e009535 100644 --- a/soporteKM/soporteKM.csproj +++ b/soporteKM/soporteKM.csproj @@ -51,6 +51,24 @@ icon_Konica.ico + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.Compression.dll + + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.Compression.Cab.dll + + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.Compression.Zip.dll + + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.WindowsInstaller.dll + + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.WindowsInstaller.Linq.dll + + + ..\packages\WixMsiToolset.3.11.0\lib\net452\Microsoft.Deployment.WindowsInstaller.Package.dll + ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll