Skip to content

Instantly share code, notes, and snippets.

@EdgarEDT
Forked from JuanKRuiz/MsgBoxUtil.cs
Last active August 29, 2015 14:18
Show Gist options
  • Save EdgarEDT/8e54150167d811a8385d to your computer and use it in GitHub Desktop.
Save EdgarEDT/8e54150167d811a8385d to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
/// <summary>
/// Modifica los textos en los botones de un MessageBox
/// </summary>
/// <!--
/// Autor: Juan Carlos Ruiz Pacheco
/// Fecha: 2009-04-10
/// Email: juankruiz@gmail.com
/// Blog : http://juank.io
/// -->
/// <example>
/// MsgBoxUtil.HackMessageBox("Descartar", "Reintentar", "Ignorar");
/// MessageBox.Show("hola3", "hola3", MessageBoxButtons.AbortRetryIgnore);
/// MsgBoxUtil.UnHackMessageBox();</example>
public class MsgBoxUtil
{
#region Interoperabilidad
/// <summary>Delegado para pasar funciones coom parametro a llmados de la api</summary>
private delegate bool EnumWindowDelegate(IntPtr handler, IntPtr longPointer);
/// <summary>Establece el texto de una ventana</summary>
[DllImport("user32.dll")]
private static extern bool SetWindowText(IntPtr handler, string texto);
/// <summary>Obtiene el nombre de la clase de una ventana</summary>
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr handler, StringBuilder nombre, int tamañoMaximo);
/// <summary>Realiza un listado de las ventanas hijas y por cada una ejecuta un callback asociado</summary>
[DllImport("user32.dll")]
private static extern bool EnumChildWindows(IntPtr handler, EnumWindowDelegate callback, IntPtr longPointer);
/// <summary>Realiza un listado de las ventanas del hilo actual por cada una ejecuta un callback asociado</summary>
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int threadID, EnumWindowDelegate callback, IntPtr longPointer);
/// <summary>Obtiene el ID del hilo actual</summary>
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId ();
/// <summary>Obtiene el handler de una ventana</summary>
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string ClassName, string WindowName);
/// <summary>Obtiene el thread id del hilo donde se ejecuta una ventana</summary>
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
/// <summary>Realiza un listado de las ventanas y por cada una ejecuta un callback asociado</summary>
[DllImport("user32.dll")]
static extern bool EnumWindows(EnumWindowDelegate lpEnumFunc, IntPtr lParam);
/// <summary>Destruye la referencia a una ventana</summary>
[DllImport("user32.dll")]
static extern bool DestroyWindow(IntPtr hwnd);
#endregion Interoperabilidad
#region Objetos locales
/// <summary>Array que almacena los textosque se colocaran a cada uno de los botones del msgbox</summary>
private static string[] textoBotones;
/// <summary>Indica cual de los botones se esta actualizando</summary>
private static int indiceTexto;
/// <summary>Delegado para llamar el proceso de cambio de texto de manera asincrona, se usa en aplicaciones no forms</summary>
private static WaitCallback EsperarYCambiarMsgBoxWC = new WaitCallback(EsperarYCambiarMsgBox);
/// <summary>Delegado para buscar e iniciar el proceso de cambio de textos en aplicaciones forms</summary>
private delegate void BuscarMsgBoxDelegate();
/// <summary>Handler del ultimo message box modificado</summary>
private static IntPtr globalHandler;
#endregion Objetos locales
#region Constantes
/// <summary>Nombre de la clase de la ventana de un messagebox</summary>
private const string MBOX_CLASSNAME = "#32770";
/// <summary>Nombre de la clase de la ventana de un boton</summary>
private const string BUTTON_CLASSNAME = "Button";
/// <summary>Número de reintentos de hallar un messagebox en el listado de ventanas
/// , se usa en aplicaciones no forms</summary>
private const int CICLOS_REINTENTO = 2;
/// <summary>Tiempo de espera entre reintentos de hallar un messagebox en el listadod de ventanas
/// , se usa en aplicaciones no forms</summary>
private const int TIEMPO_REINTENTO = 50;
/// <summary>Capacidad máxima para StringBuilders</summary>
private const int STRING_BUILDER_CAPACITY = 256;
#endregion Constantes
#region Métodos Públicos
/// <summary>
/// Modifica el texto de los botones de un messagebox
/// </summary>
/// <param name="textoBotones">lista de labels para los botones</param>
/// <remarks>Internamente se llama a EsperarYCambiarMsgBoxWC
/// que es un WaitCallBack que llama a EsperarYCambiarMsgBox</remarks>
public static void HackMessageBox( params string[]textoBotones)
{
//guardar referencia global a la lista
MsgBoxUtil.textoBotones = textoBotones;
//Si hay al menos una forma... se debe trabajar como aplicación forms
if (Application.OpenForms.Count > 0)
//Se llama el delegado desde el hilo actual de forms
Application.OpenForms[0].BeginInvoke(new BuscarMsgBoxDelegate(BuscaMessageBox));
else/*sino, se debe presuponer que la instancia del msgbox no ha sido creada
* y hay que esperar a que se cree*/
//Llamado asincrono
ThreadPool.QueueUserWorkItem(EsperarYCambiarMsgBoxWC);
}
/// <summary>Destruye el hack aplicado</summary>
public static void UnHackMessageBox()
{
//destruye la ventahna de msgbox a la qu se le aplico el hack
//windows se encarga de revivir la instancia
DestroyWindow(MsgBoxUtil.globalHandler);
}
#endregion #region Métodos Públicos
#region Métodos Privados
/// <summary>Wrapper para simplificar el llamado a EnumThreadWindows desde HackMessageBox</summary>
private static void BuscaMessageBox()
{
EnumThreadWindows(GetCurrentThreadId(), ProcesaMessageBoxEnForms, IntPtr.Zero);
}
/// <summary>
/// Busca las ventanas que sean messagebox y que pertenezcan al proceso actual
/// sino encuentra ninguna realiza algunos reintentos cada cierto tiempo
/// </summary>
/// <param name="stateInfo">no se usa, neceario para crear un waitcallback y asi realizar ejecución desde el threadpool</param>
/// <remarks>Esta funcion es util cuando no se busca el messagebox desde forms,
/// ya que se debe esperar a que la instancia estatica del messagebox sea creada
/// por lo cual se prevee hacer algunos intentos de busqueda</remarks>
private static void EsperarYCambiarMsgBox(Object stateInfo)
{
int contador = CICLOS_REINTENTO;
do
{
//sino encontro, espera un poco y busca de nuevo
//EnumWindows retorna true si enumero las ventanas hasta el final
if (EnumWindows(ProcesaMessageBoxEnOtros, IntPtr.Zero))
Thread.Sleep(TIEMPO_REINTENTO);
else//si encontro se sale del ciclo
break;
} while (--contador > 0);
}
/// <summary>
/// Determina si el handler pasado es un messagebox,
/// si es así inicia el proceso de modificacion de texto de los botones
/// </summary>
/// <param name="handler">manejador de la ventana</param>
/// <param name="longPointer">no se usa, pero se requiere como signature de delegado</param>
/// <returns>Si el handle pasado no es un MessageBox devuelve true, false de lo contrario</returns>
private static bool ProcesaMessageBoxEnForms(IntPtr handler, IntPtr longPointer)
{
//almacenar nombre de la clase
StringBuilder nombreClase = new StringBuilder(STRING_BUILDER_CAPACITY);
GetClassName(handler, nombreClase, nombreClase.Capacity);
//Si no es un MessageBox...
if (nombreClase.ToString() != MBOX_CLASSNAME)
return true;
else
{
//Inicializar indice del arreglo
indiceTexto = 0;
//Buscar y cambiar Botones del MessageBox
EnumChildWindows(handler, CambiaTextoBotonMessageBox, IntPtr.Zero);
//guardar handler del MessageBox
MsgBoxUtil.globalHandler = handler;
return false;
}
}
/// <summary>
/// Determina si el handler pasado es un messagebox,
/// si es así inicia el proceso de modificacion de texto de los botones
/// </summary>
/// <param name="handler">manejador de la ventana</param>
/// <param name="longPointer">no se usa, pero se requiere como signature de delegado</param>
/// <returns>Si el handle pasado no es un MessageBox devuelve true, false de lo contrario</returns>
/// <remarks>Util para llamar cuando el proceso que hace el llamado no es forms</remarks>
private static bool ProcesaMessageBoxEnOtros(IntPtr handler, IntPtr longPointer)
{
//almacenar nombre de la clase
StringBuilder nombreClase = new StringBuilder(STRING_BUILDER_CAPACITY);
GetClassName(handler, nombreClase, nombreClase.Capacity);
//Si no es un MessageBox...
if (nombreClase.ToString() != MBOX_CLASSNAME)
return true;
else
{ //Determinar el id de proceso actual
int pid = Process.GetCurrentProcess().Id;
/*Id del proceso de la ventana donde se ejecuta el
* MesasgeBox al cual pertenece el handler*/
int wpid;
GetWindowThreadProcessId(handler, out wpid);
//Si son del mismo proceso se procede
//a realizar la modificacion de textos
if (wpid == pid)
{
//Inicializar indice del arreglo
indiceTexto = 0;
//Buscar y cambiar Botones del MessageBox
EnumChildWindows(handler, CambiaTextoBotonMessageBox, IntPtr.Zero);
//guardar handler del MessageBox
MsgBoxUtil.globalHandler = handler;
return false;
}
else
return true;
}
}
/// <summary>
/// Cambia el texto del boton recibido como parámetro de acuerdo al listado
/// de valores iniciales al llamr a HackMessageBox
/// </summary>
/// <param name="handler">Manejador del boton</param>
/// <param name="longPointer">no se usa, pero se requiere como signature de los delegados</param>
/// <returns>siempre true</returns>
private static bool CambiaTextoBotonMessageBox(IntPtr handler, IntPtr longPointer)
{
//almacenar nombre de la clase
StringBuilder nombreClase = new StringBuilder(STRING_BUILDER_CAPACITY);
GetClassName(handler, nombreClase, nombreClase.Capacity);
//Si el handler es de un boton se modifica el texto
if(nombreClase.ToString() == BUTTON_CLASSNAME && indiceTexto < textoBotones.Length)
{
SetWindowText(handler, textoBotones[indiceTexto]);
indiceTexto++;
}
return true;
}
#endregion Métodos Privados
//http://juank.io
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment