본문 바로가기

C#/Windows

WndProc으로 Windows Message 제어하기

728x90

Windows Message

여기서 말하는 Message는 MessageBox에 출력되는 문자열이 아니다. Message는 Windows 안에서 이용되는 구조체이다. Windows는 그 안에 값들을 담아서 자신이 제어할 대상에 보낸다. 예를 들어 윈도우즈를 이용하는 사람이 메모장을 실행한 상태에서 키보드의 키를 누르면 윈도우즈는 이 키에 대한 여러 값들 즉 어떤 문자에 해당하는 키인지 키를 누른 건지 뗀 건지 등을 넣어서 메모장의 편집 영역으로 보낸다.

Windows로 메시지를 보낼 땐 SendMessage와 PostMessage 함수를 쓴다. 이것들은 win32 api에 들어있다. 전자는 메시지를 윈도우즈로 보내는 거다. 처리가 오래 걸리면 프로시저가 멈추지만 이를 감수하고라도 바로 처리해야 할 때 쓴다. 후자는 윈도우즈로 보내서 처리하지 않고 큐에 담는다. 그러면 윈도우즈가 처리할 수 있을 때 큐에 담긴 순서대로 처리한다. 데드록을 막기 위해서다.

SendMessage PostMessage
윈도우 프로시저를 직접 호출하며, 프로시저가 메시지를 처리할 때 까지 반환하지 않는다. 메시지 큐에 메시지가 삽입되며, 윈도우 프로시저에서 메시지를 처리한다. 해당 메시지가 언제 처리될 지 예측이 어렵다.
순차적으로 처리(sequentially) 비 순차적으로 처리(not sequentially)
동기 방식(synchronous) 비동기 방식(asynchronous)

Windows로부터 메시지를 받아서 처리할 때에는 WndProc 메서드를 이용한다.

 

WndProc 메서드

WndProc은 Win32 API에 들어있는 콜백 함수이다. 가만히 있을 땐 실행되지 않고 Windows가 메시지를 보낼 때 작동하는 메서드가 WndProc이다.

 

C#에서 WndProc 사용하기 - WndProc 오버라이드하기

int MessageID;

protected override void WndProc(ref Message m)
{
    if (m.Msg == 12345) // 원하는 메시지 id
    {
        MessageID = m.Msg;
    }
    else
    {
        base.WndProc(ref m);
    }
}

private void label1_Click(object sender, EventArgs e)
{
    label1.Text = MessageID.ToString();
}

주의할 점

  • 멀티스레드 이용 시 주의할 점
    • 메세지를 argument로 스레드에 실어서 보낼 때 주의해야 한다. Message는 struct 즉, value 타입이라 원칙적으로 문제될게 없지만 WndProc에서는 ref 키워드가 붙어서 레퍼런스 타입으로 처리되는데 유의한다. 문제를 피하기 위해서는 메시지 자체를 argument로 사용하면 안 되고 인스턴스를 만들어서 고유하게 한 다음에 이 인스턴스를 argumetn로 사용해야 한다.
  • MessageBox 이용 시 주의할 점
    • WndProc은 끊임없이 작동하는데 그 안에서 MessageBox를 실행하면 이후의 작업들이 멈춘다. MessageBox를 클릭하여 없애면 기다리던 작업들이 실행이 되기는 하는데 오작동한다.
  • WPF의 경우
    • WndProc은 System.Windows.Forms에 있는거라 WPF로는 이용할 수 없다. WPF에서 윈도우즈 메시지를 제어하려면 조금 더 복잡해진다.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;

    hwndSource.AddHook(WindowsMessageHook);
}

private IntPtr WindowsMessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    TextBlock1.Text = msg.ToString();

    return IntPtr.Zero;
}

 

[출처]

https://luckygg.tistory.com/174

https://shinjguk.com/archives/498

728x90

'C# > Windows' 카테고리의 다른 글

윈도우 프로그래밍 동작 방식  (0) 2023.11.05
Win32 API에서 제공하는 자료형  (0) 2023.11.05
Win64 API가 있나요?  (0) 2023.11.05
Windows API  (0) 2023.11.05