C# で Windows コンソール アプリケーションのハンドルを取得する方法を教えてもらえますか? Windows フォーム アプリケーションでは、通常はthis.Handle
.
7 に答える
うまくいくかどうかはわかりませんが、試すことができます:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
これを行うための堅牢な方法を次に示します。
コンソール 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
、 callGetConsoleWindow
を使用してそれにアタッチし、 ですぐにデタッチし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;
};
何かを読み取るためだけに現在のプロセスに変更を加えるのはかなり見苦しいものです (これがコンソール プロセスの場合、現在のコンソールの終了を回避するためにヘルパー プロセスが必要になるため、これは非常に見苦しくなります)。csrss
それにもかかわらず、さらに調査すると、プロセスまたはターゲット プロセスに注入する以外に方法がないことがわかります。
コンソールの対応情報はcsrss.exe
(または、Vista 以降、セッションごとに 1 つずつ、多数の) に配置され、管理されているため、ReadProcessMemory
. csrss
公開するのはCSRSS LPC APIだけです。完全な API リストの中で、関連する関数は 1 つだけSrvGetConsoleWindow
です。また、PID を受け入れませんが、別の実装または の関数の逆アセンブリに見られるように、呼び出し側のそれを決定しwinsrv.dll
ます。
これを試して:
[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);
私はこの問題を自分で解決しました(残念ながら、はるかに速いトーマスの答えを見る前に)。さて、彼の答えに満足できない人のために別の方法があります。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
を呼び出すために自分で実装する必要がありthis
ました。
それと他のすべての実装は次のとおりです。
注: このコードは私のアプリケーションからコピーして貼り付けたものです。アクセス修飾子は自由に変更できます。
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();
}
さて、問題は、静的メソッドである in を実際this
に呼び出すことができないことです.Main
Main
Start
Main
Program
Start
private static void Main()
{
new Program().Start();
}
private void Start()
{
AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;
throw new Exception();
}
もちろん、その結果は、私のコンソールのウィンドウを所有者とするメッセージ ボックスでした。
このメソッドをメッセージ ボックスに使用することは、もちろん、このメソッドの 1 つのアプリケーションにすぎません。
そんなことはないと思います。アプリケーションからコンソール ウィンドウにアクセスできません。あなた自身のプロセス名を探して、プロセスリストを反復しようとするかもしれません. Process
クラス IIRC には、プログラムのメイン ウィンドウ ハンドルのプロパティが含まれています。これは、コンソール アプリケーションのコンソール ウィンドウである可能性がありますが、よくわかりません。
診断をコンソールにストリーミングし、マウス入力を無効にしたいコンソールアプリケーションで、
GetConsoleWindow(),
Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title)
. これらのそれぞれがゼロ以外の同じハンドルを返しましたが、SetConsoleMode でそのハンドルを使用しようとすると、「無効なハンドル」例外がスローされました。最後にSetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS))
、STD_INPUT_HANDLE を -10 として定義してみましたが、うまくいきました。MS のドキュメントは、コンソールへのハンドルが再割り当てされる可能性があることを示唆しており、私はこの解決策に満足していません。GetStdHandle(STD_INPUT_HANDLE)
'3' を返します。他の呼び出しは、プログラムが実行されるたびに変化する 7 桁の値を返します。
コンソール ウィンドウの上に MessageBox を表示する方法に関する VB.Net のもう 1 つのバージョン
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Friend Module NativeMethods
<DllImport("kernel32.dll")> Friend Function GetConsoleWindow() As IntPtr
End Function
End Module
NotInheritable Class WndProxy
Implements IWin32Window
ReadOnly Property Handle As IntPtr Implements IWin32Window.Handle
Sub New(_hwnd As IntPtr)
Handle = _hwnd
End Sub
End Class
Module Module1
Sub Main()
' using MainWindowHandle
Dim w = New WndProxy(Process.GetCurrentProcess().MainWindowHandle)
MessageBox.Show(w, "Hi")
' using GetConsoleWindow api
Dim s = New WndProxy(NativeMethods.GetConsoleWindow)
MessageBox.Show(s, "Hi")
End Sub
End Module