헬마입니다.

이란은 제가 The Old New Thing 을 읽으면서 관심가지는 글들을 번역해서 올려놓는 란입니다.

글의 원문은 이곳입니다.

-----------------------------------------------------------------------------------------

고정된 동사를 실행하는 대신에, 이번에는 사용자에게 컨텍스트 메뉴에서 항목을 고르도록 요청하고 선택한 항목을 실행해보겠습니다.

OnContextMenu 함수를 아래와 같이 수정합니다.

#define SCRATCH_QCM_FIRST 1
#define SCRATCH_QCM_LAST  0x7FFF

#undef HANDLE_WM_CONTEXTMENU
#define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), 0L)

// WARNING! Incomplete and buggy! See discussion
void OnContextMenu(HWND hwnd, HWND hwndContext, int xPos, int yPos)
{
  POINT pt = { xPos, yPos };
  if (pt.x == -1 && pt.y == -1) {
    pt.x = pt.y = 0;
    ClientToScreen(hwnd, &pt);
  }

  IContextMenu *pcm;
  if (SUCCEEDED(GetUIObjectOfFile(hwnd, L"C:\\Windows\\clock.avi",
                   IID_IContextMenu, (void**)&pcm))) {
    HMENU hmenu = CreatePopupMenu();
    if (hmenu) {
      if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
                             SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
                             CMF_NORMAL))) {
        int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,
                                    pt.x, pt.y, hwnd, NULL);
        if (iCmd > 0) {
          CMINVOKECOMMANDINFOEX info = { 0 };
          info.cbSize = sizeof(info);
          info.fMask = CMIC_MASK_UNICODE;
          info.hwnd = hwnd;
          info.lpVerb  = MAKEINTRESOURCEA(iCmd - SCRATCH_QCM_FIRST);
          info.lpVerbW = MAKEINTRESOURCEW(iCmd - SCRATCH_QCM_FIRST);
          info.nShow = SW_SHOWNORMAL;
          pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
        }
      }
      DestroyMenu(hmenu);
    }
    pcm->Release();
  }
}

첫번째 변화는 WM_CONTEXTMENU 메시지 토론에서 가져온 것이고 HANDLE_WM_CONTEXTMENU 메시지를 수정하는 것입니다.

두번째 변화는 두번째 문제인데, 키보드로 실행한 컨텍스트 메뉴에 대한 특별한 처리에 관한 것입니다. 우리가 키보드로 실행된 컨텍스트 메뉴를 받으면, 우리는 마우스 포인터를 클라이언트 영역의 (0,0) 지역으로 옮깁니다. 이렇게 하면 컨텍스트 메뉴가 정상적인 위치에 출력되도록 할 수 있습니다. ( 만약 우리가 객체를 가진 컨테이너라면, 선택된 하위 객체의 위치에 출력하는 것이 더 나은 선택입니다. )

세번째 변화는 실제적으로 우리가 하려는 주제입니다 : 사용자에게 컨텍스트 메뉴를 출력하고, 결과를 얻고, 결과대로 행동하는 거죠.

저는 여러분이 TrackPopupMenuEx 함수 에 많이 익숙하다고 가정합니다. 여기서 우리는 TPS_RETURNCMD 플래그를 설정해서 우리의 창으로 사용자가 선택한 항목이 WM_COMMAND 메시지로 보내지는 대신 함수의 반환값으로 요구합니다.

SCRATCH_QCM_FIRST 의 값이 0이 아닌 1이라는 사실이 눈여겨볼 점입니다. 만약 이 값이 0이라면, 우리는 사용자가 선택한 항목 0과 사용자가 메뉴를 취소한 것을 구분할 수 없게됩니다.

우리는 사용자가 메뉴에서 항목을 선택했다고 확신하고, CMINVOKECOMMANDEX 구조체 에 두개의 동사 필드에 사용자 선택과 ptInvoke 멤버를 통해 실행 위치를 가르키키도록 구조체를 채웁니다.

알림 : 여러분이 메뉴 ID 를 통해 명령을 실행할 때 여러분은 반드시 항목의 시작점으로부터 상대적인 간격(offset) 을 IContextMenu::QueryContextMenu 에게 넘겨줘야합니다. 이것이 우리가 SCRATCH_QCM_FIRST 값을 빼는 이유입니다.

여러분이 이 프로그램을 실행하면, 제대로 작동하지 않는 몇가지를 발견할 수 있을겁니다. 가장 확실한 것중은 연결 프로그램(Open With) 와 보내기(Send To) 가 작동하지 않을 것이고, 더 많은 미묘한 버그도 있을겁니다. 이에 대해서는 며칠 후 언급할 것입니다.

Published Wednesday, September 22, 2004 7:00 AM by oldnewthing

댓글을 달아 주세요