使用GCC和MSVC编译时的行为不同

时间:2013-11-09 09:04:24

标签: c winapi gdi

我已经对一些按钮控件进行了细分,因为我自己绘制了整个UI(对话框的hdc)。 这是为了避免闪烁,目的是通过单个memDC完成所有绘图 - 防止UI的交错更新。

因此,我将所有内容绘制到对话框的背景中,然后将一些按钮放在UI区域上,以响应鼠标事件。到现在为止还挺好。或者我想。

我使用以下WndProc对按钮进行了细分,期望Windows可以按照正常情况执行所有操作,但绘图除外。

LRESULT CALLBACK invisibleBtnProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    long oldProc = GetWindowLong(hwndBtn, GWL_USERDATA);

    switch (uMsg)
    {
        case WM_PAINT:
            ValidateRect(hwndBtn, NULL);
            return 0;

        case WM_ERASEBKGND:
            return 1;
    }
    return CallWindowProc((WNDPROC)oldProc, hwndBtn, uMsg, wParam, lParam);
}

使用以下代码创建和子类化按钮:

for (i=0; i<n; i++)
{
    btn = CreateWindow(WC_BUTTON, L"", WS_VISIBLE|WS_CHILD, 0,0,0,0, hwndDlg, (HMENU)(firstBigBtnId+i), hInst, NULL);
    long btnProcCur = GetWindowLong(btn, GWL_WNDPROC);
    SetWindowLong(btn, GWL_USERDATA, btnProcCur);

    SetWindowLong(btn, GWL_WNDPROC, (long) invisibleBtnProc);
}

当我使用MinGW&amp; Code :: Blocks,它完美无瑕。 (在调试和发布版本中)

不幸的是,当使用MSVC&amp; VS2010,我观察到不同的行为。调试模式构建是可以的,但发布版本不是。当单击其中一个不可见按钮时,系统正在绘制它,遮盖底层的“按钮”。

我有一个需要绘制的大型WMF(emf?我忘了) - 它非常慢并且在调整窗口大小时会产生闪烁,对于那些想知道为什么自定义绘制一切都接近的人来说。

这就是我所看到的:

enter image description here

请注意,在我尝试点击最左边的按钮之前,它是不可见的 - 就像右边的按钮一样。只有在点击它时,窗户才决定绘制它。 调整父窗口的大小 - (触发对对话框的InvalidateRect调用的对话框)将删除错误的绘图。再次单击该按钮会使其被绘制。

我的想法中有哪些错误?

编辑:为SCCCE添加了以下代码(当使用GCC调试和发布构建时显示相同的不需要的行为,原始程序仅在调试版本中显示)

#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

RECT btnRect;
const int btnSize = 150;
const int btnId = 1000;
HINSTANCE hInst;


/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


LRESULT CALLBACK invisibleBtnProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    long oldProc = GetWindowLong(hwndBtn, GWL_USERDATA);

    switch (uMsg)
    {
        case WM_PAINT:
            ValidateRect(hwndBtn, NULL);
            return 0;

        case WM_ERASEBKGND:
            return 1;
    }
    return CallWindowProc((WNDPROC)oldProc, hwndBtn, uMsg, wParam, lParam);
}

void onSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    RECT mRect;
    GetClientRect(hwnd, &mRect);
    btnRect.left = (mRect.right - btnSize) / 2;
    btnRect.top = (mRect.bottom - btnSize) / 2;
    btnRect.right = btnRect.left + btnSize;
    btnRect.bottom = btnRect.top + btnSize;

    HWND btn;
    btn = GetDlgItem(hwnd, btnId);
    MoveWindow(btn, btnRect.left, btnRect.top, btnSize, btnSize, false);

    InvalidateRect(hwnd, NULL, false);
}

void onPaint(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    HBRUSH bkBrush, redBrush;
    RECT mRect;

    GetClientRect(hwnd, &mRect);

    hdc = BeginPaint(hwnd, &ps);

        bkBrush = CreateSolidBrush(RGB(51,51,51) );
        redBrush = CreateSolidBrush(RGB(255,0,0) );
        FillRect(hdc, &mRect, bkBrush);
        FillRect(hdc, &btnRect, redBrush);
        DeleteObject(bkBrush);
        DeleteObject(redBrush);

    EndPaint(hwnd, &ps);
}

/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
    case WM_CREATE:
            HWND tmp;
            tmp = CreateWindow("Button", "Press Me", WS_VISIBLE|WS_CHILD, 0,0,0,0, hwnd, (HMENU)btnId, hInst, NULL);
            long oldProc;
            oldProc = GetWindowLong(tmp, GWL_WNDPROC);
            SetWindowLong(tmp, GWL_USERDATA, oldProc);
            SetWindowLong(tmp, GWL_WNDPROC, (long)invisibleBtnProc);
            return 0;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;

        case WM_SIZE:
            onSize(hwnd, wParam, lParam);
            return 0;

        case WM_PAINT:
            onPaint(hwnd, wParam, lParam);
            return 0;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case btnId:
                    MessageBeep(MB_ICONEXCLAMATION);
                    break;
            }
            return 0;

        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

1 个答案:

答案 0 :(得分:3)

设置BS_OWNERDRAW样式告诉Windows它不会自己绘制按钮,但是你要对此负责。这就是诀窍。

你需要改变的东西不多。只需使用此样式创建按钮即可。

tmp = CreateWindow("Button", "Press Me", WS_VISIBLE|WS_CHILD|BS_OWNERDRAW, 0,0,0,0, hwnd, (HMENU)btnId, hInst, NULL);

然后在你的invisibleBtnProc中你可以添加

case WM_DRAWITEM:
    ValidateRect(hwndBtn, NULL);
    return TRUE;