Search code examples
winapicontrolsmessaging

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


I found the following on CodeProject. It makes sense except if the sub-classed control doesn't handle the OCM_ message which means the default handling of the original message never occurs. Is there an elegant solution instead of having to always sync up the messages this function sends with the sub-classed windows procedures?

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);
}

Solution

  • There is not really a clean solution here.

    You could ensure that all child controls subtract OCM__BASE when calling DefWindowProc() for unhandled OCM_... messages.

    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);
    }
    

    Otherwise, you could have each OCM_... message carry a pointer to a struct in its WPARAM or LPARAM, where the struct contains the real WPARAM/LPARAM and the output LRESULT, and then each child control can return TRUE if a given OCM_... message is handled. The parent can then call DefWindowProc() with the original WM_... message if SendMessage(OCM_...) returns FALSE.

    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);
    }