Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
I am trying to write some code to capture the image in a dialog as a bitmap then save it to disk as an emf file. But although the .emf file has a size in kB, the image in the .emf file is blank. I am using GDI+. None of the return values show an error. I can use the HBITMAP to save the image in a .bmp file. I then convert the HBITMAP to a Bitmap and try to save as an emf file, but the file image is blank. I don't think there is a problem with my conversion as if I convert the bitmap back to an HBITMAP and then save to a different .bmp it works fine. I think maybe I am using the wrong DC in my Metafile constructor but can't see what to do. Please could somebody help me with this.
 
In stdafx.h I have:
#include <Gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
My method is:
void GDICapture::CaptureDialogImage(CDialog* dialog)// TODO: make things const
{
	ULONG_PTR m_gdiplusToken;
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
	//Gdiplus::GdiplusShutdown(m_gdiplusToken);//do this later
 
	// get the DCs to copy and capture to
	HWND hDialog = dialog->GetSafeHwnd();
	HDC hDialogDC = GetDC(hDialog);
	HDC hCaptureDC = CreateCompatibleDC(hDialogDC);
 
	// get the dialog dimensions
	CRect rect;
	dialog->GetClientRect(&rect);
	int nDialogWidth = rect.Width();
	int nDialogHeight = rect.Height();
 
	HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDialogDC, nDialogWidth, nDialogHeight);
 
	// link hCaptureBitmap to hCaptureDC
	HGDIOBJ hGDIObj = SelectObject(hCaptureDC, hCaptureBitmap);
	if(hGDIObj == HGDI_ERROR)
	{
		int j = 1;//test
	}
	if(hGDIObj == NULL)
	{
		int j = 2;//test
	}
	
	BOOL b = BitBlt(hCaptureDC, 0, 0, nDialogWidth, nDialogHeight, hDialogDC, 0, 0, SRCCOPY | CAPTUREBLT);//non-zero == success
	
	//deselect hCaptureBitmap from hCaptureDC before using hCapturebitmap in FromBITMAP()
	SelectObject(hCaptureDC, hGDIObj);
 
	CImage image;
	image.Attach(hCaptureBitmap);
	image.Save(_T("C:\\test\\bitmap.bmp"));
	hCaptureBitmap = image.Detach();	
	
	//convert HBITMAP to Bitmap
	Bitmap* bm = new Bitmap(nDialogWidth, nDialogHeight);	
	HPALETTE hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
	bm->FromHBITMAP(hCaptureBitmap, hPal);
 
	//get a bitmap handle back again
	HBITMAP hbmNew;
	bm->GetHBITMAP(Color::Blue, &hbmNew);
	CImage image1;
	image1.Attach(hCaptureBitmap);
	image1.Save(_T("C:\\test\\bitmap1.bmp"));
	hCaptureBitmap = image1.Detach();
	/////////////////////////////////////
 
	// Save Captured Bitmap
	CStringW sWFileName = _T("C:\\test\\bitmap.emf");
	Metafile* metaFile = new Metafile(sWFileName, hCaptureDC);
	//tell the graphics object it has to draw on the metafile
	Graphics graphics(metaFile);
	graphics.DrawImage(bm, 0, 0, bm->GetWidth(), bm->GetHeight());
	//////////////////////////////////////////////////////////////////////////
	
	ReleaseDC(hDialog, hDialogDC);
	DeleteDC(hCaptureDC);
	DeleteObject(hCaptureBitmap);// I don't need to do this
}
Posted 14-Sep-12 4:59am
Edited 17-Sep-12 23:45pm
v7
Comments
Jochen Arndt at 14-Sep-12 11:49am
   
It's difficult to find the problem without having an idea where your code fails. You should check all return values from functions that may fail.
 
I see one possible error source:
You did not de-select the capture bitmap out of the device context.
 
See the Bitmap::FromHBITMAP() method in the MSDN:
Do not pass to the FromHBITMAP method a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
 
More notes:
 
Typo: GettockObject(); would not compile.
 
