Aplicación que se ejecuta como consola e interfaz gráfica
Siguiendo una petición en la galería de código de MSDN, he publicado este ejemplo: aplicación que se ejecuta como consola e interfaz gráfica. ¡Échale un ojo, descarga y vota si te gusta! A continuación transcribo el extracto del artículo.
Introducción
En ciertas ocasiones nuestra aplicación Windows necesita ejecutarse en modo de consola, o al menos, proveer esta interfaz bajo ciertas condiciones. Es decir, una aplicación que se comporte como aplicación Windows y aplicación consola.
Construyendo el ejemplo
El código asociado es una solución de Visual Studio 2010, y se requiere tener Visual C++ instalado (aunque no debe ser difícil portar hacia versiones anteriores). El proyecto de Visual C++ es una aplicación MFC basada en ventana de diálogos. Para compilar sólo se necesita MFC y, dado que se hacen llamadas al API de Windows, algún SDK reciente.
Descripción
Al iniciar su ejecución, la aplicación determina si ha recibido un parámetro llamado /use-console. De ser así, crea una consola y la adjunta al proceso actual, para después crear un búfer y asociarlo a la consola. Posteriormente se escribe y lee información de la misma. En caso de no recibir dicho parámetro, la aplicación simplemente muestra una ventana de diálogo modal, con algunos controles en ella.
El bloque de código interesante lo presento a continuación:
if (objCmdLine.UseConsole()) { ::AllocConsole(); HANDLE hBuffer = ::CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); ::SetConsoleActiveScreenBuffer(hBuffer); HANDLE hInputHandle = GetStdHandle(STD_INPUT_HANDLE); CString strMsg; strMsg.LoadString(IDS_INFO); ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); strMsg.LoadString(IDS_GREETINGS); ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); CONSOLE_READCONSOLE_CONTROL objCtrl; ZeroMemory(&objCtrl, sizeof(CONSOLE_READCONSOLE_CONTROL)); objCtrl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL); const int nInputSize = 256; TCHAR szInput[nInputSize]; ZeroMemory(szInput, sizeof(TCHAR) * nInputSize); DWORD dwCharsRead; ::ReadConsole(hInputHandle, szInput, nInputSize, &dwCharsRead, &objCtrl); CString strInput(szInput); strInput = strInput.Mid(0, dwCharsRead - 2); strMsg.Format(IDS_HELLOFELLOW, strInput); ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); strMsg.LoadString(IDS_PRESSKEYTOEXIT); ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); ::ReadConsole(hInputHandle, szInput, 1, NULL, NULL); ::FreeConsole(); } else { CSampleDlg dlg; m_pMainWnd = &dlg; dlg.DoModal(); }
Del bloque anterior, hay que notar las siguientes llamadas a funciones del API.
-
AllocConsole se encarga de crear una consola y asociarla al proceso actual.
-
CreateConsoleScreenBuffer y SetConsoleActiveScreenBuffer crea un búfer y lo asocia a la consola previamente creada.
-
GetStdHandle nos proporciona un manejador hacia la salida de la consola (i.e. para poder leer y escribir desde y hacia ella).
-
ReadConsole y WriteConsole se encargan de leer datos desde y escribirlos hacia la consola, respectivamente.
-
Por último, FreeConsole desasocia la consola creada.
Código fuente
-
AboutDlg (h y cpp) – el cuadro de diálogo de "acerca de".
-
ConsoleCommandLineInfo (h y cpp) – implementa la clase que analiza los parámetros de la aplicación.
-
Resource.h – el encabezado donde se guardan los valores de recursos.
-
SampleApp (h y cpp) – la aplicación; en particular, contiene el método InitInstance donde se decide si mostrar la aplicación Windows o la aplicación por consola.
-
SampleDlg (h y cpp) – la ventana de diálogo principal de la aplicación.
Más información
Para mayor información, revisa esta sección: Sobre Consolas en MSDN.
Dibujar banderas en la consola
Ayer por l tarde respondí una pregunta del foro MSDN sobre cómo cambiar el color de fondo de la consola utilizando .NET y C#. La respuesta, por supuesto, es utilizar Console.BackgroundColor y Console.ForegroundColor, y lo que se escriba a continuación saldrá en los colores especificados.
Bueno, pues se me ocurrió hacer un pequeño programa que utilizando este principio pintara algunas banderas en el monitor. La idea es establecer el fondo e imprimir un caracter, un espacio en blanco. Y así dibujar las diferentes secciones de una bandera en la consola.
No es nada útil, realmente, pero fue entretenido y ya que lo tengo, pues a postearlo. Así que bueno, un codiguín para pasar la tarde, jejeje.
using System; namespace Fermasmas.Wordpress.Com { abstract class Flag { public virtual int Width { get { return 79; } } public virtual int Height { get { return 24; } } protected virtual int WidthSectors { get { return 1; } } protected virtual int HeightSectors { get { return 1; } } protected bool IsWidthSector(int sector, int value) { int sectorWidth = Width / WidthSectors; return value >= sectorWidth * Math.Max(sector - 1, 0) && value < sectorWidth * sector; } protected bool IsHeightSector(int sector, int value) { int sectorHeight = Height / HeightSectors; return value >= sectorHeight * Math.Max(sector - 1, 0) && value < sectorHeight * sector; } protected void SetupEdgeSquare() { Console.BackgroundColor = ConsoleColor.Gray; } protected abstract void SetupSquare(int x, int y); public void Draw() { for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { Console.ResetColor(); if (x == 0 || x == Width - 1 || y == 0 || y == Height - 1) SetupEdgeSquare(); else SetupSquare(x, y); Console.Write(' '); } Console.WriteLine(); } } } class GermanFlag : Flag { protected override int HeightSectors { get { return 3; } } protected override void SetupSquare(int x, int y) { if (IsHeightSector(1, y)) Console.BackgroundColor = ConsoleColor.Black; else if (IsHeightSector(2, y)) Console.BackgroundColor = ConsoleColor.Red; else if (IsHeightSector(3, y)) Console.BackgroundColor = ConsoleColor.Yellow; } } class SpanishFlag : Flag { protected override int HeightSectors { get { return 3; } } protected override void SetupSquare(int x, int y) { if(IsHeightSector(2, y)) Console.BackgroundColor = ConsoleColor.Yellow; else Console.BackgroundColor = ConsoleColor.Red; } } class FrenchFlag : Flag { protected override int WidthSectors { get { return 3; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(1, x)) Console.BackgroundColor = ConsoleColor.Blue; else if (IsWidthSector(2, x)) Console.BackgroundColor = ConsoleColor.White; else if (IsWidthSector(3, x)) Console.BackgroundColor = ConsoleColor.Red; } } class ItalianFlag : Flag { protected override int WidthSectors { get { return 3; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(1, x)) Console.BackgroundColor = ConsoleColor.Green; else if (IsWidthSector(2, x)) Console.BackgroundColor = ConsoleColor.White; else if (IsWidthSector(3, x)) Console.BackgroundColor = ConsoleColor.Red; } } class SwissFlag : Flag { public override int Width { get { return (int)Math.Round(Math.Min(base.Width, base.Height) * 1.7, 0); } } public override int Height { get { return Math.Min(base.Width, base.Height); } } protected override int HeightSectors { get { return 5; } } protected override int WidthSectors { get { return 6; } } protected override void SetupSquare(int x, int y) { if (((IsHeightSector(2, y) || IsHeightSector(4, y)) && IsWidthSector(4, x)) || (IsHeightSector(3, y) && (IsWidthSector(3, x) || IsWidthSector(4, x) || IsWidthSector(5, x)))) Console.BackgroundColor = ConsoleColor.White; else Console.BackgroundColor = ConsoleColor.Red; } } class EnglishFlag : Flag { protected override int HeightSectors { get { return 9; } } protected override int WidthSectors { get { return 9; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(5, x) || IsWidthSector(6, x) || IsHeightSector(6, y) || IsHeightSector(7, y) || IsHeightSector(8, y)) Console.BackgroundColor = ConsoleColor.Red; else Console.BackgroundColor = ConsoleColor.White; } } class SweedishFlag : Flag { protected override int HeightSectors { get { return 9; } } protected override int WidthSectors { get { return 15; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(6, x) || IsWidthSector(7, x) || IsHeightSector(6, y) || IsHeightSector(7, y) || IsHeightSector(8, y)) Console.BackgroundColor = ConsoleColor.Yellow; else Console.BackgroundColor = ConsoleColor.DarkBlue; } } class NorwegianFlag : Flag { protected override int HeightSectors { get { return 15; } } protected override int WidthSectors { get { return 25; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(10, x) || IsWidthSector(11, x) || IsHeightSector(12, y) || IsHeightSector(13, y) || IsHeightSector(14, y)) Console.BackgroundColor = ConsoleColor.DarkBlue; else if (IsWidthSector(9, x) || IsWidthSector(12, x) || IsHeightSector(11, y) || IsHeightSector(15, y)) Console.BackgroundColor = ConsoleColor.White; else Console.BackgroundColor = ConsoleColor.Red; } } class IcelandicFlag : Flag { protected override int HeightSectors { get { return 15; } } protected override int WidthSectors { get { return 25; } } protected override void SetupSquare(int x, int y) { if (IsWidthSector(10, x) || IsWidthSector(11, x) || IsHeightSector(12, y) || IsHeightSector(13, y) || IsHeightSector(14, y)) Console.BackgroundColor = ConsoleColor.DarkRed; else if (IsWidthSector(9, x) || IsWidthSector(12, x) || IsHeightSector(11, y) || IsHeightSector(15, y)) Console.BackgroundColor = ConsoleColor.White; else Console.BackgroundColor = ConsoleColor.DarkBlue; } } class GreekFlag : Flag { protected override int HeightSectors { get { return 10; } } protected override int WidthSectors { get { return 12; } } protected override void SetupSquare(int x, int y) { if (IsHeightSector(3, y) || IsHeightSector(5, y) || IsHeightSector(7, y) || IsHeightSector(9, y)) Console.BackgroundColor = ConsoleColor.White; else Console.BackgroundColor = ConsoleColor.DarkBlue; if (IsWidthSector(1, x) || IsWidthSector(2, x) || IsWidthSector(3, x) || IsWidthSector(4, x) || IsWidthSector(5, x)) { if (IsHeightSector(1, y) || IsHeightSector(2, y) || IsHeightSector(3, y) || IsHeightSector(4, y) || IsHeightSector(5, y) || IsHeightSector(6, y)) { if (IsWidthSector(3, x) || IsHeightSector(4, y)) Console.BackgroundColor = ConsoleColor.White; else Console.BackgroundColor = ConsoleColor.DarkBlue; } } } } class Program { static void Main(string[] args) { Flag flag; bool quit; do { Console.Clear(); Console.WriteLine("Selecciona una bandera a dibujar:"); Console.WriteLine("1. Alemania"); Console.WriteLine("2. España"); Console.WriteLine("3. Francia"); Console.WriteLine("4. Italia"); Console.WriteLine("5. Suiza"); Console.WriteLine("6. Inglaterra"); Console.WriteLine("7. Suecia"); Console.WriteLine("8. Noruega"); Console.WriteLine("9. Islandia"); Console.WriteLine("0. Grecia"); Console.WriteLine("Q. Quitar"); ConsoleKeyInfo key = Console.ReadKey(true); flag = null; quit = false; switch (key.Key) { case ConsoleKey.Q: quit = true; break; case ConsoleKey.D1: flag = new GermanFlag(); break; case ConsoleKey.D2: flag = new SpanishFlag(); break; case ConsoleKey.D3: flag = new FrenchFlag(); break; case ConsoleKey.D4: flag = new ItalianFlag(); break; case ConsoleKey.D5: flag = new SwissFlag(); break; case ConsoleKey.D6: flag = new EnglishFlag(); break; case ConsoleKey.D7: flag = new SweedishFlag(); break; case ConsoleKey.D8: flag = new NorwegianFlag(); break; case ConsoleKey.D9: flag = new IcelandicFlag(); break; case ConsoleKey.D0: flag = new GreekFlag(); break; default: Console.WriteLine("Llave inválida, intenta de nuevo..."); Console.ReadKey(true); break; } if (flag != null) { Console.Clear(); flag.Draw(); Console.ReadKey(true); Console.ResetColor(); } } while (!quit); } } }
Bueno, eso es todo. Como dije, nada extraordinario y solo una curiosidad. Hasta la próxima.
u.u