views:

384

answers:

4
A: 

I omitted the patterns init code because i thought it's not important.

I attach fully working source code. I still have the problem on this small demo too.

On XP, it draws 4 lines but 3 on Win7.


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

TCHAR *szTitle = _T("ExtCreatePen DEMO");
TCHAR *szWindowClass = _T("ExtCreatePenClass");

void OnPaint(HDC hdc)
{
    static BYTE patterns[] = { 1, 2, 4, 8, 16, 32, 64, 128, 1 };
    LOGBRUSH lb;
    lb.lbStyle = BS_DIBPATTERN;
    lb.lbColor = DIB_RGB_COLORS;
    int cb = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2 + 8*4; 
    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, cb);                
    BITMAPINFO* pbmi = (BITMAPINFO*) GlobalLock(hg);
    ZeroMemory(pbmi, cb);
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbmi->bmiHeader.biWidth = 8; 
    pbmi->bmiHeader.biHeight = 8; 
    pbmi->bmiHeader.biPlanes = 1; 
    pbmi->bmiHeader.biBitCount = 1; 
    pbmi->bmiHeader.biCompression = BI_RGB;
    pbmi->bmiHeader.biSizeImage = 8;
    pbmi->bmiHeader.biClrUsed = 2;
    pbmi->bmiHeader.biClrImportant = 2;
    pbmi->bmiColors[1].rgbBlue =
    pbmi->bmiColors[1].rgbGreen =
    pbmi->bmiColors[1].rgbRed = 0xFF;
    DWORD* p = (DWORD*) &pbmi->bmiColors[2];
    for(int k=0; k<8; k++) *p++ = patterns[k] | patterns[k+1];
    GlobalUnlock(hg);
    lb.lbHatch = (LONG) hg;
    HPEN hNewPen = ExtCreatePen(PS_GEOMETRIC, 1, &lb, 0, NULL);
    assert(hNewPen);
    GlobalFree(hg);

    MoveToEx(hdc, 5,5, NULL);
    LineTo(hdc, 100, 5);

    HPEN oldpen = (HPEN) SelectObject(hdc, hNewPen);

    MoveToEx(hdc, 5,50, NULL);
    LineTo(hdc, 150, 50);       // drawn on XP but not win7

    SetROP2(hdc, R2_XORPEN);
    MoveToEx(hdc, 5, 100, NULL);
    LineTo(hdc, 200, 100);

    SelectObject(hdc, oldpen);
    DeleteObject(hNewPen);

    SetROP2(hdc, R2_COPYPEN);
    MoveToEx(hdc, 5, 150, NULL);
    LineTo(hdc, 250, 150);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;

    switch (message)
    {
    case WM_PAINT:
        BeginPaint(hWnd, &ps);
        OnPaint(ps.hdc);
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASS wcex;

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = szWindowClass;

    return RegisterClass(&wcex);
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HWND hWnd;

    MyRegisterClass(hInstance);

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    {
      return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    DestroyWindow(hWnd);

    return (int) msg.wParam;
}
shkim
A: 

I will admit, I was dubious as first, but I compiled and ran your program, and it does indeed fail to draw the second line on Windows 7, buy only in aero mode

By switching to Windows basic or classic mode, all four lines are drawn, as expected.

I can only assume that this is some kind of bad interaction with your custom pen and the new way aero mode implements GDI calls. This seems like it might be a Microsoft bug, perhaps you can post this question on one of their message boards?

karoberts
A: 

So you are creating an 8x8 black/white (monochrome) bitmap as a DIB, and then using that to create a pen. I see nothing wrong with this code. this definitely looks like a windows bug, but there may be a workaround.

Try setting

pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;

In this context, setting the values to 0 should mean the same thing as setting them to 2, but 0 is more standard behavior for situations where you have are using the full palette. You still need two entries in your palette, 0 just means "full size based on biBitCount".

Also, each palette entrie is a RGBQUAD, which means there is room for alpha, and your alpha is set to 0, which should be ignored, but maybe it isn't. so try setting the high byte of your two palette entries to 0xFF or 0x80.

Finally, it's possible that your palette is being ignored entirely, and Windows is using the BkMode, BkColor and TextColor of the destination DC for everything, so you need to make sure that they are set to values that you can see.

My guess is that this has something to do with alpha transparency, since GDI ignores alpha entirely, but Aero doesn't.

John Knoeller
A: 

This is a known bug with the Windows 7 GDI, though good luck getting Microsoft to acknowledge it.

http://social.technet.microsoft.com/Forums/en-US/w7itproappcompat/thread/a70ab0d5-e404-4e5e-b510-892b0094caa3

-Noel

Noel Carboni