Introduction

In reading the forums and answering the questions, I have found that many beginners
would like to learn the fundamental concepts of painting in WIN32 programs.
Developing applications for windows can be challenging and frustrating. it can also
be rewarding if you know some of the basic techniques that are required to take advantage
of the WIN32 operating system. This tutorial is for beginners, and will cover
the basic techniques and knowledge required to get started with painting in WIN32.

The code and the concepts are all at the SDK level, as most of the other techniques
that are used to paint to windows are based on the SDK. The WIN32 SDK is used
inside of the MFC and ATL/WTL objects that represent those frameworks. I believe the more
you know about the base technology, the SDK, the more you can take advantage of
the frameworks that are written to encapsulate the technology. In order to help developers
that are developing in MFC and WTL, the corresponding classes from those frameworks will
be explained in the appropriate sections.

This tutorial is the first in a series of five. This one is for beginners, the
next three will cover more advanced topics at the intermediate level, and the final
tutorial will cover WIN32 paint internals at the advanced level.

Device Context

At the simplest level, the device context (DC) is a surface that can be painted on.
However, the DC is also the operating system resource that coordinates
the brushes, pens and fonts that are used to render the images on the display. It is also
the layer of abstraction that the Windows Graphics Device Interface (GDI) uses to
abstract the details of a display device from a developer. This means
that it is possible to paint to the screen, a printer, or even a bitmap in memory with the
same code, initialized with a different DC that is created for a particular purpose.
This makes coding very simple compared to the development that is required to program
directly to a particular video card, or printer driver.

The key to creating solid and efficient graphics in Windows, is to know how to
create and utilize the DC that you want for a particular purpose. There are many
flavors of DCs, here is a short description of each:

Client DC

A client DC is associated with a particular window, and
will give the developer access to the client area of the target window.
This is the type of DC that is used most often by application programmers, and is
the easiest to handle. This is the type of DC that should be used when
the WM_PAINT message is handled. This is also the only DC that will be
explained with any amount of detail.

Window DC

A window DC is associated with a particular window, and
allows the developer to draw on any part of the target window, including the
borders and the caption. This is the type of DC that is sent with WM_NCPAINT
message.

Memory DC

A Memory DC is a DC that exists only in memory, and is not
associated with any window. This is the only type of DC that
can hold a user defined bitmap (HBITMAP). Memory DC's are very useful for
caching images, and for use with backbuffers on complicated displays.

General Device DC

For lack of a better name, this type of DC covers
all of the other devices that it is possible to get a DC from. For instance, a
printer or a plotter, the entire display monitor, or even custom device that
a cutting-edge technology company may invent that does not even exist yet. The
fact is, a DC can be created for any device that supports the required API
functions defined by Microsoft.

This guide will only demonstrate the Client DC in order to get the user started with basic
Windows graphics development. The tutorials later in this series will cover the other
types of DC.

Obtaining a Client Device Context

While working with the WIN32 Paint API, you will always obtain a handle to
a device context (HDC). Any of the types of DCs that were described earlier can
be stored in a HDC. This guide will only describe how to obtain a client DC,
as the other DCs are used for more advanced purposes.

The frameworks represent their DCs with a class. The base class for the DC is CDC.
A CDC encapsulates the HDC itself. All of the functions that can be called
for a HDC are encapsulated as member functions. There are also a few classes derived from the
CDC that will allow special DCs to be created and maintained. The
CPaintDC and the CClientDC will be explained later in this section.

BeginPaint

BeginPaint is the most common way to create a DC. However, this function should
only be called in your WM_PAINT message handler of your window. This is because BeginPaint
validates the invalid region of your window. WM_PAINT messages are generated whenever an
invalid region is created on your window. If BeginPaint is called outside of
the WM_PAINT handler, any region that was previously invalid will be validated, and no
WM_PAINT message will be generated for your window. This could cause major side-effects in your
control, especially if someone else would like to subclass your control in the future.

