2009-08-14 11 views

risposta

47

Non sono sicuro che funziona, ma si può provare che:

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle; 
+0

Questo lavoro ti è stato concesso? Solo curioso, perché potrei usarlo anche in un paio di posti :) – Pwninstein

+0

Per chiunque altro guardi 2-22 anni dopo; sì, questo ha funzionato (per me). –

+6

sembra funzionare solo se il processo ha avviato la console. se lo esegui da un'altra console, ottieni sempre zero. sembra che tu abbia bisogno di usare findwindowbycaption in altre situazioni (vedi http://support.microsoft.com/kb/124103) –

1

Non penso che ci sia una cosa del genere. La finestra della console non è accessibile all'applicazione. POTETE tentare di iterare l'elenco dei processi cercando il proprio nome di processo. La classe IIRC Process contiene una proprietà per l'handle della finestra principale del programma, che potrebbe essere la finestra della console per le applicazioni console, di cui non sono sicuro.

+0

"iterare l'elenco dei processi cercando il proprio nome di processo" => non è un approccio molto efficiente ... lo si può trovare dal PID, o usare};) –

+0

Whoops - La risposta di Thomas Levesque è ancora più elegante.Pur facendo affidamento sulla stessa proprietà, non ha bisogno di iterare. Ho dimenticato che puoi accedere direttamente al processo corrente ... –

+0

@Thomas: Spiacente, non hai visto il tuo commento prima. Ovviamente, l'iterazione è molto più inefficiente. Non ricordavo il metodo GetCurrentProcess() ... –

9

Prova questo:

[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); 
8

Ho appena risolto questo problema per me (purtroppo prima di vedere Thomas's answer che è molto più veloce). Bene, ecco un altro modo per coloro che non sono soddisfatti della sua risposta. Sto scrivendo questa risposta perché voglio fornire un'altra risposta + un modo migliore per progettare la classe Program se stai trattando la tua console come una finestra. Iniziamo con questo progetto:

Ho cambiato lo stile predefinito della classe Program. In realtà l'ho trasformato in una classe che contiene un programma e non solo un metodo che lo rappresenta e utilizza altre classi per il contenuto. (Se non sai cosa intendo, non è importante).

La ragione per cui ho dovuto farlo è perché volevo scrivere la seguente gestore di eventi:

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

sovraccarica questo metodo MessageBox.Show(IWin32Window, String, String).

Perché Console non implementa IWin32Window, ho dovuto implementare io stesso, naturalmente, al fine di chiamare solo this nel argomento 1 st.

Ecco l'attuazione di esso e tutto il resto:

Nota: questo codice è copia-incollato da mia domanda, si può sentire liberi di cambiare i modificatori di accesso

Program Dichiarazione di Classe :

internal class Program : IWin32Window 
{ 
    ... 
} 

IWin32Window Implementazione:

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

Esso utilizza la seguente classe:

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

Ora, il problema è che non si può effettivamente chiamare this in Main, essendo un metodo statico, quindi tutto ciò che era in Main mi sono trasferito a un nuovo metodo denominato Start e tutto ciò che sta facendo Main sta creando un nuovoe chiamando Start.

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

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

    throw new Exception(); 
} 

Il risultato è stato, ovviamente, un messaggio-box con la finestra della mia console come proprietario.
L'utilizzo di questo metodo per una casella di messaggio è ovviamente solo una applicazione di questo metodo.

+2

In breve la risposta è: [DllImport ("kernel32.dll")] internal static extern IntPtr GetConsoleWindow(); –

12

Ecco un modo robusto per fare questo:

Le funzioni correlate dal Console Win32 API sono:

[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(); 
  • Per la console l'attuale processo è collegato a, appena GetConsoleWindow() è sufficiente
  • Per la console un altro processo è collegato a, allegare ad esso pure con AttachConsole, chiamare GetConsoleWindow, li staccano subito con FreeConsole.

Per la cauta in più, registrare un gestore di eventi console prima di collegare (e annullare la registrazione dopo il distacco) e in modo da non accidentalmente chiusa se un evento di console avviene nel lasso di tempo molto piccolo siete attaccati a la console:

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

di apportare modifiche al processo in corso solo per leggere qualcosa è piuttosto brutto (se questo è un processo di console, questo diventa veramente brutto in quanto richiede un processo di aiuto per evitare che chiude la console corrente). Tuttavia, ulteriori indagini dimostrano che non c'è altro modo per iniettare nel processo csrss o nel processo di destinazione.

Le informazioni sulla corrispondenza della console si trovano e sono gestite da csrss.exe (o una moltitudine di quelle, una per ogni sessione, da Vista), quindi non possono essere recuperate con numeri simili a ReadProcessMemory. Tutto ciò che espone csrss è lo CSRSS LPC API. C'è solo una funzione rilevante nell'elenco completo delle API, SrvGetConsoleWindow. E non accetta un PID ma determina quello della parte chiamante come visto in an alternative implementation o lo smontaggio della funzione in winsrv.dll.

+3

Questa dovrebbe essere la risposta accettata. GetConsoleWindow è il modo giusto per farlo; molte delle altre risposte qui sono semplicemente sciocche. –

+1

Questa è la risposta corretta IMHO, a meno che non si desideri dedicare del tempo a capire perché l'applicazione non funziona quando la si esegue dalla console anziché dall'IDE. Grazie Ivan. – WiredEarp

0

In un'applicazione console che ha trasmesso la diagostica alla console e per la quale volevo disabilitare l'input del mouse, ho provato GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title). Ognuno di questi ha restituito lo stesso handle diverso da zero, ma quando ho provato a utilizzare quell'handle in SetConsoleMode ha generato un'eccezione "Handle non valido". Alla fine ho provato SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) con STD_INPUT_HANDLE definito come -10, e ha funzionato. La documentazione di MS suggerisce che le maniglie per console possono essere riassegnate, e non sono a mio agio o felice con questa soluzione, ma finora è l'unica cosa che ho trovato che mi permette di disabilitare la modalità di modifica rapida a livello di programmazione. GetStdHandle(STD_INPUT_HANDLE) restituisce '3', le altre chiamate restituiscono un valore di 7 cifre che varia ogni volta che viene eseguito il programma.