Click here to Skip to main content
Click here to Skip to main content

Screen and Form capture with Managed C++

By , 3 Jul 2002
 

Introduction

I was continuing to play around with IJW. And when I realized that there was no direct Screen Capture method in .NET, I thought I might as well try to make a simple Screen Capturing program. Of course since I knew as much about GDI programming as some of these Canadians seem to know about Cricket, you might as well have guessed that I found things more than a little difficult. Luckily Tom Archer, James Johnson and Neil Van Note promptly answered my questions.

The sample program allows you to capture the entire screen or only the current form and also allows you to save the image, though here I have hard coded the filename and path, as I was too lazy to show a save dialog. If you repeatedly click on the capture buttons, you get a weird effect of cascading screen captures, one on top of the other as shown in the screenshot of the sample program.

Quick Rundown

Using GetDC or GetWindowDC depending on whether we are capturing the screen or the current window, we get our HDC using which we call CreateCompatibleDC to create our memory DC. Using a call to GetSystemMetrics we get the size of the screen and then call CreateCompatibleBitmap to create a bitmap. Then we select this bitmap into the memory DC using a call to SelectObject and save the old bitmap. Using BitBlt we copy the screen or window contents into this bitmap which is now selected into the memory DC. Then we select the original bitmap back into the DC, delete the memory DC, release the DC, and delete the bitmap. To be frank, there is not much .NET stuff involved in the code, but imagine if you had to write all this using C#. Think of all the P/Invoke stuff you'd have to do and the ugly declarations you'd have had to write for the API calls. With MC++ you are saved all that bother.

Full Source Listing

The source code is self-explanatory. Notice how I have written my WinMain. This is because I am including windows.h where WinMain is already prototyped in that fashion.

#include "stdafx.h"

#using <mscorlib.dll>
#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

#include <tchar.h>
#include <windows.h>

using namespace System;
using namespace System::ComponentModel;
using namespace System::Drawing;
using namespace System::Windows::Forms;

public __gc class NForm : public Form
{
public:
    Button *btn;
    Button *btn2;
    Button *btn3;
    PictureBox *pbox;
    void btn_Click(Object *sender, System::EventArgs* e); 
    void CaptureScreen(bool FullScreen);

    NForm()
    {
        this->StartPosition = FormStartPosition::CenterScreen;
        this->Text = "Capture screen - Nish for CodeProject -IJW";
        this->Size = Drawing::Size(750,550);
        this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; 

        btn=new Button();
        btn->Text="Capture Screen";
        btn->Location=Point(5,5);
        btn->Size=Drawing::Size(100,30);
        btn->add_Click(new EventHandler(this,&NForm::btn_Click)); 
        this->Controls->Add(btn);

        btn2=new Button();
        btn2->Text="Capture Form";
        btn2->Location=Point(125,5);
        btn2->Size=Drawing::Size(100,30);
        btn2->add_Click(new EventHandler(this,&NForm::btn_Click)); 
        this->Controls->Add(btn2);

        btn3=new Button();
        btn3->Text="Save C:\\Z.Jpg";
        btn3->Location=Point(250,5);
        btn3->Size=Drawing::Size(100,30);
        btn3->add_Click(new EventHandler(this,&NForm::btn_Click)); 
        this->Controls->Add(btn3);

        pbox = new PictureBox();
        pbox->Location=Point(5,45);
        pbox->Size=Drawing::Size(730,470);
        pbox->BorderStyle = BorderStyle::Fixed3D;
        pbox->SizeMode = PictureBoxSizeMode::StretchImage;


        this->Controls->Add(pbox);
    }
};

int __stdcall WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
    Application::Run(new NForm());
    return 0;
}

void NForm::btn_Click(Object *sender, System::EventArgs *e)
{
    if(sender->Equals(btn))
        CaptureScreen(true);
    else
    {
        if(sender->Equals(btn2))
            CaptureScreen(false);
        else
            pbox->Image->Save("C:\\Z.Jpg",
                Drawing::Imaging::ImageFormat::Jpeg);
    }
}