It is just as important to use BeginPaint inside of the WM_PAINT handler as it is
to not use it outside of WM_PAINT. This is because inside of the BeginPaint function
call, Windows may generate a WM_ERASEBKGND message and a WM_NCPAINT message if it is necessary. If you
do not call BeginPaint inside of your WM_PAINT handler, your windows borders may not
be updated properly.

In order to get a handle to a DC with BeginPaint, you will need a handle to the
target window, and a PAINTSTRUCT structure. PAINTSTRUCT holds information
about the current painting session when you call BeginPaint, that is unimportant at
this level. BeginPaint also returns a handle to the DC that it creates, and this
is the value that we are interested in. Here is an example of how to call BeginPaint.

// assuming that hWnd is the handle to the window for which we want the DC.
PAINTSTRUCT ps;
HDC hdc;
hdc = ::BeginPaint(hWnd, &ps);

The function that needs to be used in order to free a HDC created with BeginPaint
is EndPaint. It is important to call EndPaint rather than one of the
other DC destroyers, because BeginPaint calls HideCursor in order
to prevent the cursor from being painted on during your paint operations, and EndPaint
calls ShowCursor to make it visible again. If EndPaint is not called,
after a BeginPaint then you may experience some weird anomalies with the cursor.

Here is an example of EndPaint:

// Call EndPaint with the same hWnd and PAINTSTRUCT that was used in
// the call to BeginPaint.
::EndPaint(hWnd, &ps);

The call to BeginPaint and EndPaint are encapsulated in the class
CPaintDC in MFC and WTL. By simply creating an instance of this DC on the stack,
the DC will automatically be created and destroyed for the developer. Here is the code for
the constructor and destructor in the MFC version of this class:

GetDC / GetDCEx

GetDC is the next most common way to create a DC. GetDC
will obtain a device context to the target windows client area. GetDCEx
will do the same thing, however it will allow you to specify a default clipping region.
GetDCEx will be ignored until the next guide.

GetDC has many uses outside of the WM_PAINT handler. Use
GetDC when a graphical effect needs to be created, that may not be part
of the permanent data of the target window. For instance, use GetDC to create the DC
for the rubber-banding effect on drawing tools, and when selecting multiple objects
in a window.

Here is an example of how to create and destroy a DC with a call to GetDC.

// Assuming that hWnd is the handle to the window for which we want the DC.
HDC hdc;
hdc = GetDC(hWnd);
// Perform painting operations here.
...
// Release the DC when you are finished. If this function succeeds it will return 1,
// otherwise it will return 0 if it fails.
::ReleaseDC(hWnd, hdc);

The call to GetDC and ReleaseDC are encapsulated in the class
CClientDC in MFC and WTL. By simply creating an instance of this DC on the stack,
the DC will automatically be created and destroyed for the developer. Here is the code for
the constructor and destructor in the MFC version of this class:

Using a Device Context

Using a DC is very simple, and it can be quite complicated, it all depends on what painting
effect is to be accomplished. This guide will simply stay with the default pen and brush
that are selected in the DC when it is first created. Here is an example of how to call a number of
different GDI functions with the DC that we have created.

//C: Create the DC on the stack. This will allow the class to be destroyed when
// the stack frame disappears.
//C: WARNING: Only use this DC in your OnPaint handler for the WM_PAINT message.
CPaintDC dc;
//C: Use the DC as you would like.
dc.Rectangle(10, 10, 150, 200);
...
//C: No need to do any thing else to manage the DC, it will destroy itself.

Here is a short example demonstrating how to use the CClientDC:

//C: Create the DC on the stack. This will allow the class to be destroyed when
// the stack frame disappears.
CClientDC dc;
//C: Use the DC as you would like.
dc.Rectangle(10, 10, 150, 200);
...
//C: No need to do any thing else to manage the DC, it will destroy itself.

Getting Started

The short demo program that is provided will display an array of shapes that has been created
by the user. The user can create a shape by clicking and dragging the mouse, the same way that
objects are selected in windows explorer. The two methods for creating a client DC are demonstrated.

BeginPaint: This function is used to paint the shapes that the user has created.

GetDC: This function is used to create the rubberbanding effect that allows the user to drag and create the shapes.

The demo program is very simple in structure. It forsakes coding style and elegance for simplicity
and clarity. All of the state that is required for this program is stored in global variables. There
is a maximum of 5 shapes that can be created because they are stored in a statically allocated array.
If more than five shapes are created, then the oldest existing shape will be replaced with the new shape.

Here is the OnPaint handler that was created to handle the WM_PAINT message:

Notice the very simple paint structure encapsulated between the calls to BeginPaint and
EndPaint. More calls could be made between the BeginPaint bracket. The same
principle would still apply, all painting for the main window should be done here.

The rubber banding effect is a little bit more complicated. This effect is created by modifying two
of the state variables in the DC. The first state that is changed is that the current drawing mode is
changed from a plain copy to a destination NOT pen, or R2_NOT. This pen will allow the first
drawing instance of the pen to change all of the bits from display, making a line visible. By simply
Drawing the exact same line a second time, the line will disappear.

The second change in DC state, is to select an empty brush color into the DC, so that when the shape
is dragged, it does not paint the center of the shape. These two tricks are not the important point that
should be noticed from this code. The important point to notice is that the DC was
received from
a call to GetDC and ended with a call to ReleaseDC. Same as the drawing code,
any operations are contained in this DC creation bracket, and the window painting proceeds in between.

One final point that should be explained about the demo program is the call that
makes the rubber band work. This code is inside of the WM_MOUSEMOVE
message handler. The handler will first check if rubberbanding is currently
activated. If so it will first draw the current rectangle, effectively erasing it from
the screen, it will update the current coordinates, then it will draw the new coordinates
effectively updating the position of the rectangle. Here is the code:

Conclusion

Once again, this guide is to get people started down the path of painting in WIN32.
This article was written completely for the WIN32 SDK. I fully believe that MFC and
ATL are cleaner ways to develop code for Windows, but I also believe that if a developer
can get a firm grasp of the basic concepts at the SDK level, they will be able to flourish
as a framework developer, because you will be able to determine what is happening
behind the scenes, especially when problems arise.

The next article I post will be at an intermediate level with more information.
The next article will describe the other ways to create DCs. There will also be
information on the different fields of the PAINTSTRUCT structure. Finally, the
update region will be explained with a great amount of detail.

Over the years I have learned to value maintainable design solutions first. This has allowed me to adapt my projects to meet the challenges that inevitably appear during development, including:
* My own misjudgments
* Incomplete requirements
* Feature creep
* Poor decisions for which I have no control

I am a Mentor and frequent contributor to CodeProject.com with tutorial articles that teach others about the inner workings of the Windows APIs.

I am the creator of an open source project on GitHub called Network Alchemy[^], which is a set of Network APIs that are focused on helping developers write robust network communication software.

I maintain my own repository and blog at CodeOfTheDamned.com/[^], because code maintenance does not have to be a living hell.

Then for fun I will tinker with my ray-tracer when ever I upgrade my hardware to see what it is capable of doing.

Comments and Discussions

Having read your tutorial I do understand all of this, but I can't get a rectangle to draw until I release the mouse, and then it disappears immediately. Using GDI+ I find that I can draw rectangles without any problems, it's just unfortunate that there's no simple rubber-banding technique like yours that uses GDI+.

Recently I have thought about publishing a few more articles on CodeProject. I revisited my articles on Win32 paint, and I am still surprised how many people find them useful. I also read in one of the articles I was going to make it a 5 part series (I am not sure what I was thinking). There is plenty of material to cover still, such as MemoryDCs, Printing, Path usage, interacting with the bitmaps in DIB format, GDI+.

However, I would rather put my time into something that people would find useful, interesting, and relavent today, almost 10 years after the original article.

If you have interest in seeing more, leave a message here. If there seems like enough interest I will continue with this topic rather than finding something new.

Regards,
Paul

