c - 为什么我的所有者数据 ListView 状态图像在 Windows XP 上显示为空白?

标签 c listview winapi windows-xp

这次是简单的问题,但我在搜索中没有看到一个问题:我有一个 ListView 控件,其中的状态图像应显示在一列中。在 wine、Windows Vista、Windows 7 和 Windows 8.1 中,情况看起来是正确的。但在 Windows XP 中,状态图像不会显示,只显示图像应有的空白区域。需要通用控件版本 6。我做错了什么?

这是一个演示这一点的示例程序。

// 17 august 2014
// scratch Windows program by pietro gagliardi 17 april 2014
// fixed typos and added toWideString() 1 may 2014
// borrows code from the scratch GTK+ program (16-17 april 2014) and from code written 31 march 2014 and 11-12 april 2014
#define _UNICODE
#define UNICODE
#define STRICT
#define _GNU_SOURCE     // needed to declare asprintf()/vasprintf()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <windows.h>
#include <commctrl.h>       // needed for InitCommonControlsEx() (thanks Xeek in irc.freenode.net/#winapi for confirming)

#ifdef  _MSC_VER
#error sorry! the scratch windows program relies on mingw-only functionality! (specifically: asprintf())
#endif

HMODULE hInstance;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
HFONT controlfont;

void panic(char *fmt, ...);
TCHAR *toWideString(char *what);
void init(void);

LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    NMHDR *nmhdr = (NMHDR *) lparam;
    NMLVDISPINFOW *fill = (NMLVDISPINFO *) lparam;

    switch (msg) {
    case WM_NOTIFY:
        if (nmhdr->code == LVN_GETDISPINFO) {
            if (fill->item.iSubItem == 0) {
                fill->item.state = INDEXTOSTATEIMAGEMASK(fill->item.iItem + 1);
                fill->item.stateMask = LVIS_STATEIMAGEMASK;
            } else
                fill->item.pszText = L"No State Image Here";
            return 0;
        }
        return DefWindowProc(hwnd, msg, wparam, lparam);
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    panic("oops: message %ud does not return anything; bug in wndproc()", msg);
}

HWND makeMainWindow(void)
{
    WNDCLASS cls;
    HWND hwnd;

    ZeroMemory(&cls, sizeof (WNDCLASS));
    cls.lpszClassName = L"mainwin";
    cls.lpfnWndProc = wndproc;
    cls.hInstance = hInstance;
    cls.hIcon = hDefaultIcon;
    cls.hCursor = hDefaultCursor;
    cls.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    if (RegisterClass(&cls) == 0)
        panic("error registering window class");
    hwnd = CreateWindowEx(0,
        L"mainwin", L"Main Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        300, 300,
        NULL, NULL, hInstance, NULL);
    if (hwnd == NULL)
        panic("opening main window failed");
    return hwnd;
}

void buildUI(HWND mainwin)
{
#define CSTYLE (WS_CHILD | WS_VISIBLE)
#define CXSTYLE (0)
#define SETFONT(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM) controlfont, (LPARAM) TRUE);

    HWND lv;
    LVCOLUMN column;
    HIMAGELIST imglist;

    lv = CreateWindowEx(WS_EX_CLIENTEDGE | CXSTYLE,
        WC_LISTVIEW, L"",
        LVS_REPORT | LVS_OWNERDATA | LVS_NOSORTHEADER | LVS_SHOWSELALWAYS | WS_HSCROLL | WS_VSCROLL | WS_TABSTOP | CSTYLE,
        10, 10, 250, 250,
        mainwin, (HMENU) 100, hInstance, NULL);
    if (lv == NULL)
        panic("error making list view");
    SETFONT(lv);
    SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE,
        LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES,
        LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);

    // error checking elided from this point to where otherwise noted

    imglist = ImageList_Create(
        GetSystemMetrics(SM_CXSMICON),
        GetSystemMetrics(SM_CYSMICON),
        ILC_COLOR32, 20, 20);
    ImageList_AddIcon(imglist, LoadIconW(NULL, IDI_ERROR));
    ImageList_AddIcon(imglist, LoadIconW(NULL, IDI_QUESTION));
    ImageList_AddIcon(imglist, LoadIconW(NULL, IDI_WARNING));
    SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) imglist);

    ZeroMemory(&column, sizeof (LVCOLUMN));
    column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
    column.fmt = LVCFMT_LEFT;
    column.pszText = L"State Image";
    column.iSubItem = 0;
    column.iOrder = 0;
    SendMessageW(lv, LVM_INSERTCOLUMN, 0, (LPARAM) (&column));
    ZeroMemory(&column, sizeof (LVCOLUMN));
    column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
    column.fmt = LVCFMT_LEFT;
    column.pszText = L"No State Image";
    column.iSubItem = 1;
    column.iOrder = 1;
    SendMessageW(lv, LVM_INSERTCOLUMN, 1, (LPARAM) (&column));

    // end of error eliding

    if (SendMessageW(lv, LVM_SETITEMCOUNT, 3, 0) == 0)
        panic("error setting number of items in list view");
}

void firstShowWindow(HWND hwnd);

