[c#] 콘솔 응용 프로그램 창 핸들을 얻는 방법


Answers

다음을 수행하는 강력한 방법이 있습니다.

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 , AttachConsole 연결되고 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 이후 각 세션 당 하나씩)의 csrss.exe 에 있으며 관리되며 ReadProcessMemory 와 같이 검색 할 수 없습니다. csrss 노출은 CSRSS LPC API 입니다. 전체 API 목록 인 SrvGetConsoleWindow 에는 관련 함수가 하나 SrvGetConsoleWindow . 또한 PID를 허용하지 않지만 호출 구현 자의 대체 구현 또는 winsrv.dll 에서의 함수 디스 어셈블리와 관련하여이를 결정합니다.

Question

누군가가 C #에서 Windows 콘솔 응용 프로그램의 핸들을 얻는 방법을 말해 줄 수 있습니까? Windows Forms 응용 프로그램에서는 보통 시도 할 것입니다 this.Handle .




콘솔에 진단 도구를 스트리밍하고 마우스 입력을 사용하지 않으려는 콘솔 응용 프로그램에서 GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title) 시도 GetConsoleWindow(), Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title) . 이들 각각은 0이 아닌 핸들을 반환했지만 SetConsoleMode에서 그 핸들을 사용하려고하면 "잘못된 핸들"예외가 발생합니다. 마지막으로 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) 를 -10으로 정의 된 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) 시도해 보았습니다. MS의 문서에 따르면 콘솔에 대한 핸들이 다시 할당 될 수 있으며이 솔루션에 만족스럽지 않거나 만족스럽지 않지만 지금까지는 빠른 편집 모드를 프로그래밍 방식으로 사용하지 않도록 설정할 수있는 유일한 방법입니다. GetStdHandle(STD_INPUT_HANDLE) 은 '3'을 반환하고 다른 호출은 프로그램이 실행될 때마다 달라지는 7 자리 값을 반환합니다.




나는이 문제를 나 자신을 위해 풀어 봤다. (불행히도 토마스의 대답 을보기 전에 훨씬 더 빠름). 글쎄, 그의 대답에 만족하지 않는 사람들을위한 또 다른 방법이있다. 이 답변을 쓰고 싶습니다. 왜냐하면 콘솔을 윈도우로 간주 할 때 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 구현하지 않기 때문에, 제 1 인자에서 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();
}

이제 문제는 여러분 this Main 에서 정적 메서드라는 것을 실제로 호출 할 수 없다는 것입니다. 그래서 Main 있던 것이 무엇이든 저는 Start 라는 이름의 새로운 메서드로 옮겼고 모든 Main 은 새로운 Program 만들고 Start 호출하는 것입니다.

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

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

    throw new Exception();
}

그 결과는 물론 내 콘솔 창을 소유자로 한 메시지 상자였습니다.
이 방법을 메시지 상자에 사용하는 것은 당연히이 방법의 한 응용 프로그램입니다.




Related