Как получить дескриптор окна консольного приложения

Может ли кто-нибудь сказать мне, как получить дескриптор консольного приложения Windows на С#? В приложении Windows Forms я обычно пытаюсь this.Handle.

Ответ 1

Не уверен, что это работает, но вы можете попробовать следующее:

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;

Ответ 2

Вот надежный способ сделать это:

Связанные функции из 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;
};

Внедрение изменений в текущем процессе просто для чтения является довольно уродливым (когда это консольный процесс, это становится очень уродливым, поскольку для этого требуется вспомогательный процесс, чтобы избежать завершения текущей консоли). Тем не менее, дальнейшие исследования показывают, что другого способа, кроме инъекции в процесс csrss или целевого процесса, нет.

Информация о ReadProcessMemory консоли находится внутри и управляется csrss.exe (или множеством из них, по одному для каждого сеанса, начиная с Vista), поэтому его нельзя получить с помощью ReadProcessMemory. Все, что предоставляет csrss это API CSRSS LPC. Там только одна соответствующая функция в полном списке API, SrvGetConsoleWindow. И он не принимает PID, но определяет, что вызывающей стороны рассматривается в альтернативной реализации или разборки функции в winsrv.dll.

Ответ 3

Попробуйте следующее:

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

Ответ 4

Я только что решил эту проблему для себя (к сожалению, прежде чем увидеть ответ Томаса, который намного быстрее). Ну, вот еще один способ для тех, кто не удовлетворен его ответом. Я пишу этот ответ, потому что хочу предоставить другой ответ + лучший способ создать класс Program, если вы рассматриваете консоль как окно. Начнем с этого дизайна:

Я изменил стиль по умолчанию класса Program. Я действительно превратил его в класс, в котором есть программа, а не только один метод, который представляет его, и использует другие классы для контента. (Если вы не знаете, что я имею в виду, не важно).

Причина, по которой я должен был это сделать, - это то, что я хотел написать следующий обработчик событий:

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).

Поскольку консоль не реализует IWin32Window, я должен был реализовать ее сам, конечно, чтобы просто позвонить this в аргументе 1 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();
}

Теперь проблема состоит в том, что вы фактически не можете вызвать this в Main, будучи статическим методом, так что все, что было в Main, я перешел к новому методу с именем Start и всем Main делает создание нового Program и вызывает Start.

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

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

    throw new Exception();
}

В результате я получил сообщение с моим консольным окном в качестве владельца.

Использование этого метода для сообщения-сообщения, конечно, является только одним применением этого метода.

Ответ 5

Я не думаю, что есть такая вещь. Консольное окно недоступно для приложения. Вы МОЖЕТЕ попытаться перебрать список процессов, ища собственное имя процесса. Класс Process IIRC содержит свойство для дескриптора главного окна программы, которое может быть консольным окном для консольных приложений, о котором я не уверен.

Ответ 6

В консольном приложении, которое транслировало диагостику на консоль и для которой я хотел отключить ввод мыши, я попробовал GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title). Каждый из них возвращал тот же ненулевой дескриптор, но когда я попытался использовать этот дескриптор в SetConsoleMode, он выбрал исключение "Invalid Handle". Наконец, я попробовал SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) с STD_INPUT_HANDLE, определенным как -10, и он сработал. Документация MS предполагает, что ручки для консолей могут быть переназначены, и я не устраиваю это или удовлетворен этим решением, но пока это единственное, что я нашел, что позволяет мне отключить быстрый режим редактирования программно. GetStdHandle(STD_INPUT_HANDLE) возвращает "3", другие вызовы возвращают 7-значное значение, которое меняется при каждом запуске программы.