c-是我的子类化公共控件工具提示在WM_DESTROY中破坏自己之前



下面的程序创建了一个带有Common Controls工具提示的窗口,该工具提示将窗口子类化。当我得到WM_DESTROY时,我本能地决定销毁工具提示,因为WM_DESTROY在销毁子窗口(我认为也是拥有的)之前,但DestroyWindow()失败,返回最后一个错误1400(ERROR_INVALID_WINDOW_HANDLE)。(在葡萄酒上,它会返回5(ERROR_ACCESS_DENIED)。我认为这是葡萄酒的一个错误,要么设置错误,要么根本没有;我可以稍后提交。)

我在Windows XP 32位和Windows 7 64位上进行了测试,同时使用了Common Controls 5和6(下面使用了命令行"flag"),并在Ubuntu 14.10和15.04测试版上测试了wine。

如果工具提示正在自我破坏,我该怎么处理它?我应该仅仅依靠这种行为吗?或者我应该将工具提示子类化并自己处理WM_DESTROY?(两个窗口相互子类化会导致问题吗?)

谢谢。

// 31 march-2 april 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
void die(char *why)
{
    fprintf(stderr, "error %s: %I32un", why, GetLastError());
    abort();
}
HWND tooltip = NULL;
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CREATESTRUCT *cs = (CREATESTRUCT *) lParam;
    TOOLINFOW ti;
    switch (uMsg) {
    case WM_CREATE:
        tooltip = CreateWindowExW(WS_EX_TOOLWINDOW,
            TOOLTIPS_CLASSW, L"",
            WS_POPUP | TTS_NOPREFIX,
            0, 0,
            0, 0,
            hwnd, NULL, cs->hInstance, NULL);
        if (tooltip == NULL)
            die("creating tooltip");

        ZeroMemory(&ti, sizeof (TOOLINFOW));
        ti.cbSize = TTTOOLINFOW_V2_SIZE;
        ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRANSPARENT;
        ti.hwnd = hwnd;
        ti.uId = (UINT_PTR) hwnd;
        ti.hinst = cs->hInstance;
        ti.lpszText = L"this is a tooltip! wow!";
        if (SendMessageW(tooltip, TTM_ADDTOOL, 0, (LPARAM) (&ti)) == FALSE)
            die("setting up tooltip");
        break;
    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    case WM_DESTROY:
        if (DestroyWindow(tooltip) == 0)
            die("deleting tooltips");
        break;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void initCommonControls(BOOL);
int main(int argc, char *argv[])
{
    BOOL comctl5;
    WNDCLASSW wc;
    HWND mainwin;
    MSG msg;
    comctl5 = FALSE;
    if (argc > 1)
        comctl5 = strcmp(argv[1], "comctl5") == 0;
    initCommonControls(comctl5);
    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"mainwin";
    wc.lpfnWndProc = wndproc;
    wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    wc.hInstance = GetModuleHandle(NULL);
    if (RegisterClassW(&wc) == 0)
        die("registering main window class");
    mainwin = CreateWindowExW(0,
        L"mainwin", L"Main Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, GetModuleHandle(NULL), NULL);
    if (mainwin == NULL)
        die("creating main window");
    ShowWindow(mainwin, SW_SHOWDEFAULT);
    UpdateWindow(mainwin);
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
// MSDN doesn't list a constant that only includes tooltips but says this and a few others do
#define wantedICCClasses (ICC_BAR_CLASSES)
static ULONG_PTR comctlManifestCookie;
static HMODULE comctl32;
// note that this is an 8-bit character string we're writing; see the encoding clause
static const char manifest[] = "<?xml version="1.0" encoding="UTF-8" standalone="yes"?>n<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">n<assemblyIdentityn    version="1.0.0.0"n    processorArchitecture="*"n    name="CompanyName.ProductName.YourApplication"n    type="win32"n/>n<description>Your application description here.</description>n<dependency>n    <dependentAssembly>n        <assemblyIdentityn            type="win32"n            name="Microsoft.Windows.Common-Controls"n            version="6.0.0.0"n            processorArchitecture="*"n            publicKeyToken="6595b64144ccf1df"n            language="*"n        />n    </dependentAssembly>n</dependency>n</assembly>n";
void initCommonControls(BOOL comctl5)
{
    WCHAR temppath[MAX_PATH + 1];
    WCHAR filename[MAX_PATH + 1];
    HANDLE file;
    DWORD nExpected, nGot;
    ACTCTX actctx;
    HANDLE ac;
    INITCOMMONCONTROLSEX icc;
    FARPROC f;
    // this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
    BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
    if (!comctl5) {
        if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
            die("getting temporary path for writing manifest file");
        if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
            die("getting temporary filename for writing manifest file");
        file = CreateFileW(filename, GENERIC_WRITE,
            0,          // don't share while writing
            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (file == NULL)
            die("creating manifest file");
        nExpected = (sizeof manifest / sizeof manifest[0]) - 1;     // - 1 to omit the terminating null character)
        if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
            die("writing manifest file");
        if (nGot != nExpected)
            die("short write to manifest file");
        if (CloseHandle(file) == 0)
            die("closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)");
        ZeroMemory(&actctx, sizeof (ACTCTX));
        actctx.cbSize = sizeof (ACTCTX);
        actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
        actctx.lpSource = filename;
        ac = CreateActCtx(&actctx);
        if (ac == INVALID_HANDLE_VALUE)
            die("creating activation context for synthesized manifest file");
        if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
            die("activating activation context for synthesized manifest file");
    }
    ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
    icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
    icc.dwICC = wantedICCClasses;
    comctl32 = LoadLibraryW(L"comctl32.dll");
    if (comctl32 == NULL)
        die("loading comctl32.dll");
    f = GetProcAddress(comctl32, "InitCommonControlsEx");
    if (f == NULL)
        die("loading InitCommonControlsEx()");
    ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
    if ((*ficc)(&icc) == FALSE)
        die("initializing Common Controls (comctl32.dll)");
}

工具提示没有WS_CHILD样式。在所有者窗口收到WM_DESTROY之前,所有这类窗口都会被完全销毁。结果DestroyWindow(工具提示)并返回错误-error_INVALID_WINDOW_HANDLE

相关内容

  • 没有找到相关文章

最新更新