void NForm::CaptureScreen(bool FullScreen)
{
    HDC hDC;
    if(FullScreen)
        hDC = GetDC(NULL); //Get full screen
    else
    {
        HWND hWnd=(HWND)this->Handle.ToInt32(); //Get HWND of form
        hDC = GetWindowDC(hWnd); //Now get it's DC handle 
    }
    HDC hMemDC = CreateCompatibleDC(hDC);
    RECT r;
    GetWindowRect((HWND)this->Handle.ToInt32(),&r); //need this for Form
    SIZE size;
    if(FullScreen)
    { 
        size.cx = GetSystemMetrics(SM_CXSCREEN);
        size.cy = GetSystemMetrics(SM_CYSCREEN);
    } 
    else
    {
        size.cx = r.right-r.left;
        size.cy = r.bottom-r.top;
    }

    //most of the remaining code is normal GDI stuff

    HBITMAP hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
    if (hBitmap)
    {
        HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);
        BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
        SelectObject(hMemDC, hOld);
        DeleteDC(hMemDC);
        ReleaseDC(NULL, hDC);
        pbox->Image = Image::FromHbitmap(hBitmap); 
        DeleteObject(hBitmap);
    }
}

History

  • May 7 02 - Posted the article
  • Jul 4 02 - Added sample VC++ project

License

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

About the Author

Nish Sivakumar
United States United States
Member
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberAziz Yosofi6 Feb '13 - 22:25 
greet vc
Questionexcuse me, i have a problem in capturing when i play the videomemberzhmx_love21 Aug '12 - 22:47 
thanks to coming,when i use the function named "StretchBlt" to capture a video's frame, but fail.
the rusult is that the image is full of black.
do anyone can give a suggestion?thanks.
sign up my email:563453708@qq.com
GeneralMy vote of 5memberMember 923867912 Jul '12 - 22:27 
Completely working and Very Helpful!!
QuestionQuestions regarding your articlememberOlariu Bogdan25 May '10 - 1:23 
Hi,
 
I have 2 questions:
 
1) I have tried to use your code to enhance my application to save the current frame to bitmap but I encounter the following error:
 
>d:\visual studio 2005\projects\windowsformscpp\windowsformscpp\Form1.h(292) : error C2665: 'System::Drawing::Image::FromHbitmap' : none of the 2 overloads could convert all the argument types
1> c:\winnt\microsoft.net\framework\v2.0.50727\system.drawing.dll: could be 'System::Drawing::Bitmap ^System::Drawing::Image::FromHbitmap(System::IntPtr)'
1> while trying to match the argument list '(HBITMAP)'
 
2) Can I avoid to use pictureBox1 object and save the bitmap to file directly from hMemDC
 
Below you have a listing with my code:
 
HDC hDC;
 
HWND hWnd=(HWND)this->Handle.ToInt32(); //Get HWND of form

hDC = GetWindowDC(hWnd); //Now get it's DC handle
 
HDC hMemDC = CreateCompatibleDC(hDC);
RECT r;
GetWindowRect((HWND)this->Handle.ToInt32(),&r); //need this for Form
 
SIZE size;
 
size.cx = r.right-r.left;
size.cy = r.bottom-r.top;
 
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
 
if (hBitmap)
{
HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hOld);
 
pictureBox1->Image = Image::FromHbitmap(hBitmap);
//pbox->Image->Save("C:\\Z.Jpg", Drawing::Imaging::ImageFormat::Jpeg);
 
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);

DeleteObject(hBitmap);
}
AnswerRe: Questions regarding your articlemvpNishant Sivakumar25 May '10 - 1:29 
I assume you've correctly converted from MC++ to C++/CLI.
 
For (1) you may need to cast to (IntPtr). Try:
 
Image::FromHbitmap((IntPtr)hBitmap);
 
And for (2) FromHbitmap returns a Bitmap object on which you can directly call Save.

GeneralRe: Questions regarding your articlememberOlariu Bogdan25 May '10 - 23:39 
Hi Nish,
 
thank you very much for your suggestions.
Now the program is working without errors.
 
To save the image I used the following syntax:
Bitmap bmp=Image::FromHbitmap((IntPtr)hBitmap);
bmp.Save("C:\\Z.Jpg", Drawing::Imaging::ImageFormat::Jpeg);
 
