Click here to Skip to main content
15,881,882 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
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
Updated 17-Sep-12 22:45pm
v7
Comments
Jochen Arndt 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 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 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 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 <atlimage.h>
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 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.

Don't forget to call DeleteEnhMetaFile, otherwise the file will be locked by the application
 
Share this answer
 
Comments
Jackie Lloyd 14-Feb-14 15:37pm    
Thanks very much, a very useful tip.
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 :)

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
}
 
Share this answer
 

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900