Botón Cerrar desactivado Fecha: 05/Octubre/2003 (11/Octubre/2003) |
. |
A raíz de la petición de ayuda de un amigo, que desea que una aplicación arranque sin que pueda ser terminada por el usuario pulsando el botón Cerrar (el aspa de la esquina derecha) de la Barra de título del formulario ni mediante la pulsación de las teclas ALT+F4, he encontrado muchos códigos en Internet, unos para C#, otros para C++ y otros para Visual Basic. Todos ellos tienen en común la necesidad de leer procedimientos externos, almacenados en librerías de Windows, User32.dll en este caso concreto. Pero así como los ejemplos encontrados para C#, por lo general, funcionan bien y rápido (los de C++ los dejo a un lado por mi ignorancia), los que he visto para Visual Basic .NET me han dado muchos problemas: algunos no funcionan bien y, los que sí lo hacen, tardan bastante en desactivar el botón de cierre, lo que causa mal efecto.
Incluso Microsoft tiene un ejemplo de este tipo, pero referido a una aplicación de consola, que puede ser consultado aquí.
Así que he realizado en Visual Basic .NET un código que desactiva ese botón y el efecto de las teclas ALT+F4 de manera rápida, lo que causa mejor impresión al usuario.
Si alguien desea profundizar sobre este asunto, ha de buscar información sobre las API de Windows, pues en realidad éso es lo que hacemos, utilizar procedimientos externos a Visual Basic, existentes en librerías del sistema. En este ejercicio leemos procedimientos de la librería USER32.DLL, y con ellos actuamos sobre el llamado Menú de sistema de las ventanas, el que provee de funcionalidad tanto a los botones de Maximizar / Minimizar / Cerrar como a los elementos que cuelgan del icono de la Barra de título: Restaurar / Mover / Tamaño / Minimizar / Maximizar / Separador / Cerrar.A continuación se muestra el código en Visual Basic .NET:
'DESACTIVAR EL BOTÓN CERRAR Y LA FUNCIÓN DE CIERRE CON ALT+F4 'NOTA de Miliuco: 'en esta aplicación se usa el tipo de dato IntPtr. Se ha diseñado 'el tipo IntPtr para que sea un número entero cuyo tamaño sea específico 'de la plataforma. Es decir, se espera que una instancia de este tipo tenga 'lugar en sistemas operativos y hardware de 32 bits, y en sistemas 'operativos y hardware de 64 bits. El tipo IntPtr se puede utilizar por idiomas 'que admiten punteros, y como un medio común para hacer referencia a los 'datos entre idiomas que admiten o no punteros. El tipo IntPtr es compatible 'con CLS (Common Language Specification) -> conjunto de características 'básicas de lenguaje englobadas en .NET Framework. El tipo IntPtr pertenece 'al espacio de nombres System 'Importar espacios de nombres necesarios para la aplicación Imports System Imports System.Drawing Imports System.Collections Imports System.ComponentModel Imports System.Windows.Forms Imports System.Data 'Para leer procedimientos externos radicados en librerías de windows 'Imports System.Runtime.InteropServices Public Class Form1 Inherits System.Windows.Forms.Form #Region " Código generado por el Diseñador de Windows Forms " Public Sub New() MyBase.New() 'El Diseñador de Windows Forms requiere esta llamada. InitializeComponent() 'Agregar cualquier inicialización después de la llamada a InitializeComponent() End Sub 'Form reemplaza a Dispose para limpiar la lista de componentes. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Requerido por el Diseñador de Windows Forms Private components As System.ComponentModel.IContainer 'NOTA: el Diseñador de Windows Forms requiere el siguiente procedimiento 'Puede modificarse utilizando el Diseñador de Windows Forms. 'No lo modifique con el editor de código. Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents Button2 As System.Windows.Forms.Button Friend WithEvents GroupBox1 As System.Windows.Forms.GroupBox <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1)) Me.Button1 = New System.Windows.Forms.Button Me.Button2 = New System.Windows.Forms.Button Me.GroupBox1 = New System.Windows.Forms.GroupBox Me.SuspendLayout() ' 'Button1 ' Me.Button1.Font = New System.Drawing.Font("Verdana", 18.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte)) Me.Button1.Location = New System.Drawing.Point(24, 32) Me.Button1.Name = "Button1" Me.Button1.Size = New System.Drawing.Size(208, 72) Me.Button1.TabIndex = 0 Me.Button1.Text = "Desactivar X" ' 'Button2 ' Me.Button2.Font = New System.Drawing.Font("Verdana", 18.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte)) Me.Button2.Location = New System.Drawing.Point(240, 32) Me.Button2.Name = "Button2" Me.Button2.Size = New System.Drawing.Size(112, 72) Me.Button2.TabIndex = 1 Me.Button2.Text = "Salir" ' 'GroupBox1 ' Me.GroupBox1.Location = New System.Drawing.Point(8, 8) Me.GroupBox1.Name = "GroupBox1" Me.GroupBox1.Size = New System.Drawing.Size(360, 112) Me.GroupBox1.TabIndex = 2 Me.GroupBox1.TabStop = False Me.GroupBox1.Text = "Botones de comando" ' 'Form1 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(376, 130) Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.GroupBox1) Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) Me.Name = "Form1" Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen Me.Text = " Botón cerrar ->" Me.ResumeLayout(False) End Sub #End Region 'Declaración de constantes necesarias (valores en hexadecimal) Private Const MF_BYPOSITION As Integer = &H400 Private Const MF_REMOVE As Integer = &H1000 Private Const MF_DISABLED As Integer = &H2 'Variable para saber si ya está desactivado el botón X Private pulsado As Boolean = True 'Importación de procedimientos externos almacenados 'en la librería de Windows USER32.DLL 'Obtener el menú de sistema Private Declare Function GetSystemMenu Lib "User32" _ (ByVal hWnd As Integer, _ ByVal bRevert As Boolean) As IntPtr 'Obtener el número de elementos del menú de sistema Private Declare Function GetMenuItemCount Lib "User32" _ (ByVal hMenu As Integer) As IntPtr 'Quitar elementos del menú de sistema Private Declare Function RemoveMenu Lib "User32" _ (ByVal hMenu As Integer, _ ByVal nPosition As Integer, _ ByVal wFlags As Long) As IntPtr 'Redibujar la barra de título de la ventana Private Declare Function DrawMenuBar Lib "User32" _ (ByVal hWnd As Integer) As IntPtr 'Método que desactiva el botón X (cerrar) Private Sub DisableCloseButton(ByVal hWnd As IntPtr) Try 'captura de excepciones Dim menuItemCount As IntPtr Dim hMenu As IntPtr 'Obtener el manejador del menú de sistema del formulario hMenu = GetSystemMenu(hWnd.ToInt32(), False) 'Obtener la cuenta de los ítems del menú de sistema. 'Es el menú que aparece al pulsar sobre el icono a la izquierda 'de la Barra de título de la ventana, consta de los ítems: Restaurar, Mover, 'Tamaño,Minimizar, Maximizar, Separador, Cerrar. menuItemCount = GetMenuItemCount(hMenu.ToInt32()) 'Quitar el ítem Close (Cerrar), que es el último de ese menú RemoveMenu(hMenu.ToInt32(), menuItemCount.ToInt32() - 1, MF_DISABLED Or MF_BYPOSITION) 'Quitar el ítem Separador, el penúltimo de ese menú, entre Maximizar y Cerrar RemoveMenu(hMenu.ToInt32(), menuItemCount.ToInt32() - 2, MF_DISABLED Or MF_BYPOSITION) 'Redibujar la barra de menú DrawMenuBar(hWnd.ToInt32()) 'mostrar un mensaje con la excepción producida Catch pollo As Exception MessageBox.Show("Se ha producido la excepción: " + vbCrLf + pollo.Message, _ "Error del programa", MessageBoxButtons.OK) End Try End Sub 'Al pulsar el botón Desactivar Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Si no hemos desactivado el botón Cerrar If (pulsado) Then 'Método desarrollado más arriba, pasando como parámetro 'el identificador de la ventana sobre la que vamos a actuar DisableCloseButton(Me.Handle) 'Aviso al usuario, no funciona el botón cerrar ni las teclas ALT+F4 MessageBox.Show("El botón Cerrar ha sido desactivado." + vbCrLf + _ "Pulsa Salir para cerrar la aplicación", "Cerrar desactivado 1") 'para saber que ya hemos desactivado el b´tón pulsado = False Else 'Si ya hemos desactivado el botón Cerrar MessageBox.Show("Ya habías pulsado aquí antes.", "Cerrar desactivado 2") End If End Sub 'Al pulsar el botón Salir Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Salir de la aplicación Me.Close() End Sub End ClassImagen del programa en funcionamiento:
Fichero con el código de ejemplo usando Visual Studio .NET 2003:
- para Visual Basic (miliuco_cerrar_desactivadovb.zip - Tamaño 75.2 KB)
- para Visual C# (miliuco_cerrar_desactivadocs.zip - Tamaño 67.3 KB)