But the problem is that I save now the Visual C environment in the picture and not the graphics that is drawn in the Form1.
I used your code in the Form1.h, Form1_Load function (private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)).
 
I have tried to use your code in WindowsFormsCPP.cpp after Application::Run(gcnew Form1()) function, but I encounter the errors below.
 
Please let me know where to place this code in order to capture the Form 1 graphics or how to point to this graphics.
If my problem is not clear for you please send me some contact email to send you the code.
 
Best regards,
Bogdan
 

>.\WindowsFormsCPP.cpp(25) : error C2673: 'main' : global functions do not have 'this' pointers
1>.\WindowsFormsCPP.cpp(25) : error C2227: left of '->Handle' must point to class/struct/union/generic type
1>.\WindowsFormsCPP.cpp(25) : error C2228: left of '.ToInt32' must have class/struct/union
1>.\WindowsFormsCPP.cpp(31) : error C2673: 'main' : global functions do not have 'this' pointers
1>.\WindowsFormsCPP.cpp(31) : error C2227: left of '->Handle' must point to class/struct/union/generic type
1>.\WindowsFormsCPP.cpp(31) : error C2228: left of '.ToInt32' must have class/struct/union
Questionhow to get the image containing total content of entire form in .net windows applicationmemberm.raghu18 Jun '07 - 19:13 
i want the total form content that will be saved as an image in .net windows application. i am getting the image as desktop capturing but i need to save the image with total content of the form. kindly help me plz.
 
Raghu

QuestionHow CreateDIBSection Work???memberjavad_200524 Sep '06 - 9:32 
For Example
HBITMAP DirectBitmap= CreateDIBSection(DirectDC, (BITMAPINFO *)&RGB32BitsBITMAPINFO, DIB_RGB_COLORS,(void **)&ptPixels, NULL, 0);
is not Manage C++
I Want to Eork With Manage
Please Convert Code To Manage

QuestionC++/CLI Image::FromHBitmapmemberCoder2k25 Jun '06 - 8:35 
I am using the new C++/CLI syntax and I am getting compiler errors when I try and create an image object from the HBITMAP pointer. I have tried a couple of different things including wrapping it in a new IntPtr object. Any other ideas?
QuestionRe: C++/CLI Image::FromHBitmapmemberCoder2k25 Jun '06 - 10:01 
I have fixed that error now I am having issues with trying to assign it to a Bitmap object. I get error C3073: ref class does not have a user-defined copy constructor. The line is: Bitmap image = Image::FromHBitmap((IntPtr)hBitmap));
AnswerRe: C++/CLI Image::FromHBitmapmemberCoder2k25 Jun '06 - 10:15 
Fixed it by not assigning the bitmap to the variable but returning it directly. However this doesn't allow the destruction of hBitmap with DestroyObject and could cause a memory leak unless c++/cli automatically takes care of that.
Generalproblemmembermr_ander5on6 Jun '05 - 10:58 
Sigh | :sigh: hi nish, i found your article very interesting, but following it i encountered a problem o nullreferenceexception probably due tu hdc because at debug you can read that hdc has an undefined value...what am i doing wrong?
 
i'm linking to a dll that need an hdc to draw an image modifying one passed and this is the error:
hwnd{HWND__} { unused= } HWND__*
 
on this source code:
 
