wake-up-neo.com

Wie bekomme ich das Fenster einer Konsolenanwendung in den Griff?

Kann mir jemand sagen, wie Sie mit einer Windows-Konsolenanwendung in C # umgehen können? In einer Windows Forms-Anwendung würde ich normalerweise this.Handle versuchen.

37
Grant

Nicht sicher, ob es funktioniert, aber Sie können es versuchen:

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
55
Thomas Levesque

Hier ist eine robuste Methode, dies zu tun:

Die zugehörigen Funktionen aus der Console Win32 API sind:

[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();
  • Für die Konsole, an die der aktuelle Prozess angehängt ist, reicht GetConsoleWindow() aus
  • Für die Konsole ist ein anderer Prozess angeschlossen, verbinden Sie ihn ebenfalls mit AttachConsole, rufen Sie GetConsoleWindow auf, sie trennen sich sofort mit FreeConsole.

Wenn Sie besonders vorsichtig sind, registrieren Sie einen Console-Ereignishandler, bevor Sie ihn anhängen (und die Registrierung nach dem Trennen aufheben), damit Sie nicht versehentlich beendet werden, wenn ein Console-Ereignis in dem winzigen Zeitrahmen auftritt, in dem Sie an der Konsole angebracht sind:

[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;
};

Änderungen am aktuellen Prozess vorzunehmen, nur um etwas zu lesen, ist ziemlich hässlich (wenn dies ein Konsolenprozess ist, wird dies sehr hässlich, da ein Hilfsprozess erforderlich ist, um das Beenden der aktuellen Konsole zu vermeiden). Weitere Untersuchungen zeigen jedoch, dass es keine andere Möglichkeit gibt, in den csrss-Prozess oder den Zielprozess zu injizieren.

Die Informationen zur Konsolenkorrespondenz befinden sich in csrss.exe (und einer Vielzahl davon, eine für jede Sitzung, seit Vista) und werden von diesem verwaltet. Sie können also nicht mit ReadProcessMemory abgerufen werden. Alles, was csrss verfügbar macht, ist die CSRSS LPC API . Die vollständige API-Liste enthält nur eine relevante Funktion, SrvGetConsoleWindow. Und es akzeptiert keine PID, bestimmt aber die des Anrufers, wie in einer alternativen Implementierung oder der Zerlegung der Funktion in winsrv.dll.

22
ivan_pozdeev

Versuche dies:

[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);
10
Zain Ali

Ich habe dieses Problem gerade für mich gelöst (leider bevor ich Thomas 'Antwort gesehen habe, was viel schneller geht). Nun, hier ist ein anderer Weg für diejenigen, die mit seiner Antwort nicht zufrieden sind. Ich schreibe diese Antwort, weil ich eine andere Antwort + liefern möchte, um die Klasse Program besser zu entwerfen, wenn Sie Ihre Konsole als Fenster behandeln. Beginnen wir mit diesem Design:

Ich habe den Standardstil der Klasse Program geändert. Ich habe es tatsächlich zu einer Klasse gemacht, die ein Programm enthält, und nicht nur eine Methode, die es darstellt und andere Klassen für den Inhalt verwendet. (Wenn Sie nicht wissen, was ich meine, nicht wichtig).

Der Grund, warum ich es tun musste, ist, dass ich den folgenden Ereignishandler schreiben wollte:

_private void CatchUnhandled(Object sender, UnhandledExceptionEventArgs e)
{
    var exception = e.ExceptionObject as Exception;
    MessageBox.Show(this, exception.Message, "Error"); // Check out 1st arg.
}
_

Diese Methode wird überladen MessageBox.Show(IWin32Window, String, String) .

Da Console IWin32Window nicht implementiert, musste ich es natürlich selbst implementieren, um einfach this in der 1 aufzurufenst Streit.

Hier ist die Implementierung davon und alles andere:

Hinweis: Dieser Code wurde aus meiner Anwendung kopiert. Sie können die Zugriffsmodifikatoren jederzeit ändern.

Program Klassenerklärung:

_internal class Program : IWin32Window
{
    ...
}
_

IWin32Window Implementierung:

_public IntPtr Handle
{
    get { return NativeMethods.GetConsoleWindow(); }
}
_

Es wird die folgende Klasse verwendet:

_internal static class NativeMethods
{
    [DllImport("kernel32.dll")]
    internal static extern IntPtr GetConsoleWindow();
}
_

Das Problem ist nun, dass Sie this in Main nicht aufrufen können, da es sich um eine statische Methode handelt. Was auch immer in Main war, ich bin zu einer neuen Methode mit dem Namen Start übergegangen, und alles, was Main tut, erstellt ein neues Program und ruft Start auf.

_private static void Main()
{
    new Program().Start();
}

private void Start()
{
    AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;

    throw new Exception();
}
_

Das Ergebnis war natürlich eine Nachrichtenbox mit dem Fenster meiner Konsole als Besitzer.
Die Verwendung dieser Methode für eine Nachrichtenbox ist natürlich nur eine Anwendung dieser Methode.

7
MasterMastic

Ich glaube nicht, dass es so etwas gibt. Das Konsolenfenster ist für die Anwendung nicht zugänglich. Sie können versuchen, die Prozessliste nach Ihrem eigenen Prozessnamen zu durchlaufen. Die Process-Klasse IIRC enthält eine Eigenschaft für das Hauptfenster-Handle des Programms, das möglicherweise das Konsolenfenster für Konsolenanwendungen ist, dessen ich nicht sicher bin.

1

In einer Konsolenanwendung, die Diagostik auf die Konsole gestreamt hat und für die ich die Mauseingabe deaktivieren wollte, habe ich versucht, __.GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title). Jeder von ihnen gab den gleichen nicht-null-Handle zurück, aber als ich versuchte, diesen Handle zu verwenden In SetConsoleMode wurde eine Ausnahme "Invalid Handle" ausgelöst. Schließlich versuchte ich SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) mit STD_INPUT_HANDLE als -10, und es funktionierte. Die Dokumentation von MS legt nahe, dass Handles zu Konsolen möglicherweise neu zugewiesen werden, und ich bin mit dieser Lösung nicht zufrieden oder zufrieden, aber bisher habe ich nur das gefunden, was es mir ermöglicht, den schnellen Bearbeitungsmodus programmgesteuert zu deaktivieren. GetStdHandle(STD_INPUT_HANDLE) gibt '3' zurück, die anderen Aufrufe geben einen 7-stelligen Wert zurück, der bei jeder Ausführung des Programms variiert.

0
mickeyf