If you want to use Unicode strings with ANSI builds just pass them as L'bitmap.emf' for constant strings or use CStringW (you can pass an ANSI string to the CStringW constructor; it is converted automatically to a wide string).
Jackie Lloyd at 17-Sep-12 11:02am
   
Hi,
I have updated as you suggest. I also found out that no file was created on C: as I did not have admin rights. So now an .emf file is created but there is no image in althoug the file does have a size in KB. I really don't know what is wrong as I have looked at a lot of examples today and can't spot why mine is wrong.
Jochen Arndt at 17-Sep-12 11:23am
   
There is something screwed up now in your code. The de-selection must be:
SelectObject(hCaptureDC, hGDIObj1);
And the second call of CreateCompatibleBitmap() must be removed.
NOTE:
SelectObject() returns the previously selected object that can be later used to restore the original state. If there is actually no object selected of the passed type, the return value may be NULL which is not an error in this case.
Jochen Arndt at 17-Sep-12 12:01pm
   
Yes (the '1' was my error). Assigning a return value is not necessary (it must be hCaptureBitmap, the previously selected object). You may edit your question again to show the actual code. For testing you may also use a bmp file extension to create a plain bitmap (but I don't think that this is the problem).
To test if your bitmap is created, you may use the old GDI (after BitBlt and de-selction) to save as BMP file (or JPG/PNG/GIF by file extension):
#include
CImage Image;
Image.Attach(hCaptureBitmap);
Image.Save(_T("test.bmp"));
hCaptureBitmap = Image.Detach();
 
If this works, the error is in the GDI+ saving range; otherwise it is in the bitmap creation.
Jackie Lloyd at 17-Sep-12 12:38pm
   
Hi,
I did this and I got a .bmp file save with a screen shot of my screen, not my dialog. The screenshot appeared to be in in the same position and have the same size as my dialog.
Jochen Arndt at 17-Sep-12 12:55pm
   
Then try first to get a file with the dialog image (I can't see from the code why it is a full screen image). If that image is OK, we need further investigation with the GDI+ saving.
Jackie Lloyd at 18-Sep-12 4:09am
   
Hi Jochen,
I now have the dialog saved in the bitmap - the problem was that i had a breakpoint in the code so that as soon as I clicked a button to save the dialog, the dialog disappeared and teh visual studio view appeared, thus wiping out the dialog from the screen. Without the breakpoint it all works fine. So the only problem is my conversion from HBITMAP to Bitmap :)
 
I am wondering if the DC passed to the Metafile constructor is wrong as it an HDC for the 'in memory' copy of the image. Does a metafile need a different type of DC?
Jochen Arndt at 18-Sep-12 5:01am
   
I did not have used GDI+ often. So I'm also stuck now.
 
Did you read this article http://www.codeproject.com/Articles/5245/Conversion-of-Enhanced-Metafile-to-Bitmap-and-vice?
 
However, you may also try to use the CImage for writing rather than creating a Bitmap object.
CImage Image;
Image.Attach(hCaptureBitmap);
[...]
graphics.DrawImage(&Image);
hCaptureBitmap = Image.Detach();
Jackie Lloyd at 18-Sep-12 8:01am
   
thanks - the article helped me finish this off an get it working. I pasted the code below. I really appreciate your help.
Jochen Arndt at 18-Sep-12 8:07am
   
Good to know that everything is working now.
TRK3 at 17-Sep-12 19:25pm
   
Sounds like you are a lot closer now.
 
Are you calling this function on your dialog before your dialog gets a chance to paint?
Jackie Lloyd at 18-Sep-12 3:33am
   
thanks, you were nearly right which helped me solve the problem as described below.
Jackie Lloyd at 17-Sep-12 11:52am
   
Thankyou very much for keeping on at this with me.
 
I have removed the second call to CreateCompatibleBitmap.
 
And before the bitmap conversion I tried both of these seperately:
HGDIObj hGDIObj1 = SelectObject(hCaptureDC, hGDIObj);
and:
HGDIObj hGDIObj1 = NULL;
SelectObject(hCaptureDC, hGDIObj1);
 
but neither of these solved my problem, and my .emf file is still blank.
Jochen Arndt at 17-Sep-12 12:03pm
   
Answered to the other (deleted) comment. So see the comment above.
Jackie Lloyd at 14-Sep-12 14:16pm
   
Hi, I'm sorry there were mistakes in what I wrote, symptom of state of mind after getting nowehere with this for several hours.
I had checked all return values and everything looked ok.
I have tried to understand what I should do about de-selecting the capture bitmap- can you give me some more info please. And what should what I should pass to FromHBITMAP? There is another method:
static Bitmap* FromHBITMAP(
[in] HBITMAP hbm,
[in] HPALETTE hpal
);
would this be better?
Richard MacCutchan at 14-Sep-12 15:46pm
   
I have a recollection of an article (search the articles section) somewhere here which was about capturing the dialog window in a similar manner to what you are trying to do. Sorry, I can't remember its title and foolishly omitted to bookmark it.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

Don't forget to call DeleteEnhMetaFile, otherwise the file will be locked by the application
  Permalink  
Comments
Jackie Lloyd at 14-Feb-14 15:37pm
   
Thanks very much, a very useful tip.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

This code finally worked, although I don't know why I had to extract the bitmap bm1 width and height straight after I had created the bitmap as if I tried to access these variable in the bitmap within BitBlt it failed for some reason.
 
Many, many thanks to Jochen for perservering with me Smile | :)
 
