업데이트: 2007년 11월
이 항목에서는 Dispatcher에서 노출하는 기존 메시지 루프를 사용하거나 상호 작용 코드의 Win32측에서 별도의 메시지 루프를 생성하여 WPF(Windows Presentation Foundation)와의 상호 작용을 위한 메시지 루프를 구현하는 방법에 대해 설명합니다.
ComponentDispatcher와 메시지 루프
상호 운용과 키보드 이벤트 지원을 위한 일반적인 시나리오는 IKeyboardInputSink를 구현하거나 HwndSource 또는 HwndHost처럼 이미 IKeyboardInputSink가 구현되어 있는 클래스에서 서브클래싱하는 것입니다. 하지만 키보드 싱크를 지원한다고 해서 상호 운용 경계를 통해 메시지를 보내고 받을 때 발생할 수 있는 모든 가능한 메시지 루프 요구 사항이 해결되는 것은 아닙니다. 응용 프로그램 메시지 루프 아키텍처를 쉽게 형식화할 수 있도록 WPF(Windows Presentation Foundation)는 메시지 루프가 따라야 하는 간단한 프로토콜을 정의하는 ComponentDispatcher 클래스를 제공합니다.
ComponentDispatcher는 다양한 멤버를 노출하는 정적 클래스입니다. 각 메서드의 범위는 암시적으로 호출 스레드에 연결됩니다. 다음 단원에서 설명하는 중요한 시점에서는 메시지 루프가 이러한 API 중 일부를 호출해야 합니다.
ComponentDispatcher는 키보드 싱크와 같은 다른 구성 요소에서 수신할 수 있는 이벤트를 제공합니다. Dispatcher 클래스는 적절한 순서로 적절한 ComponentDispatcher 메서드를 호출합니다. 고유한 메시지 루프를 구현하는 경우에는 코드에서 유사한 방식으로 ComponentDispatcher 메서드를 호출해야 합니다.
스레드에서 ComponentDispatcher 메서드를 호출하면 해당 스레드에서 등록한 이벤트 처리기만 호출됩니다.
메시지 루프 작성
다음은 고유한 메시지 루프를 작성할 경우 사용하게 되는 ComponentDispatcher 멤버의 검사 목록입니다.
PushModal: 스레드가 모달임을 나타내려면 메시지 루프에서 이 메서드를 호출해야 합니다.
PopModal: 스레드가 모달이 아닌 스레드로 되돌려졌음을 나타내려면 메시지 루프에서 이 메서드를 호출해야 합니다.
RaiseIdle: ComponentDispatcher가 ThreadIdle 이벤트를 발생시켜야 한다는 것을 나타내려면 메시지 루프에서 이 메서드를 호출해야 합니다. IsThreadModal이 true인 경우 ComponentDispatcher는 ThreadIdle 이벤트를 발생시키지 않지만 ComponentDispatcher가 모달 상태인 동안 RaiseIdle 이벤트에 응답할 수 없는 경우에도 메시지 루프에서 해당 이벤트를 선택적으로 호출할 수 있습니다.
RaiseThreadMessage: 새 메시지를 사용할 수 있음을 나타내려면 메시지 루프에서 이 메서드를 호출해야 합니다. 반환 값은 ComponentDispatcher 이벤트 수신기가 메시지를 처리했는지 여부를 나타냅니다. RaiseThreadMessage에서 true(처리됨)를 반환하는 경우 디스패처는 더 이상 메시지를 처리하지 않습니다. 반환 값이 false이면 디스패처는 Win32 함수인 TranslateMessage를 호출한 다음 DispatchMessage를 호출해야 합니다.
ComponentDispatcher 및 기존 메시지 처리 사용
다음은 고유한 WPF 메시지 루프에 의존하는 경우 사용하게 되는 ComponentDispatcher 멤버의 검사 목록입니다.
IsThreadModal: 응용 프로그램이 모달 상태가 되었는지 여부(예: 모달 메시지 루프가 푸시되었는지 여부)를 반환합니다. ComponentDispatcher 클래스는 메시지 루프의 PushModal 및 PopModal 호출 횟수를 유지하기 때문에 이 상태를 추적할 수 있습니다.
ThreadFilterMessage 및 ThreadPreprocessMessage 이벤트는 대리자 호출에 대한 표준 규칙을 따릅니다. 대리자는 임의의 순서로 호출되며 첫 번째 대리자가 메시지를 처리된 것으로 표시하는 경우에도 모든 대리자가 호출됩니다.
ThreadIdle: 유휴 상태를 처리할 수 있는 적당하고 효율적인 시간(스레드에 다른 보류 중인 메시지가 없는 시간)을 나타냅니다. 스레드가 모달인 경우 ThreadIdle이 발생하지 않습니다.
ThreadFilterMessage: 메시지 펌프를 처리하는 모든 메시지에서 발생합니다.
ThreadPreprocessMessage: ThreadFilterMessage 중에 처리되지 않은 모든 메시지에서 발생합니다.
ThreadFilterMessage 이벤트나 ThreadPreprocessMessage 이벤트가 발생한 후 이벤트 데이터에서 참조로 전달되는 handled 매개 변수가 true이면 메시지가 처리된 것으로 간주됩니다. handled가 true이면 다른 처리기에서 메시지를 먼저 처리했다는 의미이므로 이벤트 처리기가 메시지를 무시해야 합니다. 두 이벤트에 대한 이벤트 처리기는 메시지를 수정합니다. 디스패처는 변경되지 않은 원래 메시지가 아니라 수정된 메시지를 디스패치해야 합니다. ThreadPreprocessMessage가 모든 수신기로 전달되지만 아키텍처상의 용도는 메시지의 대상인 HWND를 포함하는 최상위 창에서만 메시지에 대한 응답으로 코드를 호출하는 것입니다.
HwndSource에서 ComponentDispatcher 이벤트를 처리하는 방법
HwndSource가 최상위 창인 경우(부모 HWND가 없는 경우) ComponentDispatcher에 등록됩니다. ThreadPreprocessMessage가 발생하고 메시지가 HwndSource 또는 자식 창에서 사용되는 경우 HwndSource는 해당 IKeyboardInputSink.TranslateAccelerator, TranslateChar, OnMnemonic 키보드 싱크 시퀀스를 호출합니다.
HwndSource가 최상위 창이 아닌 경우(부모 HWND가 있는 경우) 처리가 수행되지 않습니다. 최상위 창만 처리를 수행해야 하며 상호 운용 시나리오의 일부로 키보드 싱크를 지원하는 최상위 창이 있어야 합니다.
HwndSource의 WndProc가 먼저 호출되는 적절한 키보드 싱크 메서드 없이 호출되면 응용 프로그램은 KeyDown과 같은 더 높은 수준의 키보드 이벤트를 받게 됩니다. 하지만 어떠한 키보드 싱크 메서드도 호출되지 않으므로 선택키 지원과 같은 원하는 키보드 입력 모델을 사용할 수 없게 됩니다. 메시지 루프가 ComponentDispatcher에서 관련 스레드를 올바르게 통지하지 않거나 부모 HWND가 적절한 키보드 싱크 응답을 호출하지 않은 경우 이러한 현상이 발생할 수 있습니다.
AddHook 메서드를 사용하여 키보드 싱크로 전달되는 메시지에 대한 후크를 추가한 경우 해당 메시지가 HWND로 전송되지 않을 수 있습니다. 이 메시지가 메시지 펌프 수준에서 처리되고 DispatchMessage 함수로 제출되지 않을 수 있습니다.