c# - sirve - ¿Cómo obtengo el control de la ventana de una aplicación de consola?
handle con getstdhandle std_output_handle); (6)
Acabo de resolver este problema por mí mismo (lamentablemente antes de ver la respuesta de Thomas, que es mucho más rápido). Bueno, aquí hay otra forma para aquellos que no están satisfechos con su respuesta. Estoy escribiendo esta respuesta porque quiero brindar otra respuesta + una mejor manera de diseñar la clase del Program
si está tratando su consola como una Ventana. Comencemos con ese diseño:
He cambiado el estilo predeterminado de la clase Program
. De hecho, me he convertido en una clase que tiene un programa y no solo un método que lo represente y use otras clases para el contenido. (Si no sabes lo que quiero decir, no es importante).
La razón por la que tuve que hacerlo es porque quería escribir el siguiente controlador de eventos:
private void CatchUnhandled(Object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
MessageBox.Show(this, exception.Message, "Error"); // Check out 1st arg.
}
Sobrecarga este método MessageBox.Show(IWin32Window, String, String)
.
Debido a que Console no implementa IWin32Window
, tuve que implementarlo yo mismo, por supuesto, para llamar this
en el primer argumento.
Aquí está la implementación de él y todo lo demás:
Nota: este código está copiado desde mi aplicación, puede cambiar los modificadores de acceso.
Declaración de clase de Program
:
internal class Program : IWin32Window
{
...
}
Implementación de IWin32Window
:
public IntPtr Handle
{
get { return NativeMethods.GetConsoleWindow(); }
}
Utiliza la siguiente clase:
internal static class NativeMethods
{
[DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow();
}
Ahora, el problema es que en realidad no se puede llamar a this
en Main
, ya que es un método estático, así que lo que haya en Main
me he movido a un nuevo método llamado Start
y todo lo que Main
está haciendo es crear un nuevo Program
y llamar a Start
.
private static void Main()
{
new Program().Start();
}
private void Start()
{
AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;
throw new Exception();
}
El resultado fue, por supuesto, un cuadro de mensaje con la ventana de mi consola como propietario.
El uso de este método para un cuadro de mensaje es, por supuesto, una sola aplicación de este método.
¿Puede alguien decirme cómo obtener el control de una aplicación de consola de Windows en C #? En una aplicación de Windows Forms, normalmente probaría this.Handle
. this.Handle
.
En una aplicación de consola que transmitía diagostics a la consola, y para la cual quería desactivar la entrada del mouse, probé GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title)
. Cada uno de estos devolvió el mismo identificador distinto de cero, pero cuando traté de usar ese identificador en SetConsoleMode lanzó una excepción "Invalid Handle". Finalmente probé SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))
con STD_INPUT_HANDLE definido como -10, y funcionó. La documentación de MS sugiere que se pueden reasignar identificadores a las consolas, y no estoy cómodo o contento con esta solución, pero hasta ahora es lo único que he encontrado que me permite desactivar el modo de edición rápida mediante programación. GetStdHandle(STD_INPUT_HANDLE)
devuelve ''3'', las otras llamadas devuelven un valor de 7 dígitos que varía cada vez que se ejecuta el programa.
No creo que haya tal cosa. La ventana de la consola no es accesible para la aplicación. Usted PUEDE tratar de iterar la lista de procesos buscando su propio nombre de proceso. La clase de Process
IIRC contiene una propiedad para el identificador de ventana principal del programa, que podría ser la ventana de consola para aplicaciones de consola, de lo que no estoy seguro.
No estoy seguro de que funcione, pero puedes intentarlo:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
Prueba esto:
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);
…
Console.Title = "Test";
…
IntPtr handle = FindWindowByCaption(IntPtr.Zero, Console.Title);
- El método
Process.MainWindowHandle
antes mencionado solo funciona para el proceso que inició la consola - El método
FindWindowByCaption
es inherentemente poco confiable
Aquí hay una forma robusta de hacer esto:
Las funciones relacionadas de la API Win32 de la consola son:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
static extern bool FreeConsole();
- Para la consola al que está conectado el proceso actual, solo
GetConsoleWindow()
es suficiente - Para la consola se adjunta otro proceso, adjuntarlo también con
AttachConsole
, llamar aGetConsoleWindow
, separarlos inmediatamente conFreeConsole
.
Para mayor precaución, registre un controlador de eventos de consola antes de adjuntarlo (y anule el registro después de separarlo) para que no se interrumpa accidentalmente si ocurre un evento de consola en el pequeño marco de tiempo que está conectado a la consola:
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine,
bool Add);
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
enum CtrlTypes : uint {
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
bool is_attached=false;
ConsoleCtrlDelegate ConsoleCtrlDelegateDetach = delegate(CtrlType) {
if (is_attached = !FreeConsole())
Trace.Error(''FreeConsole on '' + CtrlType + '': '' + new Win32Exception());
return true;
};
Hacer cambios en el proceso actual solo para leer algo es bastante feo (cuando se trata de un proceso de consola, esto se vuelve realmente feo, ya que requiere un proceso de ayuda para evitar terminar la consola actual). Sin embargo, una mayor investigación muestra que no hay otra forma de inyectar en el proceso de csrss
o el proceso de destino.
La información de la correspondencia de la consola se encuentra en y es administrada por csrss.exe
(o una multitud de ellos, uno para cada sesión, desde Vista), por lo que no se puede recuperar con ReadProcessMemory
como ReadProcessMemory
. Todo lo que csrss
expone es la CSRSS LPC API . Solo hay una función relevante en la lista de API completa, SrvGetConsoleWindow
. Y no acepta un PID, pero determina el de la parte que llama como se ve en una implementación alternativa o el desensamblaje de la función en winsrv.dll
.