Get a HBITMAP from an 24bit BMP file loaded into memory - winapi

I have a 24bit BMP file loaded into RAM and I'm trying to create a HBITMAP for this image file. I have found some examples around which I've been experimenting with, but can't seem to make work. Basically, I need a HBITMAP for the file, so that I can unload the file and just keep the HBITMAP which I can dispose of later with DeleteObject(). Since this bitmap is loaded very early on in my application, there is no application Window and therefore no HDC. This is what I have so far:-
HBITMAP cBitmap; // This should be where my bitmap handle ends up.
mem; // This is a void* pointer to the loaded BMP file
tagBITMAPFILEHEADER bfh = *(tagBITMAPFILEHEADER*)mem;
tagBITMAPINFOHEADER bih = *(tagBITMAPINFOHEADER*)(mem + sizeof(tagBITMAPFILEHEADER));
RGBQUAD rgb = *(RGBQUAD*)(mem + sizeof(tagBITMAPFILEHEADER) + sizeof(tagBITMAPINFOHEADER));
BITMAPINFO bi;
bi.bmiColors[0] = rgb;
bi.bmiHeader = bih;
UINT8* pixels = mem + bfh.bfOffBits;
void* ppv;
HBITMAP hBitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppv, NULL, 0);
SetDIBits(NULL, hBitmap, 0, bih.biHeight, pixels, &bi, DIB_RGB_COLORS);
GetObject(hBitmap, sizeof(BITMAP), &cBitmap);
For some reason that just baffles me, cBitmap ends up being NULL. And another thing that puzzles me... DIB_RGB_COLORS means that the BITMAPINFO has a pointer to literal RGB values, but how does that work with a 24bit image, which doesn't have a palette?

If you are loading a file from disk, it's probably easiest to use LoadImage() with the LR_LOADFROMFILE flag.
To create a HBITMAP from file data that has already been loaded into memory, you can do something similar to the following:
HBITMAP ConvertDibToHBitmap(void* bmpData)
{
HBITMAP hBitmap = NULL;
BOOL success = FALSE;
// NOTE: Assumes the BITMAPFILEHEADER is present (not normally the case for
// an in-memory DIB)
LPBITMAPFILEHEADER bfh = (LPBITMAPFILEHEADER) bmpData;
LPBITMAPINFOHEADER bih = (LPBITMAPINFOHEADER) (bfh + 1);
void *pixels = (char*) (bih + 1); // NOTE: Assumes no color table (i.e., bpp >= 24)
HDC hdc = GetDC(NULL);
if (hdc != NULL) {
hBitmap = CreateCompatibleBitmap(hdc, bih->biWidth, bih->biHeight);
if (hBitmap != NULL) {
HDC hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL) {
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
if (StretchDIBits(hdcMem, 0, 0, bih->biWidth, bih->biHeight,
0, 0, bih->biWidth, bih->biHeight, pixels,
(LPBITMAPINFO) bih, DIB_RGB_COLORS, SRCCOPY) > 0)
success = TRUE;
SelectObject(hdcMem, hOldBitmap);
DeleteDC(hdcMem);
}
}
ReleaseDC(NULL, hdc);
}
if (!success && hBitmap != NULL) {
DeleteObject(hBitmap);
hBitmap = NULL;
}
return hBitmap;
}

Um, that's not how GetObject works. The usage is
GetObject(handle, sizeof(object), &object);
In your case, the expectation is that you call it as
BITMAP bitmap;
GetObject(hBitmap, sizeof(BITMAP), &bitmap);
You are not passing a pointer to a BITMAP structure, so the behavior is undefined.
You don't need GetObject at all. You already have your HBITMAP. It's in the hBitmap variable.
cBitmap = hBitmap;

Related

Is HDC and Memory DC different?

I am studying Double buffering in windows winapi.
When I draw text on HDC direct using DrawText function, it works well like under code.
case WM_PAINT:
hDC = BeginPaint(hwnd,&ps);
DrawText(hDC,"test",4,&rt,DT_CENTER | DT_WORDBREAK);
DeleteDC(hMemDC);
ReleaseDC(hwnd,hDC);
EndPaint(hwnd,&ps);
break;
But, I would like to want to use double buffering, so I make memory dc and bitblt function.
Under code is that,It don't works.I can show white empty screen.
case WM_PAINT:
hDC = BeginPaint(hwnd,&ps);
hMemDC = CreateCompatibleDC(hDC);
//GetClientRect(hwnd, &crt);
//hBitmap = CreateCompatibleBitmap(hDC, crt.right, crt.bottom);
//OldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
DrawText(hMemDC,"test",4,&rt,DT_CENTER | DT_WORDBREAK);
BitBlt(hDC,0,0,800,800,hMemDC,0,0,SRCCOPY);
DeleteDC(hMemDC);
ReleaseDC(hwnd,hDC);
EndPaint(hwnd,&ps);
break;
Is memory dc different original dc ?
If I use CreateCompatibleBitmap function, it work well.
What is concept am i missing ?
Is there website well-organized website?
A typical non-double-buffered drawing routine looks like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rcWnd;
GetClientRect(hWnd, &rcWnd);
{
DrawText(hdc, _T("Hello, world!"), -1,
&rcWnd, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
EndPaint(hWnd, &ps);
}
break;
A typical double-buffered drawing routine looks like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rcWnd;
GetClientRect(hWnd, &rcWnd);
{
const int width = rcWnd.right - rcWnd.left
const int height = rcWnd.bottom - rcWnd.top;
// create a new DC based on the target HDC
HDC hDCMem = CreateCompatibleDC(hdc);
// create a bitmap that is compatible with the target DC
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, width, height);
// select the new bitmap in to the DC, saving the old bitmap
HBITMAP hOldBmp = (HBITMAP)SelectObject(hDCMem, hMemBmp);
// do your drawing
HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hDCMem, &rcWnd, hBr);
DeleteObject(hBr);
DrawText(hDCMem, _T("Hello, world!"), -1,
&rcWnd, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// copy all the bits from our new DC over to the target DC
BitBlt(hdc, rcWnd.left, rcWnd.top, width, height, hDCMem, 0, 0, SRCCOPY);
// select the original bitmap the DC came with
SelectObject(hDCMem, hOldBmp);
// delete our bitmap
DeleteObject(hMemBmp);
// delete the DC
DeleteDC(hDCMem);
}
EndPaint(hWnd, &ps);
}
break;
case WM_ERASEBKGND:
// since we are drawing the entire area because we are double-buffering, there is
// no need to erase the background. This will speed up your drawing.
return TRUE;
HDC objects are all the same -- there is no special things that happen when you create another one in memory. I mean, they are all in memory, really. You are just creating another canvas to do your drawing on, then copying that canvas over in one shot.

How do i Use Fillrect or DrawText on 32bit HBITMAP in C++

i'm sorry for what i did. i edited.
i'd like to use Fillrect on 32bit HBITMAP which is Created with CreateDIBSection
but i can't make rect visible in color that i want to.
(i Drawed a fillrect with CreateSolidBrush blue(RGB(0, 0, 255)) on 32bit HBITMAP(hdcbmp), but it doesn't appear blue.)
here is source code
is there anyway to show rect color that i want to?
sorry for my poor english.
void DrawAlphaBitmap(HWND hWnd, ULONG uWidth, ULONG uHeight)
{
BLENDFUNCTION bf;
HBITMAP hbitmap;
HBITMAP hOldBitmap;
BITMAPINFO bmi;
PVOID pvBits;
HDC hdcwnd = GetDC(hWnd);
HDC hdcbmp = CreateCompatibleDC(hdcwnd);
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = uWidth;
bmi.bmiHeader.biHeight = uHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;
hbitmap = CreateDIBSection(hdcbmp, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
hOldBitmap = (HBITMAP)SelectObject(hdcbmp, hbitmap);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
RECT rc2 = { 100, 100, 200, 200 };
FillRect(hdcbmp, &rc2, CreateSolidBrush(RGB(0, 0, 255)));
AlphaBlend(hdcwnd, 0, 0, uWidth, uHeight, hdcbmp, 0, 0, uWidth, uHeight, bf);
SelectObject(hdcbmp, hOldBitmap);
DeleteObject(hbitmap);
DeleteDC(hdcbmp);
ReleaseDC(hWnd, hdcwnd);
}
From documentation for BLENDFUNCTION:
AlphaFormat:
This flag is set when the bitmap has an Alpha channel (that is, per-pixel alpha).
In this case, alpha channel is not set. CreateDIBSection initializes the alpha values to zero. When AC_SRC_ALPHA is set, AlphaBlend ignores pixels whose alpha value is zero. Modify your code as follows:
//bf.AlphaFormat = AC_SRC_ALPHA; <- remove
bf.AlphaFormat = 0; //replace with 0
Side note, you have resource leak in creation of HBRUSH handle. Change the code to
HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 255));
RECT rc2 = { 0, 0, w, h };
FillRect(memdc, &rc2, hbrush);
DeleteObject(hbrush);
Ideally, your function prototype should be void DrawAlphaBitmap(HDC hdc, ULONG uWidth, ULONG uHeight); so that HDC can be passed directly, for example from BeginPaint/EndPaint in WM_PAINT message.

How can I load a bitmap inside my window?

I'm trying to load a bitmap to a window I created. The bitmap should be the background of the window (I want to add labels on it an a progress bar later on).
This is my code:
HINSTANCE hInst;
LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndCls;
static WCHAR szAppName[] = L"BitmapIntro";
MSG Msg;
hInst = hInstance;
WndCls.cbSize = sizeof(WndCls);
WndCls.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
WndCls.lpfnWndProc = WindProcedure;
WndCls.cbClsExtra = 0;
WndCls.cbWndExtra = 0;
WndCls.hInstance = hInst;
WndCls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndCls.lpszMenuName = NULL;
WndCls.lpszClassName = szAppName;
WndCls.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
RegisterClassEx(&WndCls);
CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
szAppName,
L"Bitmaps Fundamentals",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC, MemDCExercising;
PAINTSTRUCT Ps;
HBITMAP bmpExercising;
switch (Msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &Ps);
// Load the bitmap from the resource
bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Create a memory device compatible with the above DC variable
MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
SelectObject(MemDCExercising, bmpExercising);
// Copy the bits from the memory DC into the current dc
BitBlt(hDC, 10, 10, 450, 400, MemDCExercising, 0, 0, SRCCOPY);
// Restore the old bitmap
DeleteDC(MemDCExercising);
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
The problem is, the PNG size is small compared to the window, so when the PNG opens, it only occupies the left high corner. How can I make it stretch to my window size or at least draw it over and over until it fills the window?
How can I make it stretch to my window size
Use StretchBlt() instead of BitBlt().
case WM_PAINT:
{
// Get the window dimensions
RECT r;
GetClientRect(hWnd, &r);
// Load the bitmap from the resource
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Get the bitmap dimensions
BITMAP bmp;
GetObject(bmpExercising, sizeof(BITMAP), &bmp);
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
// Create a memory device compatible with the above DC variable
HDC MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
HBITMAP hOldBmp = SelectObject(MemDCExercising, bmpExercising);
// Copy the bits from the memory DC into the current dc
StretchBlt(hDC, 0, 0, r.right - r.left, r.bottom - r.top, MemDCExercising, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);
// Restore the old bitmap
SelectObject(MemDCExercising, hOldBmp);
// Destroy the memory device
DeleteDC(MemDCExercising);
// Destroy the bitmap
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
}
or at least draw it over and over until it fills the window?
There are two different ways to handle that.
at startup, load the bitmap and create an HBRUSH around it using CreatePatternBrush(), and then assign that to the WNDCLASS::hbrBackground field when you register your window class. Let the OS draw the window background using the bitmap for you.
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
WndCls.hbrBackground = CreatePatternBrush(bmpExercising);
if you want to paint the bitmap manually, have your paint handler call BitBlt() in a couple of loops. You know the dimensions of the bitmap (which you can retrieve in code using GetObject() and the BITMAP structure), and you know the dimensions of the window (which you can retrieve in code using GetWindowRect() or GetClientRect()). So simply draw the same bitmap more than one time at different offsets as needed. Start by drawing it once in the top-left corner, then move right bitmap-width pixels and draw it again, repeating until you move past window-width pixels. Then move left back to 0 and move down bitmap-height pixels and repeat the whole width-line again, repeating until you move past window-height pixels.
case WM_PAINT:
{
// Get the window dimensions
RECT r;
GetClientRect(hWnd, &r);
// Load the bitmap from the resource
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Get the bitmap dimensions
BITMAP bmp;
GetObject(bmpExercising, sizeof(BITMAP), &bmp);
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
// Create a memory device compatible with the above DC variable
HDC MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
HBITMAP hOldBmp = SelectObject(MemDCExercising, bmpExercising);
int width = r.right - r.left;
int height = r.bottom - r.top;
// Copy the bits from the memory DC into the current dc
for(int y = 0; y < height; y += bmp.bmHeight)
{
for(int x = 0; x < width; x += bmp.bmWidth)
{
BitBlt(hDC, x, y, bmp.bmWidth, bmp.bmHeight, MemDCExercising, 0, 0, SRCCOPY);
}
}
// Restore the old bitmap
SelectObject(MemDCExercising, hOldBmp);
// Destroy the memory device
DeleteDC(MemDCExercising);
// Destroy the bitmap
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
}
Now, with that said, here are some additional notes:
you should not be loading the bitmap inside of your paint handler. Load it one time before creating the window, and then reuse the same HBITMAP for each paint operation until the window is destroyed, then free the bitmap.
LoadBitmap() is deprecated, you should be using LoadImage() instead, eg:
HBITMAP bmpExercising = (HBITMAP) LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP3), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
you said "The bitmap should be the background of the window", so you should be drawing the bitmap in response to the WM_ERASEBKGND message instead of the WM_PAINT message.
case WM_ERASEBKGND:
{
HDC hDC = (HDC) wParam;
// draw the bitmap on hDC as needed...
return 1;
}

BS_OWNERDRAW and GDI+

I'm trying to create a launcher window with 5 "invisible" buttons that have a smaller partially transparent image centered inside them. The image inside changes depending on whether the user is hovering over the button. In this example the images are resources IDB_Website1 and IDB_Website2.
The images are in .png format. I'm using GDI+ and loading PNG files from resources with http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI.
I've used BS_OWNERDRAW to handle the graphics of the button. The problem is that I don't know how to link the "PNG from resources" code to the parent's WM_DRAWITEM. With the current way I've done the code, I lose the transparency of the .png by going through HBITMAP.
Global variable:
HBITMAP globalpic;
Main wndproc:
case WM_CREATE:
{
HWND hwndButton = CreateWindow(TEXT("button"), NULL,
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
190, 230, 260, 50, // <- the larger rect size
hWnd, (HMENU) HT_BUTTON1, hInst, NULL);
HTButton = (WNDPROC) SetWindowLong(hwndButton, GWL_WNDPROC, (LONG) ButtonProc);
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT* lpDrawItem = (DRAWITEMSTRUCT*)lParam;
HBITMAP hBmp;
HDC hdc = CreateCompatibleDC(NULL);
if (hovering) // <- mousehover detection, not included
{
gdi.CreateBM(IDB_Website2, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
} else {
gdi.CreateBM(IDB_Website1, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
}
hBmp = globalpic; // get the global var gdi.CreateBM created
DeleteDC(hdc);
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
HDC hMemDC = CreateCompatibleDC(NULL);
HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hBmp);
StretchBlt (lpDrawItem->hDC,
lpDrawItem->rcItem.left + 30,
lpDrawItem->rcItem.top + 5,
200, <- the width of image
40, <- the height of image
hMemDC,
0, 0,
bm.bmWidth,
bm.bmHeight,
SRCCOPY);
SelectObject(hMemDC, hOldBmp);
DeleteDC(hMemDC);
DeleteObject(hBmp);
break;
}
class GDI's method CreateBM.
Currently, since I don't know of a better way, it sends the HBITMAP to the global variable globalpic. The problem is that by doing this, I lose transparency.
void CreateBM(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
CGdiPlusBitmapResource m_pBitmap;
m_pBitmap.Load(menuid, pType, hInst);
m_pBitmap.m_pBitmap->GetHBITMAP(RGB(255, 255, 0), &globalpic);
//m_pBitmap.m_pBitmap->GetHBITMAP(GetBkColor(hdc), &globalpic); // <- no-go either
}
Any way I could do this more directly? I feel that going through the HBITMAP route is completely unnecessary but I couldn't for the life of me figure out how I should do it.
Then again, if using the HBITMAP way is fine, any pointers on how I could make the color (I guess (RGB(255, 255, 0) ...?) transparent with the current implementation?
Answering to Hans:
I also have a GDI method like this, but it's unused at the moment because I couldn't get the image removed after drawing it (as is needed when hovering changes the image):
void Create(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
Graphics grpx(hdc);
ht_img = new CGdiPlusBitmapResource();
ht_img -> Load(menuid, pType, hInst);
grpx.DrawImage(*ht_img, x, y, w, h);
delete ht_img;
}

My code which uses UpdateLayeredWindow doesn't work

I attampted to draw a irregular window with the UpdateLayeredWindow(), in msvc2008, xp sp3.
Here is part of my code:
//Add something(CreateWindowEx()):
hwndCyauWnd = CreateWindowEx(
/*WS_EX_TOOLWINDOW |*/ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_LAYERED,
lpwsCyauClassName,
lpwsCyauWndName,
WS_CLIPSIBLINGS | WS_POPUP,
GetSystemMetrics(SM_CXSCREEN)-320,
GetSystemMetrics(SM_CYSCREEN)-232,
320, 200,
NULL,
NULL,
hInstance,
NULL);
//Skip Lines
HDC hdcCyauWnd = GetDC(hwndCyauWnd);
HDC hdcBuffer = CreateCompatibleDC(hdcCyauWnd);
//HBITMAP hbmCyau = CreateCompatibleBitmap(hdcBuffer,120, 93);
//SelectObject(hdcBuffer, hbmCyau);
POINT ptZero = {0, 0};
POINT ptDrawPos = {0, 0};
RECT rctCyauWnd;
GetWindowRect(hwndCyauWnd, &rctCyauWnd);
SIZE szCyauWnd={rctCyauWnd.right - rctCyauWnd.left, rctCyauWnd.bottom - rctCyauWnd.top};
BLENDFUNCTION blendPixelFunction = { AC_SRC_OVER, 0, 100, AC_SRC_ALPHA};
Graphics gphCyauWnd(hdcBuffer);
Image imgCyau(L"surface0000.png");
gphCyauWnd.DrawImage(&imgCyau, 0, 0, 125, 93);
UpdateLayeredWindow(hwndCyauWnd,
hdcCyauWnd, &ptZero,
&szCyauWnd,
hdcBuffer, &ptZero,
0, //RGB(255, 255, 255),
&blendPixelFunction,
ULW_ALPHA);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
I have tried several method to use this function, but all failed, NOTHING APPEAR on the screen.
Could anybody tell me what happens and how to slove it?
Add:
Whole source file have been upload to my skydrive, anyone can edit, much appreciation! (I have become a poor underdog now...)
You mixed up GDI and GDI+, which is not a good idea. Here is a working example:
hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, szTitle, 0,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
// Load PNG
CImage img;
img.Load("BACKGR.png");
// Get image sizes
int nWidth = img.GetWidth();
int nHeight = img.GetHeight();
// Create memory DC
HDC hdcScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hdcScreen);
// Create memory bitmap
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDC, hBmp);
// Draw image to memory bitmap (currently selected in memory DC)
img.Draw(hDC, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 128;// half transparent
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptLocation = {0, 0};
SIZE szWnd = {nWidth, nHeight};
POINT ptSrc = {0, 0};
UpdateLayeredWindow(hWnd, hdcScreen, &ptLocation, &szWnd, hDC, &ptSrc, 0, &blend, ULW_ALPHA);
ShowWindow(hWnd, SW_SHOW);
SelectObject(hDC, hBmpOld);
DeleteObject(hBmp);
DeleteDC(hDC);
ReleaseDC(NULL, hdcScreen);
If you want GDI+ to draw to an image with an alpha channel, you have to draw to a Bitmap, not an HDC, and you have to specify that the Bitmap's format has alpha. To do that with an HBITMAP, you have to also point GDI+ to the bitmap bits.
Something like this:
BITMAPINFOHEADER bih;
HBITMAP hbmp;
HDC hdc;
void *bits;
bih.biSize = sizeof(bih);
bih.biWidth = width;
bih.biHeight = -height;
bih.biPlanes = 1;
bih.biBitCount = 32;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
hdc = CreateCompatibleDC(NULL);
hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, &bits, NULL, 0);
Bitmap bitmap(width, height, 0, PixelFormat32bppPARGB, bits);
Graphics graphics(bitmap);
graphics->DrawWhatever(...);
graphics->Flush();
SelectObject(hdc, hbitmap);
UpdateLayeredWindow(hwnd, hdc, ...

Resources