private: System::Void btn_link_Click(System::Object * sender, System::EventArgs * e)
{
typedef UINT (CALLBACK* LPFNDLLFUNC1)();
typedef UINT (CALLBACK* LPFNDLLFUNC2)(DWORD,UINT);
typedef UINT (CALLBACK* LPFNDLLFUNC3)(void*,void*,HWND);

System::Drawing::Rectangle rect;
rect.set_X(10);
rect.set_Y(10);
rect.set_Height(10);
rect.set_Width(10);

Bitmap* bmp = new Bitmap("C:\\Documents and Settings\\mr_anderson\\Desktop\\ProvaDll\\Immagine4.bmp");

Graphics* g=Graphics::FromImage(bmp);

g = pictureBox1->CreateGraphics();
g->DrawImage(bmp,1,1);
 

System::Drawing::Imaging::BitmapData* bmpdata = bmp->LockBits(rect, ImageLockMode::ReadWrite, PixelFormat::Format24bppRgb);


/*IntPtr punt= bmpdata->get_Scan0();
void* temp = punt.ToPointer();*/
//***********************************************************
void* temp = bmpdata->get_Scan0().ToPointer();
//***********************************************************

 
//***********************************************************
System::Reflection::MethodInfo* method;
method= this->GetType()->GetMethod("condClr");
IntPtr ptr = method->MethodHandle.GetFunctionPointer();
void* funcptr = ptr.ToPointer();
//***********************************************************


 

//************************************************************
HWND hWnd=(HWND)pictureBox2->Handle.ToInt32();
HDC hdc = ::GetDC(hWnd);
//HDC hMemDC = CreateCompatibleDC(NULL);
////GetWindowRect((HWND)pictureBox2->Handle.ToInt32(),pictureBox2);
//
//*************************************************************
 

HINSTANCE hDLL; // Handle to DLL
LPFNDLLFUNC1 start,stop;
LPFNDLLFUNC3 execute;
LPFNDLLFUNC2 setsize; // Function pointer
/*DWORD dwParam1;
UINT uParam2, uReturnVal;*/
hDLL = LoadLibrary("C:\\Documents and Settings\\mr_anderson\\Desktop\\ProvaDll\\xvBlob.dll");
MessageBox::Show("eccezione");
if (hDLL != NULL)
{
MessageBox::Show("Dll linkata");
start = (LPFNDLLFUNC1)GetProcAddress(hDLL, "BlobStart");
stop = (LPFNDLLFUNC1)GetProcAddress(hDLL, "BlobStop");
execute = (LPFNDLLFUNC3)GetProcAddress(hDLL, "BlobExcecute");
setsize = (LPFNDLLFUNC2)GetProcAddress(hDLL, "BlobSetSize");

if (!start && !stop && !execute && !setsize)
{
// handle the error
FreeLibrary(hDLL);
}
else
{
// call the function
start();
MessageBox::Show("start funziona");
setsize(bmp->Width,bmp->Height);
MessageBox::Show("setsize funziona");
/*execute(temp, funcptr, hMemDC);*/
execute(temp, funcptr, hWnd);
MessageBox::Show("execute funziona");
stop();
MessageBox::Show("stop funziona");
MessageBox::Show("Funzione linkata");
FreeLibrary(hDLL);
 
bmp->UnlockBits(bmpdata);
::ReleaseDC(hWnd, hdc);
}
}
}
 

thanks in advance

 
donato
Questionhow SetDIBitsToDevice work???memberjags_vc17 Mar '05 - 18:23 
hello???
 
i want to know how SetDIBitsToDevice works ???
I have tryied it to display bitmap DIB data to directly to a
CPaintDC object,but displayed bitmap is cutting bitmap that is not a full bitmap so can any You tell how SetDIBitsToDevice works or any other idea????
GeneralBlack Screenmembersomeone3214824 Apr '04 - 14:10 
Hi, i'm using Borland C++ 6.0 and when I try to compile the code I get an error on the following line:
 
pbox->Image = Image::FromHbitmap(hBitmap);
 
so i changed that line for a function that saves the hBitmap to a file, bue then i get only a black screen on the file.
 
Any ideas why?
AnswerRe: Black Screen [modified]memberdamn_tiby27 Apr '07 - 6:39 
I've solved this problem for C++Builder.
 
You can use a TImage :
screen->Piture->Bitmap->Handle=hBitmap;
 
here is the code(just for the whole screen):
 
HDC hDC;
hDC=GetDC(NULL);
HDC hMemDC=CreateCompatibleDC(hDC);
RECT r;
HWND hwnd=this->Handle;
GetWindowRect(hwnd,&r);
SIZE size;
size.cx = GetSystemMetrics(SM_CXSCREEN);
size.cy = GetSystemMetrics(SM_CYSCREEN);
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
 
if (hBitmap)
{
HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hOld);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
screen->Picture->Bitmap->Handle=hBitmap;
 
}
 
