如何从 WM_CTLCOLORLISTBOX 中检索 Combobox 的 GWLP_USERDATA?

Posted

技术标签:

【中文标题】如何从 WM_CTLCOLORLISTBOX 中检索 Combobox 的 GWLP_USERDATA?【英文标题】:How can I retrieve Combobox's GWLP_USERDATA from WM_CTLCOLORLISTBOX? 【发布时间】:2021-02-25 05:38:54 【问题描述】:

到目前为止,我一直在使用的大多数消息都通过 lParam 中的 HWND。所以我使用该值来检索与 GWLP_USERDATA 关联的 hwnd 对象,所以我可以这样做:

    case WM_COMMAND:
    
        auto sender = (HWND)lParam;
        auto obj = getObj(sender);
        obj->myMethod();
    

并获取与调用的 hwnd 关联的方法。但是,组合框的WM_CTLCOLORLISTBOX 不会在lParam 中发送组合框的HWND。我必须发送CB_GETCOMBOBOXINFO 消息,但这需要控件的 hwnd,在该设计中,该控件不可用。所以我的问题是:如何从WM_CTLCOLORLISTBOX 检索与组合框关联的对象?我想不出任何其他方法来循环到目前为止创建的所有对象(所以我需要保留它们的数组),以某种方式过滤与组合框控件类型关联的对象,然后在 CB_GETCOMBOBOXINFO 中使用该 hwnd信息。这似乎是一种蛮力。还有比这更好的方法吗?这是我目前的做法:

    case WM_CTLCOLORLISTBOX:
    
      auto dc = (HDC) wParam;
      SetBkMode(dc, TRANSPARENT);
      COMBOBOXINFO info;
      
      for(int i = 0; i < COUNTOF(instances); i++)
      
        auto obj = instances[i];
        if(obj->type != ControlType_combobox) 
            continue;
        memset(&info, 0, sizeof(COMBOBOXINFO));
        info.cbSize  = sizeof(COMBOBOXINFO);
        auto hCombo = instances[i];
        SendMessage(obj->hwnd, CB_GETCOMBOBOXINFO, 0, (LPARAM) &info);
        if((HWND)lParam == info.hwndList)
        
            return (LRESULT) obj->hBrush;
        
      
    
    break;

完整代码:

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "Comdlg32.lib")

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <winuser.h>
#include <assert.h>

#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))

enum ControlType

    ControlType_none,
    ControlType_button,
    ControlType_combobox
;

class Foo

public:

    int n = 0;
    const wchar_t *str = nullptr;
    HBRUSH hBrush = nullptr;
    ControlType type = ControlType_none;
    HWND hwnd = nullptr;

    void sayHello()
    
        MessageBox(NULL, str, L"", MB_OK);
    

    Foo()  

    ~Foo()
     
        if(hBrush) 
            DeleteObject(hBrush);
            hBrush = nullptr;
        
     
;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HFONT getDefaultFont();
void SetDefaultFont(HWND hwnd);
void saveInstance(Foo *f);
void freeInstances();
void associateObj(HWND hwnd, Foo *instance);
Foo* getObj(HWND hwnd);

Foo *instances[12];
int instance_size = 0;

HFONT hDefaultSystemFont;

HINSTANCE g_hinst;

const wchar_t *items[] =
 
  L"Windows", L"Mac", 
  L"FreeBSD", L"Arch",
;

enum

  ID_COMBO = 10,
  ID_BTN1,