int main(void)
{
    HWND mainwin;
    MSG msg;

    init();

    mainwin = makeMainWindow();
    buildUI(mainwin);
    firstShowWindow(mainwin);

    for (;;) {
        BOOL gmret;

        gmret = GetMessage(&msg, NULL, 0, 0);
        if (gmret == -1)
            panic("error getting message");
        if (gmret == 0)
            break;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

DWORD iccFlags =
//  ICC_ANIMATE_CLASS |         // animation control
//  ICC_BAR_CLASSES |               // toolbar, statusbar, trackbar, tooltip
//  ICC_COOL_CLASSES |          // rebar
//  ICC_DATE_CLASSES |          // date and time picker
//  ICC_HOTKEY_CLASS |          // hot key
//  ICC_INTERNET_CLASSES |      // IP address entry field
//  ICC_LINK_CLASS |                // hyperlink
    ICC_LISTVIEW_CLASSES |          // list-view, header
//  ICC_NATIVEFNTCTL_CLASS |        // native font
//  ICC_PAGESCROLLER_CLASS |        // pager
//  ICC_PROGRESS_CLASS |            // progress bar
//  ICC_STANDARD_CLASSES |      // "one of the intrinsic User32 control classes"
//  ICC_TAB_CLASSES |               // tab, tooltip
//  ICC_TREEVIEW_CLASSES |      // tree-view, tooltip
//  ICC_UPDOWN_CLASS |          // up-down
//  ICC_USEREX_CLASSES |            // ComboBoxEx
//  ICC_WIN95_CLASSES |         // some of the above
    0;

void init(void)
{
    INITCOMMONCONTROLSEX icc;
    NONCLIENTMETRICS ncm;

    hInstance = GetModuleHandle(NULL);
    if (hInstance == NULL)
        panic("error getting hInstance");
    hDefaultIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
    if (hDefaultIcon == NULL)
        panic("error getting default window class icon");
    hDefaultCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    if (hDefaultCursor == NULL)
        panic("error getting default window cursor");
    icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
    icc.dwICC = iccFlags;
    if (InitCommonControlsEx(&icc) == FALSE)
        panic("error initializing Common Controls");
    ncm.cbSize = sizeof (NONCLIENTMETRICS);
    if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
        sizeof (NONCLIENTMETRICS), &ncm, 0) == 0)
        panic("error getting non-client metrics for getting control font");
    controlfont = CreateFontIndirect(&ncm.lfMessageFont);
    if (controlfont == NULL)
        panic("error getting control font");
}

void panic(char *fmt, ...)
{
    char *msg;
    TCHAR *lerrmsg;
    char *fullmsg;
    va_list arg;
    DWORD lasterr;
    DWORD lerrsuccess;

    lasterr = GetLastError();
    va_start(arg, fmt);
    if (vasprintf(&msg, fmt, arg) == -1) {
        fprintf(stderr, "critical error: vasprintf() failed in panic() preparing panic message; fmt = \"%s\"\n", fmt);
        abort();
    }
    // according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms680582%28v=vs.85%29.aspx
    lerrsuccess = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, lasterr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lerrmsg, 0, NULL);
    if (lerrsuccess == 0) {
        fprintf(stderr, "critical error: FormatMessage() failed in panic() preparing GetLastError() string; panic message = \"%s\", last error in panic(): %ld, last error from FormatMessage(): %ld\n", msg, lasterr, GetLastError());
        abort();
    }
    // note to self: use %ws instead of %S (thanks jon_y in irc.oftc.net/#mingw-w64)
    if (asprintf(&fullmsg, "panic: %s\nlast error: %ws\n", msg, lerrmsg) == -1) {
        fprintf(stderr, "critical error: asprintf() failed in panic() preparing full report; panic message = \"%s\", last error message: \"%ws\"\n", msg, lerrmsg);
        abort();
    }
    fprintf(stderr, "%s\n", fullmsg);
    va_end(arg);
    exit(1);
}

void firstShowWindow(HWND hwnd)
{
    // we need to get nCmdShow
    int nCmdShow;
    STARTUPINFO si;

    nCmdShow = SW_SHOWDEFAULT;
    GetStartupInfo(&si);
    if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
        nCmdShow = si.wShowWindow;
    ShowWindow(hwnd, nCmdShow);
    if (UpdateWindow(hwnd) == 0)
        panic("UpdateWindow(hwnd) failed in first show");
}

TCHAR *toWideString(char *what)
{
    TCHAR *buf;
    int n;
    size_t len;

    len = strlen(what);
    if (len == 0) {
        buf = (TCHAR *) malloc(sizeof (TCHAR));
        if (buf == NULL)
            goto mallocfail;
        buf[0] = L'\0';
    } else {
        n = MultiByteToWideChar(CP_UTF8, 0, what, -1, NULL, 0);
        if (n == 0)
            panic("error getting number of bytes to convert \"%s\" to UTF-16", what);
        buf = (TCHAR *) malloc((n + 1) * sizeof (TCHAR));
        if (buf == NULL)
            goto mallocfail;
        if (MultiByteToWideChar(CP_UTF8, 0, what, -1, buf, n) == 0)
            panic("erorr converting \"%s\" to UTF-16", what);
    }
    return buf;
mallocfail:
    panic("error allocating memory for UTF-16 version of \"%s\"", what);
}

最佳答案

ListView 控件使用LVM_SETCALLBACKMASK使其通过LVN_GETDISPINFO请求项目状态数据。

我只能猜测,在 Windows 7 及更高版本上,所有者数据控件的此要求已被删除 - 文档没有说明 - 但在 XP/Vista 中,您需要发送此消息以使其请求项目状态。

关于c - 为什么我的所有者数据 ListView 状态图像在 Windows XP 上显示为空白?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25354448/

相关文章:

c - 以相反的顺序打印双向链表?

c - Struct fl 没有名为 sub 的成员

c - 什么时候应该将数组名称视为指针,什么时候它只代表数组本身?

c++ - CreateFont、DeleteObject 和 GDI 句柄/内存泄漏

WNDCLASSEX的概念,良好的编程习惯和系统类的WndProc

c - 如何更新forloop流程中的变量?

带 fragment 的 Android ListView

c# - 在 DataTemplate 内的 ListView 上添加 EventTrigger 导致 XamlParseException

java - 扩展 ListActivity android.R.id.list

windows - WIN7下防止hard reset删除注册表修改?