(BTW, the message posted below this one got me thinking about creating the advanced and more difficult portions of Windows API Graphics)

Any Idea how to get the tray clock background and tray clock text color? Which DC I need to get here? Will GetBkColor or GetTextColor API help to serve this? I have been trying this from couple of days and I am not able to do this, You inputs will be useful.
Thanks in advance!!

In this case your best bet will be to start with the Spy++ utility that comes with Visual Studio.

When you open Spy++, click Search | Find Window or Alt+F3. This will display a dialog and one of the controls will be some cross-hairs. You can drag that to any window that is visible to find the basic info regarding system info. When you click OK it will locate that window in the system window tree list.

The thing to take note of, is (this example is on Vista) that the tray windows class name is "TrayClockWClass".

With that info you can use the FindWindow API to get a handle to the clock tray, and then you can query any of the functions that you would like regarding that window.

There may actually be some defined windows constants that dictate what the color of the tray will be based on the system settings. I do not know if they exist for the this clock in the tray, and I wouldn't know what constant to suggest to look up. If there is, the API that you would use to get the color is GetSysColor.

I'm writing code to capture images off an openGL window. Is it possible to obtain a DC and not release it till I'm finished capturing ??? What happens if I don't release the DC till the end of the capturing process? What is the overhead of getting/releasing the DC each time ?

A nice article. But could you expain some more on differences between using the (BeginPaint, EndPaint) and (GetDC, ReleaseDC) pairs?

I replaced the (BeginPaint, EndPaint) pair in the OnPaint method with the (GetDC and ReleasesDC) pair. And found the overlapped shape boundary keeps refresshing. Why does it happen to (GetDC and ReleaseDC) pair but not to the (BeginPaint and EndPaint) pair?

The main difference between the two, is that BeginPaint will validate the current invalid region for your window. This will end the cycle that keeps asking that region to be painted.

If you only use GetDC, but you dont call ValidateRect or ValidateRgn before you return of WM_PAINT, you will immediately get another call to WM_PAINT to paint that exact region. The system doesnt know that it has been updated yet.

There are a few other nuances that are minor. By calling BeginPaint, the window will automatically perform clipping with something called the clipping region. It is basically a region that represents only the visible portion of the invalid region for your window.

check out this other article I wrote to get more info about the different ways to get a DC.

If you only use GetDC, but you dont call ValidateRect or ValidateRgn before you return of WM_PAINT, you will immediately get another call to WM_PAINT to paint that exact region. The system doesnt know that it has been updated yet.

Thank you for your quick reply and valueable information you provided. With your comments, I have added 3 lines of codes to ValidateRect the Client Rect of the application.

That seems strange to me. In one of my own MFC dialog-based application, we have graphical drawing program, in which this->InvalideRect(MyCRect, false) is used to erase the unwanted strokes drawn previously. This works fine in the primary monitor. However, when I move the same application to the secondary monitor, I found one problem, i.e. the strokes previously drawn can no longer be erased. The strokes are drawn using the GetDC, ReleaseDC pairs in the OnMouseMove(UINT nFlags, CPoint point) function.

Do you have any idea of what could go wrong with that application? Note, in that applicaiton, ValidateRect is never used to validate any part of the rect. But the symptoms look similar.

I am not sure what your problems could be with the little code that you have shown.

In the call to InvalidateRect, the BOOL at the end of the function indicates whether you want to erase the background before a call to WM_PAINT is made. But I dont think that would explain why it works on one monitor and not another.

How are you depending on your shapes from being cleared? Are you using the WM_ERASEBKGND message or some other mechanism.

You may want to post your question on the general forum as well, then a lot more people can see and offer suggestios as well.

Build a man a fire, and he will be warm for a dayLight a man on fire, and he will be warm for the rest of his life!

The way the system knows that a portion of a window needs to be updated is that it flags a particular region for updates. This is called the Invalid region.

After you are done painting a window the way you would like it to appear, updating, then you need to validate that region. You are basically removing the region that you updated from the invalid region that is flagged for update.

