web-dev-qa-db-ja.com

コンソールアプリケーションのウィンドウのハンドルを取得する方法

誰かがC#でWindowsコンソールアプリケーションのハンドルを取得する方法を教えてもらえますか? Windows Formsアプリケーションでは、通常this.Handle

41
Grant

動作するかどうかはわかりませんが、試してみてください:

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

これを行うための堅牢な方法を次に示します。

Console Win32 API の関連関数は次のとおりです。

_[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();
_
  • 現在のプロセスが接続されているコンソールの場合、GetConsoleWindow()で十分です
  • 別のプロセスが接続されているコンソールの場合は、AttachConsoleを使用して接続し、GetConsoleWindowを呼び出し、すぐにFreeConsoleを使用して切り離します。

さらに慎重にするには、接続する前にコンソールイベントハンドラーを登録(およびデタッチ後に登録解除)して、コンソールに接続している短い時間枠でコンソールイベントが発生した場合に誤って終了しないようにします。

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

何かを読むためだけに現在のプロセスに変更を加えることはかなりugいです(これがコンソールプロセスである場合、現在のコンソールの終了を避けるためにヘルパープロセスが必要になるため、これは本当にいものになります)。それでも、さらなる調査により、csrssプロセスまたはターゲットプロセスに注入する方法は他にないことが示されています。

コンソール通信情報は、_csrss.exe_(またはVista以来、セッションごとに1つ)に配置されて管理されているため、ReadProcessMemoryなどで取得することはできません。 csrssが公開するのは、 CSRSS LPC API だけです。完全なAPIリストには、SrvGetConsoleWindowという関連する関数が1つしかありません。また、PIDは受け入れませんが、 代替実装 で見られるように呼び出し側のそれを決定するか、_winsrv.dll_で関数の逆アセンブリを決定します。

25
ivan_pozdeev

これを試して:

[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

私は自分でこの問題を解決しました(残念ながら Thomasの答え の方がずっと早いです)。さて、彼の答えに満足していない人のための別の方法があります。コンソールをウィンドウとして扱う場合は、別の回答+ Programクラスを設計するより良い方法を提供したいので、この回答を書いています。その設計から始めましょう:

Programクラスのデフォルトのスタイルを少し変更しました。実際に、プログラムを含むクラスを作成しました。それを表す1つのメソッドだけでなく、コンテンツに他のクラスを使用します。 (意味がわからない場合、重要ではありません)。

私がそれをしなければならなかった理由は、次のイベントハンドラを記述したかったからです。

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

このメソッドをオーバーロードします MessageBox.Show(IWin32Window, String, String)

Consoleは _IWin32Window_ を実装していないため、もちろん1でthisを呼び出すために、自分で実装する必要がありましたst 引数。

ここにそれと他のすべての実装があります:

注:このコードはアプリケーションからコピーアンドペーストされています。アクセス修飾子を自由に変更できます

Programクラス宣言:

_internal class Program : IWin32Window
{
    ...
}
_

_IWin32Window_ 実装:

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

次のクラスを使用します。

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

現在、問題は、静的メソッドであるthisMainを実際に呼び出すことができないため、MainにあるものはすべてStartという名前の新しいメソッドに移動し、すべてのMainが実行しているのは新しいProgramを作成してStartを呼び出すことです.

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

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

    throw new Exception();
}
_

もちろん、結果は所有者としてのコンソールのウィンドウを持つメッセージボックスでした。
この方法をメッセージボックスに使用することは、もちろんこの方法の1つのアプリケーションにすぎません。

7
MasterMastic

そんなことはないと思います。コンソールウィンドウは、アプリケーションからアクセスできません。独自のプロセス名を探してプロセスリストを反復処理する場合があります。 ProcessクラスIIRCには、プログラムのメインウィンドウハンドルのプロパティが含まれています。これはmightコンソールアプリケーションのコンソールウィンドウです。

1

ダイアゴスティックをコンソールにストリーミングし、マウス入力を無効にしたいコンソールアプリケーションで、GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title)を試しました。これらはそれぞれ、ゼロ以外の同じハンドルを返しましたが、SetConsoleModeでそのハンドルを使用しようとすると、「無効なハンドル」例外がスローされました。最後に、STD_INPUT_HANDLEを-10として定義してSetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))を試しましたが、うまくいきました。 MSのドキュメントでは、コンソールへのハンドルを再割り当てできることが示唆されており、このソリューションに満足したり満足したりすることはできませんが、これまでのところ、プログラムでクイック編集モードを無効にできるのはそれだけです。 GetStdHandle(STD_INPUT_HANDLE)は '3'を返し、他の呼び出しはプログラムが実行されるたびに異なる7桁の値を返します。

0
mickeyf