Ответ 1
Не уверен, что это работает, но вы можете попробовать следующее:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
Может ли кто-нибудь сказать мне, как получить дескриптор консольного приложения Windows на С#? В приложении Windows Forms я обычно пытаюсь this.Handle
.
Не уверен, что это работает, но вы можете попробовать следующее:
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
Process.MainWindowHandle
работает только для процесса, который запустил консольFindWindowByCaption
по своей сути ненадеженВот надежный способ сделать это:
Связанные функции из 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
.
Попробуйте следующее:
[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
. Я действительно превратил его в класс, в котором есть программа, а не только один метод, который представляет его, и использует другие классы для контента. (Если вы не знаете, что я имею в виду, не важно).
Причина, по которой я должен был это сделать, - это то, что я хотел написать следующий обработчик событий:
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();
}
В результате я получил сообщение с моим консольным окном в качестве владельца.
Использование этого метода для сообщения-сообщения, конечно, является только одним применением этого метода.
Я не думаю, что есть такая вещь. Консольное окно недоступно для приложения. Вы МОЖЕТЕ попытаться перебрать список процессов, ища собственное имя процесса. Класс Process
IIRC содержит свойство для дескриптора главного окна программы, которое может быть консольным окном для консольных приложений, о котором я не уверен.
В консольном приложении, которое транслировало диагостику на консоль и для которой я хотел отключить ввод мыши, я попробовал
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-значное значение, которое меняется при каждом запуске программы.