[WinAPI] SetCapture, ReleaseCapture, 영역 밖에서도 WM_MOUSEMOVE 받기

발을 하다보면, 프로세스 영역 바깥에서는 WM_MOUSEMOVE 이벤트가 받아지지 않습니다.

이렇게 마우스가 영역 밖으로 이동했을 때 WM_MOUSEMOVE를 받고 싶으면 SetCapture, ReleaseCapture를 사용합니다.
SetCaptrue 함수 원형은 아래와 같습니다.

HWND SetCapture( HWND hWnd );

이전에 SetCapture를 했던 HWND가 있다면 이전 HWND를 반환합니다. 없다면 NULL을 반환합니다.
SetCapture를 했다면 후에 ReleaseCapture를 호출해야합니다.
ReleaseCapture를 하면 영역 밖에서 WM_MOUSEMOVE가 더 이상 받아지지 않습니다.

ReleaseCapture 함수 원형은 아래와 같습니다.

BOOL ReleaseCapture();

함수가 실패하면 return 값은 0입니다. 이 경우 GetLastError를 호출하여 실패 이유를 확인할 수 있습니다.

위 함수들을 사용할때 유의사항은 SetCapture는 Mouse 버튼이 눌려있어야 영역 밖에서 메시지가 날아옵니다.

SetCapture captures mouse input either when the mouse is over the capturing window, or when the mouse button was pressed while the mouse was over the capturing window and the button is still down

예제를 만들기 위해서 아래와 같이 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE를 추가합니다.

그리고 아래와 같이 코딩합니다.

void CMFCApplication1Dlg::OnLButtonDown( UINT nFlags, CPoint point )
{
    // 이전에 캡쳐된 HWND 반환, 없으면 NULL 반환
    HWND hwnd = ::SetCapture( this->GetSafeHwnd() );
 
    CDialogEx::OnLButtonDown( nFlags, point );
}
 
void CMFCApplication1Dlg::OnLButtonUp( UINT nFlags, CPoint point )
{
    // SetCapture 이후에 호출해야함
    BOOL bRes = ::ReleaseCapture();
 
    CDialogEx::OnLButtonUp( nFlags, point );
}
 
void CMFCApplication1Dlg::OnMouseMove( UINT nFlags, CPoint point )
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    m_sDisp.Format( _T( "(%d, %d)\n" ), point.x, point.y );
    UpdateData( FALSE );
 
    CDialogEx::OnMouseMove( nFlags, point );
}

실행해서 확인해봅니다.