Quitar botón cerrar de la ventana en C#

Deshabilitar el botón cerrar de la barra de título de la ventana usando procedimientos externos a .NET existentes en librerías del sistema (Platform Invoque) en Visual Studio 2017 con C#

Un programador desea que una aplicación arranque sin que pueda ser terminada por el usuario pulsando el botón Cerrar de la Barra de título del formulario (el aspa o la equis de la esquina derecha) ni mediante la pulsación de las teclas ALT+F4. El código en C# que se presenta en este artículo desactiva ese botón y el efecto de las teclas ALT+F4.

En primer lugar es necesario importar procedimientos externos a .NET, almacenados en la librería de Windows llamada user32.dll, estos procedimientos permiten manipular los botones de la barra de título de las ventanas actuando sobre el llamado «Menú de sistema», el que provee de funcionalidad tanto a los botones 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.
.NET Framework proporciona librerías para muchas APIs del sistema, son las librerías manejadas o gestionadas (managed libraries), pero hay muchas otras librerías de Windows que carecen de equivalente en .NET.
Estas librerías también pueden ser utilizadas desde .NET gracias a los servicios de invocación de plataforma (PInvoke = Platfotm Invoque) que permiten utilizar librerías no manejadas (APIs de Windows como por ejemplo user32.dll) en los proyectos .NET.
Para ello se importa el espacio de nombres System.Runtime.InteropServices y los procedimientos externos.

using System.Runtime.InteropServices;

//Obtener el menú de sistema
[DllImport("User32.Dll")]
public static extern IntPtr GetSystemMenu(int hWnd, bool bRevert);

//Obtener el número de elementos del menú de sistema
[DllImport("User32.Dll")]
public static extern IntPtr GetMenuItemCount(int hMenu);

//Quitar elementos del menú de sistema
[DllImport("user32.Dll")]
public static extern IntPtr RemoveMenu(int hMenu, int nPosition,int wFlags); 

//Redibujar la barra de título de la ventana
[DllImport("User32.Dll")]
public static extern IntPtr DrawMenuBar(int hwnd);


Hay que declarar una constantes que se usan para saber el índice que tiene el elemento Cerrar dentro del Menú y eliminarlo.

//Declaración de constantes necesarias (valores en hexadecimal
private const int MF_BYPOSITION = 0x400; //posición de cada ítem en el menú
private const int MF_DISABLED = 0x2; //ítem del menú deshabilitado
//private const int MF_REMOVE = 0x1000;


Este es el método DisableCloseButton que desactiva el botón cerrar.

//Método que desactiva el botón X (cerrar)
public void DisableCloseButton(int hWnd)
{
	IntPtr hMenu;
	IntPtr menuItemCount;
	//Obtener el manejador del menú de sistema del formulario
	hMenu = GetSystemMenu(hWnd, 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 | MF_BYPOSITION);
	//Quitar el ítem Separador, el penúltimo de ese menú, entre Maximizar y Cerrar
	RemoveMenu(hMenu.ToInt32(), menuItemCount.ToInt32() - 2, MF_DISABLED | MF_BYPOSITION);
	//Redibujar la barra de menú
	DrawMenuBar(hWnd);
}


Al pulsar el botón Cerrar se llama al método DisableCloseButton al que se le pasa el identificador de la ventana sobre la que actúa que en este caso es la única ventana del programa.

//Al pulsar el botón Desactivar
private void button1_Click(object sender, System.EventArgs e)
{
	if (pulsado)
	{
		//Método desarrollado más arriba, pasando como parámetro
		//el identificador de la ventana sobre la que vamos a actuar
		DisableCloseButton(this.Handle.ToInt32());
		//Aviso al usuario, no funciona el botón cerrar ni ALT+F4
		MessageBox.Show ("El botón Cerrar ha sido desactivado.nPulsa Salir para cerrar la aplicación",    "Cerrar desactivado 1");
		pulsado = false;
	}
	else
	{
		MessageBox.Show ("Ya habías pulsado aquí.nPulsa Salir para cerrar la aplicación",    "Cerrar desactivado 2");
	}
}


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, y que funcione correctamente en ambos sistemas.
El tipo IntPtr se puede utilizar por lenguajes que admiten punteros y como un medio común para hacer referencia a los datos entre lenguajes 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.

Código de la aplicación completa en Visual Studio 2017: ZIP.