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

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


person Grant    schedule 14.08.2009    source источник


Ответы (7)


Не уверен, что это работает, но вы можете попробовать это:

IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
person Thomas Levesque    schedule 14.08.2009
comment
Это сработало для тебя, Грант? Просто любопытно, потому что я мог бы использовать это и в паре мест :) - person Mark Carpenter; 14.08.2009
comment
Для всех, кто ищет 2-22 года спустя; да, это сработало (для меня). - person John Hoven; 22.06.2011
comment
похоже, это работает, только если ваш процесс запустил консоль. если вы запускаете его с другой консоли, вы всегда получаете ноль. похоже, вам нужно использовать findwindowbycaption в других ситуациях (см. support.microsoft.com/kb/124103) - person John Gardner; 14.07.2011
comment
Это работало для меня при отладке в Visual Studio, но когда я запускал без отладки, это не сработало. Двойная синхронизация myProgram.exe в bin/Release действительно работала, но FindWindowByCaption может быть более надежным решением. - person Carl Walsh; 20.07.2013
comment
Раньше я всегда относился к этому окну с большим уважением и никогда не мог манипулировать им с помощью кода, но это простое окно, которое мало чем отличается от других. Строка выше работает, и вы также можете перечислить и найти ее через WinApi и делать с ней всевозможные сумасшедшие вещи. Просто будьте осторожны, у него есть WndProc, который немного меняет размер. - person Bitterblue; 19.08.2013
comment
@MarkCarpenter извините за поздний ответ. Точно не помню, но помечено как принятое, так что скорее всего :) - person Grant; 24.03.2021

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

Связанные функции из Консольный 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 или целевой процесс.

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

person ivan_pozdeev    schedule 19.02.2015
comment
Это должен быть принятый ответ. GetConsoleWindow — правильный способ сделать это; многие другие ответы здесь просто глупы. - person James Johnston; 14.09.2015
comment
Это правильный ответ ИМХО, если вы не хотите тратить время на выяснение того, почему ваше приложение не работает, когда вы запускаете его из консоли, а не из IDE. Спасибо Иван. - person WiredEarp; 04.08.2016

Попробуй это:

[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);
person Zain Ali    schedule 04.02.2011

Я только что решил эту проблему для себя (к сожалению, до того, как увидел ответ Томаса, который намного быстрее). Ну вот еще способ для тех, кого его ответ не устраивает. Я пишу этот ответ, потому что хочу дать другой ответ + лучший способ разработки класса 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 в 1st аргументе.

Вот реализация этого и всего остального:

Примечание: этот код скопирован из моего приложения, вы можете свободно изменять модификаторы доступа

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();
}

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

person MasterMastic    schedule 24.02.2013
comment
Вкратце ответ таков: [DllImport(kernel32.dll)] внутренний статический внешний IntPtr GetConsoleWindow(); - person Patrick from NDepend team; 31.07.2013

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

person Thorsten Dittmar    schedule 14.08.2009
comment
перебирать список процессов в поисках собственного имени процесса => не очень эффективный подход... вы можете найти его по PID или использовать } ;) - person Thomas Levesque; 14.08.2009
comment
Упс - ответ Томаса Левеска еще более элегантен. Полагаясь на одно и то же свойство, ему не нужно повторять. Я забыл, что вы можете получить доступ к текущему процессу напрямую... - person Thorsten Dittmar; 14.08.2009
comment
@Thomas: Извините, не видел вашего комментария раньше. Конечно, итерация гораздо менее эффективна. Я не запомнил метод GetCurrentProcess()... - person Thorsten Dittmar; 14.08.2009

В консольном приложении, которое транслировало диагностику на консоль и для которого я хотел отключить ввод с помощью мыши, я попробовал 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-значное значение, которое изменяется при каждом запуске программы.

person MickeyfAgain_BeforeExitOfSO    schedule 21.06.2017

Еще одна версия в VB.Net о том, как показать MessageBox поверх окна консоли.

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
person volody    schedule 23.03.2021