;


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PWSTR lpCmdLine, int nCmdShow) 

    HWND hwnd;
    MSG  msg ;    
    WNDCLASSW wc = 0;
    wc.lpszClassName = L"Application";
    wc.hInstance     = hInstance ;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpfnWndProc   = WndProc ;
    wc.hCursor       = LoadCursor(0,IDC_ARROW);

    g_hinst = hInstance;
  
    RegisterClassW(&wc);
    hwnd = CreateWindowW(wc.lpszClassName, L"",
                  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                  100, 100, 300, 170, 0, 0, hInstance, 0);  


    while (GetMessage(&msg, NULL, 0, 0))
    
        DispatchMessage(&msg);
    

    return (int) msg.wParam;


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 
        WPARAM wParam, LPARAM lParam) 

    switch(msg)
    
    
        case WM_CREATE:
        
            HWND hwndCombo = CreateWindow(L"Combobox", NULL, 
                    WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
                    10, 10, 120, 110, hwnd, (HMENU) ID_COMBO, g_hinst, NULL);
              auto f = new Foo;
              f->n = 10;
              f->str = L"Hello from combo!";
              f->hBrush = CreateSolidBrush(RGB(0, 128, 0));
              f->type = ControlType_combobox;
              associateObj(hwndCombo, f);
              for (int i = 0; i < COUNTOF(items); i++)
              
                SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
              
              SetDefaultFont(hwndCombo);

              HWND btn1 =
              CreateWindow(L"Button", L"Click me!", 
                    WS_CHILD | WS_VISIBLE,
                    5, 40, 90, 25, hwnd, (HMENU) ID_BTN1, g_hinst, NULL);
              auto f2 = new Foo;
              f2->n = 20;
              f2->str = L"Hello from button!";
              f2->type = ControlType_button;
              associateObj(btn1, f2);
              SetDefaultFont(btn1);
        
        break;

        case WM_DESTROY:
            DeleteObject(hDefaultSystemFont);
            hDefaultSystemFont = NULL;
            freeInstances();
            PostQuitMessage(0);
            break;

        case WM_COMMAND:
        
            auto sender = (HWND)lParam;
            auto o = getObj(sender);
            o->sayHello();
        
        break;

        case WM_CTLCOLORLISTBOX:
        
          auto dc = (HDC) wParam;
          SetBkMode(dc, TRANSPARENT);
          COMBOBOXINFO info;
          
          for(int i = 0; i < COUNTOF(instances); i++)
          
            auto obj = instances[i];
            if(obj->type != ControlType_combobox) 
                continue;
            memset(&info, 0, sizeof(COMBOBOXINFO));
            info.cbSize  = sizeof(COMBOBOXINFO);
            auto hCombo = instances[i];
            SendMessage(obj->hwnd, CB_GETCOMBOBOXINFO, 0, (LPARAM) &info);
            if((HWND)lParam == info.hwndList)
            
                return (LRESULT) obj->hBrush;
            
          
        
        break;
    
  
    return DefWindowProcW(hwnd, msg, wParam, lParam);


void associateObj(HWND hwnd, Foo *instance)

    instance->hwnd = hwnd;
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(void*)instance);
    saveInstance(instance);


Foo* getObj(HWND hwnd)

    return (Foo*)(void*)GetWindowLongPtr(hwnd, GWLP_USERDATA);


void saveInstance(Foo *f)

    instances[instance_size++] = f;


void freeInstances()

    for(int i = 0; i < COUNTOF(instances); i++) 
        delete instances[i];
        instances[i] = nullptr;
    


HFONT getDefaultFont()

  if(hDefaultSystemFont == NULL) 
    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(NONCLIENTMETRICS);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
    hDefaultSystemFont = CreateFontIndirect(&ncm.lfMessageFont);
  
  return hDefaultSystemFont;



void SetDefaultFont(HWND hwnd)

    SendMessage(hwnd, WM_SETFONT, (LPARAM) getDefaultFont(), TRUE);

【问题讨论】:

您可以在列表框HWND 上调用GetParent() 以获取组合的HWND 不是解决您当前问题的方法,但请考虑使用SetProp/GetProp。由于您正在编写对 Windows 通用控件的抽象,因此您现在不仅要与一方,而且要与两方争夺 GWLP_USERDATA:第三方应用程序和库的客户端。 @JonathanPotter 喜欢这样吗? auto sender = GetParent((HWND)lParam); 但 `getObj(sender)` 正在返回 NULL @IInspectable 谢谢你的建议,我会考虑切换到SetProp / GetProp,听起来更合适。 【参考方案1】:

创建 ComboBox 时,使用CB_GETCOMBOBOXINFO 获取其 ListBox 的HWND,然后使用SetProp() 将对象指针保存在其中。这样,在您的WM_CTLCOLORLISTBOX 处理程序中,您可以在提供的HWND 上使用GetProp() 来访问您的对象。无需狩猎。

更新:如评论中所述,您还可以在WM_CTLCOLORLISTBOX提供的HWND上使用GetParent()来获取其拥有的ComboBox的HWND,然后您可以从中访问使用 getObj() 的关联对象指针。

【讨论】:

我确实保留了associateObj(hwndCombo, f); 然后auto sender = GetParent((HWND)lParam); assert(sender != NULL); auto obj = getObj(sender); 但我在obj 上收到了NULL。我错过了什么?

以上是关于如何从 WM_CTLCOLORLISTBOX 中检索 Combobox 的 GWLP_USERDATA?的主要内容,如果未能解决你的问题,请参考以下文章

从client(content=&quot;&lt;p&gt;&lt;/p&gt;&quot;)中检測到有潜在危急的 Request.Form 值。

唯品会Gucci腰带被得物中检鉴定为假后续:消费者被迫法院维权

android中检測网络连接状态简单总结

Android中检測字符编码(GB2312,ASCII,UTF8,UNICODE,TOTAL——ENCODINGS)方法

如何使用 HashMap 从 Android 的 Firebase 中的子节点获取数据?

如何在 VB 中格式化动态网格视图?