void GDICapture::CaptureDialogImage(CDialog* dialog)// TODO: make things const
{
	ULONG_PTR m_gdiplusToken;
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
	//Gdiplus::GdiplusShutdown(m_gdiplusToken);//do this later
	// get the DCs to copy and capture to
	HWND hDialog = dialog->GetSafeHwnd();
	HDC hDialogDC = GetDC(hDialog);
	HDC hCaptureDC = CreateCompatibleDC(hDialogDC);
	// get the dialog dimensions
	CRect rect;
	dialog->GetClientRect(&rect);
	int nDialogWidth = rect.Width();
	int nDialogHeight = rect.Height();
	HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDialogDC, nDialogWidth, nDialogHeight);
	// link hCaptureBitmap to hCaptureDC
	HGDIOBJ hGDIObj = SelectObject(hCaptureDC, hCaptureBitmap);
		
	BOOL b = BitBlt(hCaptureDC, 0, 0, nDialogWidth, nDialogHeight, hDialogDC, 0, 0, SRCCOPY | CAPTUREBLT);//non-zero == success
	
	//deselect hCaptureBitmap from hCaptureDC before using hCapturebitmap in FromBITMAP()
	SelectObject(hCaptureDC, hGDIObj);
	CImage image;
	image.Attach(hCaptureBitmap);
	image.Save(_T("C:\\test\\bitmap.bmp"));
	hCaptureBitmap = image.Detach();	
	
	//convert HBITMAP to Bitmap
	Bitmap* bm = new Bitmap(nDialogWidth, nDialogHeight);	
	HPALETTE hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
	bm->FromHBITMAP(hCaptureBitmap, hPal);
	
	//save the bmp in an emf file
	Bitmap* bm1 = new Bitmap(nDialogWidth, nDialogHeight);
	int nWidth = bm1->GetWidth();
	int nHeight = bm1->GetHeight();
	GetObject(hCaptureBitmap, sizeof(BITMAP), bm1);
	HDC hdcMem = ::CreateCompatibleDC(GetDC(NULL));
	SelectObject(hdcMem, hCaptureBitmap);
	HDC emfdc = CreateEnhMetaFile(hdcMem, _T("C:\\test\\testEmf.emf"), NULL, "bkjfygsd");
	BitBlt(emfdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, SRCCOPY);
	::CloseEnhMetaFile(emfdc);
 
	ReleaseDC(hDialog, hDialogDC);
	DeleteDC(hCaptureDC);
	DeleteObject(hCaptureBitmap);// I don't need to do this
}
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Advertise | Privacy | Mobile
Web04 | 2.8.141220.1 | Last Updated 14 Feb 2014
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100