I don't have a Windows 7 machine to test on so I don't know if the alpha channel is the real issue but assuming that it is, you can work around it by setting the alpha channel back to the correct state after writing the buggy text:
enum { WIDTH = 255 * 3, HEIGHT = 25 };
#define CalcStride(w, bpp) ( ((((w) * (bpp)) + 31) & ~31) >> 3 )
#define PMC(c, a) ( (c) = ((int)(c) * (a) / 255) )
#define PM(q) PMC( (q).rgbRed, (q).rgbReserved), PMC( (q).rgbGreen, (q).rgbReserved), PMC( (q).rgbBlue, (q).rgbReserved)
RGBQUAD* GetPxPtr32(void*pBits, UINT x, UINT y)
{
return ((RGBQUAD*) ( ((char*)pBits) + (y * CalcStride(WIDTH, 32)) )) + x;
}
void SaveAlpha32(void*pBits, BYTE*buf)
{
for (UINT x = 0; x < WIDTH; ++x)
for (UINT y = 0; y < HEIGHT; ++y)
buf[(y * WIDTH) + x] = GetPxPtr32(pBits, x, y)->rgbReserved;
}
void RestoreAlpha32(void*pBits, const BYTE*buf)
{
for (UINT x = 0; x < WIDTH; ++x)
for (UINT y = 0; y < HEIGHT; ++y)
GetPxPtr32(pBits, x, y)->rgbReserved = buf[(y * WIDTH) + x];
}
void Draw(HDC hDC, HBITMAP hBM, void*pBits, UINT w, UINT h, bool isDwmActive)
{
// Fill with white and a silly gradient alpha channel:
for (UINT y = 0; y < h; ++y)
for (UINT x = 0; x < w; ++x)
(*(UINT32*)GetPxPtr32(pBits, x, y)) = 0xffffffff, GetPxPtr32(pBits, x, y)->rgbReserved = max(42, x % 255);
BYTE *alphas = isDwmActive ? 0 : (BYTE*) LocalAlloc(LPTR, sizeof(BYTE) * w * h), fillWithRed = true;
if (!isDwmActive) SaveAlpha32(pBits, alphas);
HGDIOBJ hBmOld = SelectObject(hDC, hBM);
RECT r = { 0, 0, WIDTH, HEIGHT };
int cbk = SetBkColor(hDC, RGB(255, 0, 0));
if (fillWithRed) ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &r, NULL, 0, NULL);
int ctx = SetTextColor(hDC, RGB(0, 0, 0));
int mode = SetBkMode(hDC, TRANSPARENT);
DrawText(hDC, TEXT("Hello World Hello World Hello World Hello World Hello World"), -1, &r, DT_SINGLELINE|DT_VCENTER|DT_CENTER); // Plain GDI always destroys the alpha
SetBkMode(hDC, mode), SetBkColor(hDC, cbk), SetTextColor(hDC, ctx);
SelectObject(hDC, hBmOld), GdiFlush();
if (!isDwmActive) RestoreAlpha32(pBits, alphas), LocalFree(alphas);
for (UINT y = 0; y < h; ++y) for (UINT x = 0; x < w; ++x) PM(*GetPxPtr32(pBits, x, y));
}
int main()
{
const INT w = WIDTH, h = HEIGHT, bpp = 32, x = 222, y = 222;
HWND hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TOPMOST, WC_STATIC, 0, WS_VISIBLE|WS_POPUP, x, y, WIDTH, HEIGHT, 0, 0, 0, 0);
SetWindowLong(hWnd, GWLP_WNDPROC, (LONG_PTR) DefWindowProc); // HACK
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
BITMAPINFOHEADER&bih = bi.bmiHeader;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = w, bih.biHeight = -h;
bih.biPlanes = 1, bih.biBitCount = bpp;
bih.biCompression = BI_RGB;
void*bits;
HBITMAP hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
HDC hDCScreen = GetDC(NULL), hDC = CreateCompatibleDC(hDCScreen);
Draw(hDC, hBmp, bits, w, h, false);
HGDIOBJ hBmOld = SelectObject(hDC, hBmp);
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER, blend.AlphaFormat = AC_SRC_ALPHA, blend.SourceConstantAlpha = 255;
POINT location = { x, y }, srcpt = { 0, 0 };
SIZE szWnd = { w, h };
UpdateLayeredWindow(hWnd, hDCScreen, &location, &szWnd, hDC, &srcpt, 0, &blend, ULW_ALPHA);
SelectObject(hDC, hBmOld), DeleteObject(hBmp);
DeleteDC(hDC), ReleaseDC(NULL, hDCScreen);
struct Closer { Closer(HWND h) { SetTimer(h, 1, 1000 * 11, TP); } static void CALLBACK TP(HWND h,UINT,UINT_PTR,DWORD) { ExitProcess(666); } } closer(hWnd); // HACK
for (MSG msg; GetMessage(&msg, 0, 0, 0); ) DispatchMessage(&msg);
return 666;
}
If you don't care about Vista without the platform update, you can try using Direct2D instead of GDI+.
CreateDIBSection
. – HHR