Monday, August 20, 2007

Why you should learn the API before MFC

My opinion, although by no means the only one, is that you should use the right framework for the right job.

First of all a clarification on what the API and MFC are. API is a generic term meaning Application Programming Interface, however in the context of Windows programming, it means specifically the Windows API, which is the lowest level of interaction between applications and the windows operating system. Drivers of course have even lower levels, and different sets of function calls to work with, but for the vast majority of windows development this is not an issue. MFC is a Class Library, it's a bunch of C++ classes that have been written to reduce the amount of work it takes to do certain things with the API. It also introduces an (arguably) Object Oriented framework into the application that you can either take advantage of or ignore, which is what most beginners do since the framework isn't really aimed at writing MP3 players, IRC clients or games.

Every program, whether it is written with MFC, Delphi, Visual Basic, perl, or any other wacked out language or framework you can think of, is eventually built upon the API. In many cases this interaction is hidden, so you don't deal directly with the API, the runtime and support libraries do it for you. Some people ask, "MFC can do Blah Blah Blah, can the API?" The answer is that MFC can only do what the API can do, because it's built on top of it. However doing things yourself with the API may take considerably more code than using the pre-written MFC classes.

So what is the right framework? For starters, for people that are just learning to program, I strongly believe that you should work with the API untill you are comfortable with the way windows applications work and you understand all of the basic mechanics behind things like the message loop, GDI, controls, and maybe even multithreading and sockets. This way you will understand the fundamental building blocks of all windows applications, and can apply this common knowledge to MFC, Visual Basic, or whatever other framework you choose to work with later. It's also important because these other frameworks don't support everything that the API does, simply because it does a whole lot and they can't necessarily support all of the arcane little things that most people won't use. So when you finally do need to use them you need to add it yourself, you can't rely on the framework to do it for you and if you don't understand the API this could be quite the chore.

But isn't MFC easier? In a certain sense it's easier in that many common tasks are done for you, thus reducing the amount of code that you need to actually type. However, less code does not mean "easier" when you don't understand the code you DO need to write, or how all of the code that is there to support you actually works. Generally beginners who use the wizards to start there applications have no idea what most of the generated code does, and spend a great deal of time trying to figure out where to add things, or what changes to make to acheive a certain result. If you start your programs from scratch, either in the API or with MFC, then you know where everything is because you put it there, and you will only use features that you understand.

Another important factor is that most people that are learing the Win32 API for the first time don't already have a strong base in C++. To try and comprehend windows programming with MFC and learn C++ at the same time can be a monumental task. Although it's not impossible, it will take you considerably longer to become productive than if you already knew either C++ or the API.

So basically...

What it comes down to is that I think you should learn the API untill you feel comfortable with it, and then try out MFC. If it seems like it's making sense to you and saving you time, then by all means use it.

However, and this is important... if you work with MFC without understanding the API and then ask for help with something, and the answer you get is stated using the api (such as "Use the HDC provided in the WM_CTLCOLORSTATIC message") and you say "huh?" because you don't know how to translate an API subject into MFC on your own, then you are in trouble and people will get frustrated with you for not learning what you need to know before you try and use MFC.

I personally prefer to work with the API, it just suits me better, but if I were to write a database frontend, or a host for a set of ActiveX controls I would seriously consider using MFC, as it would eliminate a lot of code that I would need to reinvent otherwise.

Sunday, August 19, 2007

Windows GDI - Part 1 - Device Context & Usage..

Here we will try to answer some questions related to Device context & Windows GDI.

What is GDI ?
GDI means Graphic device interface.


How we are able to draw on devices ?
Applications can draw and print output on a variety of devices. The software that supports this device independence is contained in two dynamic-link libraries. The first, Gdi.dll, is referred to as the graphics device interface (GDI); the second is referred to as a device driver. The name of the second depends on the device where the application draws output. For example, if the application draws output in the client area of its window on a VGA display, this library is Vga.dll; if the application prints output on an Epson FX-80 printer, this library is Epson9.dll.

What is a DC?
A DC is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output.

Why DC is used ?

To do all the drawings on a window --like drawing a bitmap, drawing an icon etc. we need device context of the window. So we have to get the DC for that particular window before we do any such operations.


What are the different components of a DC? Explain in detail?

DC has mainly two components
1. Graphic objects
The following table will give info on various graphic objects...

2. Graphic Modes


What is the need of SetBkMode Function? Why SetBkMode(TRANSPARENT) is used?

Background mode is set to OPAQUE by default. So when text is drawn on a particular area, the back ground rectangle, which we specify for Draw Text() API, will be filled by background color. The background will have a default color & can be changed using SetBkColor (). SetBkMode function is used, when we need to draw a text on some background, without filling the bounding rectangle . There we need to change the BkMode to TRANSPARENT, by calling
SetBkMode(TRANSPARENT)..

Types of DC
-------------------
There are 4 types of DC

1. Display DC
2. Memory DC
3. PrintDC
4. Infomation DC

Here we will be concentrating on first two DC's..

Display DC is the most common DC, which we use in OnPaint function. OnPaint function is the message handler of WM_PAINT function. When we say CPaintDC dc(this); as the first statement of OnPaint function, we obtain Display DC & that is dc.. Inside the constructor of CPaintDC BeginPaint() is called, which actually returns the device context. After getting the dc, all the drawing functions available in CDC class can be used since CPaintDC is inherited from CDC.

Memory DC is a temporary place holder in memory. Instead of drawing directly into device we may choose to draw in memory and later draw it onto device. Here a portion of memory act as a virtual device.

How Memory DC is created ?
by calling the CreateCompatibleDC function.

How Memory DC stores the drawings ?

Memory DC stores the everything as a bitmap.
Before an application can begin drawing, it must select a bitmap with the appropriate width and height into the DC by calling the SelectObject function. To create a bitmap of the appropriate dimensions, use the CreateBitmap, CreateBitmapIndirect, or CreateCompatibleBitmap function.When an application passes the handle returned by CreateCompatibleDC to one of the drawing functions, the requested output does not appear on a device's drawing surface.

How final output is rendered onto device using MemoryDC ?

When an application passes the handle returned by CreateCompatibleDC to one of the drawing functions, the requested output does not appear on a device's drawing surface. Instead, the system stores the color information for the resultant line, curve, text, or region in the array of bits. The application can copy the image stored in memory back onto a drawing surface by calling the BitBlt function, identifying the memory DC as the source device context and a window or screen DC as the target device context

Give examples where Memory DC comes handy over Display DC?
Suppose we are drawing lot of things in OnPaint and we need to invalidate the screen often. In this case display DC will cause lot of flicker. So if we do all the drawing in Memory DC and render it at the end of OnPaint(), it will remove the flicker.

Best example is
BufferDC class which uses the concept.

What are the basic steps for any drawing?

First step is to get the DC for the particular window. Then we need to create the bitmap/pen/brush/palette as per our needs. Or we can load a bitmap/icon from resource file using LoadImage () function. Once this is done, we have a handle for the Graphic object. (Handle can be of type hBitmap, hIcon etc.) Using this handle we can draw the object. On the other hand we can use the handle to select the object also. Then we can draw the same onto device. After drawing we need to delete the handle. For Windows the following code illustrates how to draw the bitmap on to screen.

Desktop MFC (Windows)

CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here

// Do not call CWnd::OnPaint() for painting messages
// TODO: Add your message handler code here

CBitmap Bitmap; // Bitmap class to load the bitmap

CPoint pt; // start point of drawing
pt.x = pt.y = 10;

CSize size; // size of bitmap

size.cx = 320;
size.cy = 240;

// load the bitmap into application
Bitmap.LoadBitmap(MAKEINTRESOURCE(IDB_STARTUPSCREEN));

//Get the bitmap handle
HBITMAP bitmap = Bitmap.operator HBITMAP();

// draw the Bitmap..
dc.DrawState(pt,size,bitmap,DST_BITMAP);

//delete handle
BOOL retvalue = Bitmap.DeleteObject();


But for WinCE DrawState() API is not supported..So we need to copy the bitmap into memory dc ie, using SelectObject API() and the draw the same.

void DrawBitmap(CPaintDC *device context, int BitmapID,CRect* rect)
{


CDC memoryDC;
HANDLE hBitmap; // Handle
CBitmap bmpImage;
BITMAP bitmpImage;

devicecontext->SetBkMode(TRANSPARENT);

// Get compatible device context
memoryDC.CreateCompatibleDC(devicecontext);

//Load the bitmap
hBitmap = LoadImage(AfxGetInstanceHandle(),MAKEINTRESOURCE(BitmapID),
IMAGE_BITMAP,ZERO,ZERO,LR_DEFAULTCOLOR);

//Attach the bitmap
if(hBitmap)
{
bmpImage.Detach();
bmpImage.DeleteObject();
bmpImage.Attach(hBitmap);
}

// select the bitmap to DC
bmpImage.GetObject(sizeof(BITMAP),&bitmpImage);
CBitmap *pOldBmp = (CBitmap*)memoryDC.SelectObject(&bmpImage);

// Draw the bitmap on screen
devicecontext->StretchBlt(rect->left,rect->top,rect->Width(),rect->Height(),
&memoryDC, ZERO, ZERO, bitmpImage.bmWidth,
bitmpImage.bmHeight, SRCCOPY);

// Delete the bitmap attached to the object from memory by
// freeing all system storage associated with the object.

memoryDC.SelectObject(pOldBmp);
DeleteObject(bmpImage.Detach());
}

Similarly we can draw icon/ draw pattern brushes etc. which i will elaborate more in upcoming blogs. I will try to cover more details about bitmaps / brushes / pens etc. as separate blogs in Windows GDI Section.

Pls feel free to comment..