如何通过 OCM_ 消息处理未处理的 Win32 消息反射到子级控件

How to handle unhandled Win32 Message Reflection to Sublcassed Controls via OCM_ messages

我在 CodeProject 上找到了以下内容。这是有道理的,除非子类控件不处理 OCM_ 消息,这意味着原始消息的默认处理永远不会发生。是否有一个优雅的解决方案,而不是必须始终同步此函数发送的消息与子类 windows 过程?

LRESULT DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
  switch (umsg) {
    case WM_NOTIFY:
    {
      NMHDR* nmhdr = (NMHDR*)lparam;
      if (nmhdr->hwndFrom != NULL)
        return SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, lparam);
      break;
    }

    // All of these provide the control's HHWND in LPARAM
    case WM_COMMAND:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORDLG:
    case WM_CTLCOLORLISTBOX:
    case WM_CTLCOLORMSGBOX:
    case WM_CTLCOLORSCROLLBAR:
    case WM_CTLCOLORSTATIC:
    case WM_VKEYTOITEM:
    case WM_CHARTOITEM:
      if (lparam != 0)
        return SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, lparam);
      break;

  // All of these provide ID of the control in WPARAM:
    case WM_DRAWITEM:
    case WM_MEASUREITEM:
    case WM_DELETEITEM:
    case WM_COMPAREITEM:
      if (wparam != 0) {
        HWND hwndControl = GetDlgItem(hwnd, wparam);
        if (hwndControl)
          return SendMessage(hwndControl, umsg + OCM__BASE, wparam, lparam);
      }
      break;

    // Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that 
    // usually does not make much sense.
  }

  return DefWindowProc(hwnd, umsg, wparam, lparam);
}

这里没有真正的干净解决方案。

您可以确保所有 child 控件在为未处理的 OCM_... 消息调用 DefWindowProc() 时减去 OCM__BASE

LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
  switch (umsg) {
    ...
  }

  if ((umsg >= OCM__BASE) && (umsg <= OCM__MAX)) {
    umsg -= OCM__BASE;
  }

  return DefWindowProc(hwnd, umsg, wparam, lparam);
}

否则,您可以让每个 OCM_... 消息在其 WPARAMLPARAM 中携带一个指向结构的指针,其中结构包含真正的 WPARAM/LPARAM 和输出 LRESULT,然后每个 child 控件可以 return TRUE 如果给定的 OCM_... 消息被处理。如果 SendMessage(OCM_...) return 为 FALSE,parent 然后可以使用原始 WM_... 消息调用 DefWindowProc()

struct OCMInfo
{
  LPARAM lParam;
  LRESULT lResult;
};

LRESULT WINAPI DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
  switch (umsg) {
    case WM_NOTIFY:
    {
      NMHDR* nmhdr = (NMHDR*)lparam;
      if (nmhdr->hwndFrom != NULL)
      {
        OCMInfo info;
        info.lParam = lParam;
        info.lResult = 0;
        if (SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, (LPARAM)&info))
          return info.lResult;
      }
      break;
    }

    // All of these provide the control's HHWND in LPARAM
    case WM_COMMAND:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORDLG:
    case WM_CTLCOLORLISTBOX:
    case WM_CTLCOLORMSGBOX:
    case WM_CTLCOLORSCROLLBAR:
    case WM_CTLCOLORSTATIC:
    case WM_VKEYTOITEM:
    case WM_CHARTOITEM:
      if (lparam != 0)
      {
        OCMInfo info;
        info.lParam = lParam;
        info.lResult = 0;
        if (SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, (LPARAM)&info))
          return info.lResult;
      }
      break;

  // All of these provide ID of the control in WPARAM:
    case WM_DRAWITEM:
    case WM_MEASUREITEM:
    case WM_DELETEITEM:
    case WM_COMPAREITEM:
      if (wparam != 0) {
        HWND hwndControl = GetDlgItem(hwnd, wparam);
        if (hwndControl)
        {
          OCMInfo info;
          info.lParam = lParam;
          info.lResult = 0;
          if (SendMessage(hwndControl, umsg + OCM__BASE, wparam, (LPARAM)&info))
            return info.lResult;
        }
      }
      break;

    // Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that 
    // usually does not make much sense.
  }

  return DefWindowProc(hwnd, umsg, wparam, lparam);
}

LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
  switch (umsg) {
    case OCM__NOTIFY:
    {
      OCMInfo* info = (OCMInfo*)lparam;
      NMHDR* nmhdr = (NMHDR*)(info->lparam);
      if (...) {
        ...
        info->lResult = ...;
        return TRUE;
      }
      break;
    }

    case OCM__COMMAND:
    {
      OCMInfo* info = (OCMInfo*)lparam;
      if (...) {
        ...
        info->lResult = ...;
        return TRUE;
      }
      break;
    }

    ...
  }

  return DefWindowProc(hwnd, umsg, wparam, lparam);
}