When you validate a region, you can validate all, or only a portion of that window.

When windows reaches a point in execution that tells it that it should repaint your window, it checks the invalid regions for your window. If there are any invalid areas, it will send your window a WM_PAINT message. If you have validated all of the invalid areas before it reaches that point, there will be nothing new to paint, and Windows will not send you a WM_PAINT message.

1) UpdateWindow is basically a wrapper around a super function called RedrawWindow. RedrawWindow has a number of flags that you can tell the system that your window needs to be updated. UpdateWindow probably looks like this inside:

RedrawWindow also has other flags that allow you to validate and invalidate the windows update region. In fact, ValidateRect, and InvalidateRect are implemented with the RedrawWindow function as well.

The point is, this function is the main interface to change the update region for a window.

2) Correct as far as you will continue to get WM_PAINT messages forever. WM_PAINT is a low priority message. This message is actually generated when you call GetMessage or PeekMessage. When those functions that handle the message queue clear the queue of all other messages, it will then check the update region for the windows that are associated with the current thread, and return WM_PAINT messages for those windows.

If your message queue never gets empty, you will not receive a WM_PAINT message. That is one reason why UpdateWindow exists, so that during long processing operations you can FORCE a WM_PAINT message to be sent to your window so it will be properly updated.

your meaning is that the RedrawWindow() send (sendmessage means that the function would not return untill the message is handled) the WM_PAINT message to force updata window,and before we call UpdateWindow() , we have invalidate the client area ,so the message can be handled. is that so?

Paul Watt wrote:

Correct as far as you will continue to get WM_PAINT messages forever

Does it mean that the WM_PAINT message exists forever ,but it can not be handled because of its low priority.

last if the window has know that it should update the window but there are not invalidete region ,the WM_PAINT message also can not be handled. That is why we use the function of InvalidateRect().

your meaning is that the RedrawWindow() send (sendmessage means that the function would not return untill the message is handled) the WM_PAINT message to force updata window,and before we call UpdateWindow() , we have invalidate the client area ,so the message can be handled. is that so?

Yes correct. You actually indicated something that I forgot to mention (I got caught up in my explanation of RedrawWindow). If you call UpdateWindow, without there being any invalid regions on the window, nothing will happen. Windows will attempt to send the WM_PAINT message, and it will see that there is nothing to paint, so it wont send it.

Even if it did send it, and in your handler you called BeginPaint, the DC that is returned will be completely clipped. Because the DC that it returns only allows you to paint on the invalid region.

wzh19831221 wrote:

Does it mean that the WM_PAINT message exists forever ,but it can not be handled because of its low priority.

Well the WM_PAINT message does not exist forever, because it is really only an event that gets triggered when the message loop is emptied. It is not a traditionally posted windows message.

However, you are correct in the fact that the invalid region will exist forever, because the paint messages are low-priority. For you knowledge, the WM_TIMER message works much like WM_PAINT, it is a low-priority message as well.

wzh19831221 wrote:

last if the window has know that it should update the window but there are not invalidete region ,the WM_PAINT message also can not be handled. That is why we use the function of InvalidateRect().

Correct. I do not believe that a WM_PAINT message will even be generated if you do not have an invalid region for your window. Also refer to my comment above, even if WM_PAINT does get sent, nothing really will happen because of BeginPaint.

Therefore, InvalidateRect is the programmers way of flagging a region to be updated.

thanks very much!
you help me much!
I have a small problem to consult you
I want to see the context of the UpdateWindow() ,I have used some tools such as brower tool and msdn ,but can not get the information i want to get
Can you give me some information1

If you only use GetDC, but you dont call ValidateRect or ValidateRgn before you return of WM_PAINT, you will immediately get another call to WM_PAINT to paint that exact region. The system doesnt know that it has been updated yet.

I have refered to some informations and I have know that the Invalidate function is to add a region to the update window ;
because we donnt validate the invalide region ,the systerm still call the WM_PAINT to region
so the phenomenon of refreshing occurs.