have fun!
 

 
-- modified at 12:44 Friday 27th April, 2007
Generala questionsussyediyildiz1 Aug '02 - 23:02 
hi,
you save the image to file by using the picture box.
How can i save the image without first displaying it on the picture box? i mean i dont want to display the screenshot but to save it to a file in jpg format.
i searched to save the device context object that is created by the bitblt method but couldnt find.
thanx...
GeneralRe: a questioneditorNishant S1 Aug '02 - 23:11 
Take a look at System.Drawing.Image
 
Nish

 

Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Review by Shog9
Click here for review[NW]

GeneralRe: a questionsussyediyildiz2 Aug '02 - 1:24 
i use Visual C++ 6.0 ?
Generaldohmemberem4 Jul '02 - 15:07 
the screen capture doesn't grab any windows with transparency on win2k/xp
GeneralGood work but..memberRama Krishna8 May '02 - 4:09 
Good Effort! If you write articles at this speed you will soon become the code project top article writer by the end of this year Smile | :)
 
IJW is quite easy and simple to use so some performance considerations can easily be overlooked. In general the managed to unmanaged transitions should be minimized. For instance the CaptureScreen method has simply too many calls to unmanaged methods. Each of these calls would cause a transition so you pay a performance penalty.
 
Luckily VC++ provides #pragma unmanaged. Using which you can produce native output for function following the #pragma. You can refactor the CaptureScreen functions so that it takes only handle to the current window and fullscreen parameter, and returns an image handle. Ofcourse you need to make it a global function. You could do something like this
#pragma unmanaged
HBITMAP CaptureScreen(bool FullScreen, HWND hwnd)
{
	HDC hDC;
	if(FullScreen)
		hDC = GetDC(NULL); //Get full screen
	else
	{
		hDC = GetWindowDC(hwnd); //Now get it's DC handle 
	}
	HDC hMemDC = CreateCompatibleDC(hDC);
	RECT r;
	GetWindowRect(hwnd,&r); //need this for Form
	SIZE size;
	if(FullScreen)
	{ 
		size.cx = GetSystemMetrics(SM_CXSCREEN);
		size.cy = GetSystemMetrics(SM_CYSCREEN);
	} 
	else
	{
		size.cx = r.right-r.left;
		size.cy = r.bottom-r.top;
	}
 
	//most of the remaining code is normal GDI stuff

	HBITMAP hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
	if (hBitmap)
	{
		HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);
		BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);
		SelectObject(hMemDC, hOld);
		DeleteDC(hMemDC);
		ReleaseDC(NULL, hDC);
	}
	
	return hBitmap;
}
#pragma managed

 
Then you can call this function in the regular way. The advantage you minimize the transistions to just 1, and make the world a better place to live in Smile | :)
GeneralRe: Good work but..memberNish [BusterBoy]8 May '02 - 8:56 
Thanks Rama Rose | [Rose]
 
I really think that you should write an article on VC++.NET optimizations.
 
Nish
 

The posting stats are now in PDF:-
http://www.busterboy.org/codeproject/
Feel free to make your comments.
Updated - May 04th, Saturday

GeneralRe: Good work but..memberRama Krishna8 May '02 - 8:57 
I need to finish my screen saver first. Today I added a best of Nish featureSmile | :)
GeneralRe: Good work but..memberNish [BusterBoy]8 May '02 - 9:08 
Rama Krishna wrote:
I need to finish my screen saver first. Today I added a best of Nish feature
 
Oh, okay Smile | :)
 
Nish
 

The posting stats are now in PDF:-
http://www.busterboy.org/codeproject/
Feel free to make your comments.
Updated - May 04th, Saturday

GeneralRe: Good work but..subeditorJames T. Johnson8 May '02 - 9:38 
Thanks, I had forgotten all about that too. Now to modify my MC++ class to put the GDI stuff in unmanaged sections Smile | :)
 
James
 
Simplicity Rules!
GeneralRe: Good work but..memberNish [BusterBoy]8 May '02 - 9:42 
For the saver??
 
Nish
 

The posting stats are now in PDF:-
http://www.busterboy.org/codeproject/
Feel free to make your comments.
Updated - May 04th, Saturday

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 4 Jul 2002
Article Copyright 2002 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid