Hello to all,

i have a jpg that i want to set as wallpaper. I am using masm32 libs BitmapFromFile and i get a handle to the generated bitmap.
The call to set the wallpaper (SystemParametersInfo) wants the full path to the bitmap and not a handle.
so i thought to save the bitmap to a temp dir and use that!
But, how can i save the bitmap generated from BitmapfromFile ??
Posted on 2004-03-26 08:30:59 by Ray
You will need to GetObject() on the bitmap handle, and use the BITMAP structure parameters (width, height, pointer to data) to construst .bmp file. BMP file is BITMAPFILEHEADER structure, BITMAPINFOHEADER and structure, followed by data.
Posted on 2004-03-26 09:46:03 by comrade
Pain in the arse those bitmaps :) This code will work.

SaveBitmap Proc hBitmap:DWORD,hFile:DWORD

LOCAL cbWrite :DWORD
LOCAL bmp :BITMAP
LOCAL dwNumColors :DWORD
LOCAL hDC :DWORD
LOCAL hDC_DIB :DWORD
LOCAL hDC_DDB :DWORD
LOCAL pBMI :DWORD
LOCAL RGBQuadSize :DWORD
LOCAL DataSize :DWORD
LOCAL pBFH :DWORD
LOCAL pData :DWORD
LOCAL pRGBQuad :DWORD
LOCAL imgX :DWORD
LOCAL imgY :DWORD
LOCAL OldObj :DWORD
LOCAL hNewBmp :DWORD
LOCAL cClrBits :DWORD

invoke GetObject,hBitmap,SIZEOF BITMAP,ADDR bmp
mov eax,bmp.bmWidth
mov imgX,eax
mov eax,bmp.bmHeight
mov imgY,eax

mov eax,bmp.bmBitsPixel
mov cClrBits,eax

; Calculate size of RGBQUAD array
mov ecx,cClrBits
mov eax,1
shl eax,cl
mov dwNumColors,eax
mov edx,SIZEOF RGBQUAD
imul edx
mov RGBQuadSize,eax

; Calculate Data size
mov eax,imgX
imul cClrBits
add eax,31
and eax,-31
shr eax,3
imul imgY
mov DataSize,eax

; Create a memory buffer
mov eax,RGBQuadSize
.IF cClrBits == 24 ; There is no RGBQUAD array for 24 bit
mov dwNumColors,0
mov RGBQuadSize,0
mov eax,0
.ENDIF
add eax,SIZEOF BITMAPINFOHEADER
add eax,SIZEOF BITMAPFILEHEADER
add eax,DataSize
invoke GlobalAlloc,GMEM_FIXED,eax
mov pBFH,eax

add eax,SIZEOF BITMAPFILEHEADER
mov pBMI,eax
add eax,SIZEOF BITMAPINFOHEADER
mov pRGBQuad,eax
add eax,RGBQuadSize
mov pData,eax

; The bitmap must be a compatible bitmap
; so bitblt the DIB into a new compatible bitmap
invoke GetDC,hDlg
mov hDC,eax
invoke CreateCompatibleDC,hDC
mov hDC_DIB,eax
invoke CreateCompatibleDC,hDC
mov hDC_DDB,eax
invoke CreateCompatibleBitmap,hDC,imgX,imgY
mov hNewBmp,eax
invoke ReleaseDC,hDlg,hDC

invoke SelectObject,hDC_DDB,hNewBmp
mov OldObj,eax
invoke SelectObject,hDC_DIB,hBitmap
invoke BitBlt,hDC_DDB,0,0,imgX,imgY,hDC_DIB,0,0,SRCCOPY
invoke SelectObject,hDC_DDB,OldObj
invoke SelectObject,hDC_DIB,OldObj
invoke DeleteDC,hDC_DIB

invoke GetObject,hNewBmp,SIZEOF BITMAP,ADDR bmp

mov edi,pBMI
mov [edi].BITMAPINFO.bmiHeader.biXPelsPerMeter,0
mov [edi].BITMAPINFO.bmiHeader.biYPelsPerMeter,0
mov eax,dwNumColors
mov [edi].BITMAPINFO.bmiHeader.biClrUsed,eax
mov [edi].BITMAPINFO.bmiHeader.biClrImportant,0

mov [edi].BITMAPINFO.bmiHeader.biSize,SIZEOF BITMAPINFOHEADER
mov eax,bmp.bmWidth
mov [edi].BITMAPINFO.bmiHeader.biWidth,eax
mov eax,bmp.bmHeight
mov [edi].BITMAPINFO.bmiHeader.biHeight,eax
mov [edi].BITMAPINFO.bmiHeader.biPlanes,1
mov [edi].BITMAPINFO.bmiHeader.biCompression,BI_RGB
mov eax,cClrBits
mov [edi].BITMAPINFO.bmiHeader.biBitCount,ax
mov eax,DataSize
mov [edi].BITMAPINFO.bmiHeader.biSizeImage,eax

invoke GetDIBits, hDC_DDB, hNewBmp, 0, imgY, pData, pBMI, DIB_RGB_COLORS

mov esi,pBFH
mov [esi].BITMAPFILEHEADER.bfType,"MB"
mov eax,RGBQuadSize
add eax,DataSize
add eax,SIZEOF BITMAPINFOHEADER + SIZEOF BITMAPFILEHEADER
mov [esi].BITMAPFILEHEADER.bfSize,eax
mov [esi].BITMAPFILEHEADER.bfReserved1,0
mov [esi].BITMAPFILEHEADER.bfReserved2,0
mov eax,RGBQuadSize
add eax,sizeof BITMAPFILEHEADER
add eax,sizeof BITMAPINFOHEADER
mov [esi].BITMAPFILEHEADER.bfOffBits,eax

mov ecx,SIZEOF BITMAPFILEHEADER
add ecx,sizeof BITMAPINFOHEADER
add ecx,DataSize
add ecx,RGBQuadSize
invoke WriteFile,hFile,pBFH,ecx,ADDR cbWrite,NULL

invoke DeleteDC,hDC_DDB
invoke GlobalFree,pBFH
invoke DeleteObject,hBitmap
invoke DeleteObject,hNewBmp

ret
SaveBitmap endp
Posted on 2004-03-26 10:08:08 by donkey
Btw, you should fix up the BitmapFromFile (and friends) from masm32, they have a couple of quirks.

First, they call CoInitialize and CoUninitialize every time they are called. I guess this is okay to have the very self-contained and makes everybody able to use it - it's more efficient to call CoInitialize and CoUninitialize once at program startup.

Next, at least BitmapFromMemory has a bug: it uses CreateStreamOnHGlobal with "fDeleteOnRelease" set to true - but it still frees the memory itself (CoTaskMemFree near the end). This is a double free, and while it only causes crashes on some win32 problems, it's a nasty bug (double frees are sometimes used in malicious exloits). Besides, if you want to be really safe, you should use the icky GlobalAlloc for the memory.

Other than that, I guess they're okay.
Posted on 2004-03-26 11:18:40 by f0dder
Thanks guys,
donkey, thanks for the code, i will use it:)

f0dder: hmm, ok, coInit and coUnInit its clear..
the other bug you mentioned about freeing the mem, which call of the 2 u think should i comment out?

:stupid:
Posted on 2004-03-27 04:50:37 by Ray
You should remove the final CoTaskMemFree and leave fDeleteOnRelease set to TRUE - this way you don't have to keep the memory pointer around. Not very important in this example, since everything is included in one PROC, but in more advanced applications where the lifetime of the istream is longer, this makes resource management easier.

And you really ought to use GlobalAlloc for the memory :), just to follow the PlatformSDK nazi-ishly and be sure that there's no problems whatsoever with your code, even theoretical problems ^_^
Posted on 2004-03-27 05:04:00 by f0dder
damned you f0dder !

you always find problems where there aren't! hehe..

donkey: the code works just fine :)

:alright:
Posted on 2004-03-27 06:15:05 by Ray

you always find problems where there aren't! hehe..

Well, the double free problem is real enough, although it only causes problems on some windows versions (NT4, I think). The memory allocation thing *shouldn't* really matter, but I'd rather be safe than sorry. Especially because I've had some weird problems with the clipboard API when not using GlobalAlloc, as the PlatformSDK says you should.
Posted on 2004-03-27 06:30:45 by f0dder
indeed !

After some time of debugging, i traced a bug back to that function ! (BitmapFromFile). After a few repeative calls it exits with an INVALID_FILE_NAME ! Yikes! Even though the file ptr is just fine!

I dont want to dig in more that this,,, but has anyone witnessed similar behaviour?
Posted on 2004-03-28 01:44:04 by Ray
Somebody had crashes on NT4. I don't know how bad the bug is on later NT's, but double frees can lead to heap corruption, which is a thing you definitely don't want. And the constant COM init/deinit it a bit silly (but given the context: "code snippet that everybody should be able to use without having even an understanding of programming", it's fair enough, I guess).
Posted on 2004-03-28 05:04:16 by f0dder
Yup,

Watch for GDI leaks as well, I remember QvasiModo and I pointed a couple out in BitmapFromPicture a while back, I think they were fixed in the new version but I haven't updated MASM in some time so I am not sure. If I recall correctly it was a problem with a bitmap not being selected out of it's DC and the DC not being released.
Posted on 2004-03-28 05:21:00 by donkey
got it.
thanks!
Posted on 2004-03-29 07:49:03 by Ray