Using the Win32 API in a C++ DLL

At this point I'm assuming a little more knowledge of basic C++ syntax however I'll try to keep things as simple as possible so even without knowledge of C++ it can still be followed, a knowledge of the GDI API would be very useful though.
Ok, what we're going to do is write a routine to fill a device context will a checkered pattern, it may seem like a huge jump from the previous simple methods but it's not too bad and I'll try and keep it simple.
First up, we'll need to write the shell of the function, which will need to take a DC handle, the size of the checks to draw, the width and height to fill and the colour values. The first thing that's different in C++ from VB is that we've got lots more variables types to play about with which make code more readable by clearly defining exactly what a parameter should contain. In VB we pass all the parameters as Long's however in C++ we can use other types which more accurate describe the parameter but essentially map to the same thing. The first of these new parameters we'll use is one to hold a handle to a device context, HDC. The second is to describe a colour, COLORRREF. Both of these simply map to a 32-bit Long integer however they just make it a bit more obvious what we're expecting them to be.
With this new information we can now write the function shell in the C++ DLL:

int _stdcall CheckerDC(HDC inDC, int inSize,
int inWidth, int inHeight,
COLORREF inColA,
COLORREF inColB) {
}

Again here we're returning a long representing the functions outcome.
Now we can start writing the routine, first off let's do some validation though. You could check to see if the DC handle you've been passed it a valid one or not with the GetObjectType() API call however I'll skip that here (It can always be added later).
The first check is to make sure that the checker size makes sense so we'll make sure it isn't below 1 (A check size of 0 could cause stack overflow problems with the looping structure).

if (inSize < 1) return 0; // Invalid size

Remember again that the condition has to go within parenthesis here and the "return" keyword actually quits the routine.
Now we'll check to see if the colours are the same, if so then we can perform a simple fill rather than the more exhaustive checker code:

if (inColA == inColB) { // Colours are the same – Just fill
}

You'll see here that I've added curly braces after the "if" condition in the same way as round a function, this is because we're going to put more than one line within the block. VB does the same thing with If statements:

If (Condition) Then Call DoSomething()

Or if multiple things need doing:

If (Condition) Then
Call DoSomething()
Call DoSomethingElse()
' etc...
End If

In C++ though this premise extends to most block structure such as loops however they must remain round method blocks.
In the same way was we use the HDC type to hold a handle to a device context, we can use the HBRUSH type to hold a handle to a brush object, which we'll need to declare now. Another nice thing here is that we can assign the value of the variable when it's declared so let's create the brush to fill with now:

HBRUSH FillBrush = CreateSolidBrush(inColA) // Create brush object

Now we'll need an API "RECT" type to hold the area to draw in. Now, unlike VB we don't need to add definitions to the API declarations to use them, instead C++ uses what's known as header files which contain all the declarations for us. By default our DLL project was created with some of these headers already included including all the common GDI API's, however if you require use of a specific API then you can find the header that it's contained within by searching on the MSDN for the Method/Type name and looking for the header requirement at the bottom of the page. This is the requirement for the FillRect() API which we'll be using in a second:

Requirements
Windows NT/2000/XP:
Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.

You'll see there that the header we need is "Windows.h" however if you then look at the top of the .cpp file you've been writing your code in you'll see the line:

#include "stdafx.h"

The "#include" means insert everything from the following file here so in this case it's going to insert the contents of the file "stdafx.h" at the top of the project. If you have a look in file view for the project and open up stdafx.h then you'll see amongst other things the lines:

// Windows Header Files:
#include <windows.h>

So we've already got the header file declared and don't need to re-include it.
Ok, back to the code. We'll need to declare the RECT variable and we can do the same trick of filling it in the same line by including curly braces round the entry list:

RECT FillArea = {0, 0, inWidth, inHeight}; // Declare fill area

You must get the parameters in the correct order, you can find the ordering of the parameters either by looking in the MSDN or by opposite clicking on the "RECT" keyword and going to it's definition where you'll find:

typedef struct tagRECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

It may look a bit nasty but you can pretty much ignore the last line, which just set's up some aliases for the type depending on how it's being used. The important bit is the ordering of the elements within the type (In case you were unsure, this is the same as a UDT in VB) in this case left, top, right, bottom so this is how we must pass the elements to C++. You can also manually fill the type as follows if you choose as we would in VB:

RECT FillArea;

FillArea.left = 0;
FillArea.top = 0;
FillArea.right = inWidth;
FillArea.bottom = inHeight;

We've already got the API declared so let's go ahead and call the FillRect() API to fill this DC with the selected colour. One thing to note here is that the second parameter of the call is actually a pointer to a rectangle structure so we must pass a pointer to our RECT structure rather than the structure itself, we do this by prefixing the variable name with an ampersand character:

FillRect(inDC, &FillArea, FillBrush); // Fill the DC

We must now kill the brush object to prevent memory leaks which is done in exactly the same was as in VB:

DeleteObject(FillBrush); // Clean up

Finally we'll not need to bother with the rest of the routine so just return 1 now to let the user know it was a success:

return 1; // Indicate success

Let's have a look at the code so far:

if (inSize < 1) return 0; // Invalid size
if (inColA == inColB) { // Colours are the same - Just fill
HBRUSH FillBrush = CreateSolidBrush(inColA); // Create brush object
RECT FillArea = {0, 0, inWidth, inHeight}; // Declare fill area
FillRect(inDC, &FillArea, FillBrush); // Fill the DC
DeleteObject(FillBrush); // Clean up
return 1; // Indicate success
}

So far so good, indeed you can actually test the routine now if you wish, just skip to the end of the chapter and grab the declaration and sample code for VB then call the function with both colours the same and you should get the picture box being filled with the solid colour.
Now we need to deal with the rest of the routine that will be run as long as both of the first tests were passed.
First up we'll need two API brush objects to draw with rather than re-creating them every time so let's declare them now:

HBRUSH BrushA = CreateSolidBrush(inColA); // Create GDI brush objects
HBRUSH BrushB = CreateSolidBrush(inColB);

We must remember to kill these when we're done tough, I quite often add the clean-up code as soon as I've created the objects so as to not forget but if you're following this tutorial then it shouldn't be a problem.

Now we'll need a couple of variables to hold which brush we started the line on and which brush we should be drawing this checker in. There may be other ways of accomplishing this but this is how I've done this in the past before so I'll stick with it here too:

int FirstBrush = 0, ThisBrush = 0; // Which brush to use

Finally we'll declare a RECT structure that we'll just re-use to set the area to fill:

RECT FillArea; // The area to fill for this checker

Ok, let's set up the base loop structure now that will handle the drawing. The outer loop will iterate horizontally and deal with the first brush for the line, while the inner loop will iterate vertically and deal with which brush to draw each checker with.
As such the loop structure will look like this:

for (int LoopX = 0; LoopX < inWidth; LoopX += inSize) {
ThisBrush = FirstBrush;

for (int LoopY = 0; LoopY < inHeight; LoopY += inSize) {
// Draw check and swap brushes
}

// Switch first brush
FirstBrush = (FirstBrush + 1) % 2;
}

Nothing too new apart from the loops have a "+=" in which simply means add the following value to this value, it's shorthand for addition to the same variable i.e:

MyVar = MyVar + 5;

Can also be written as:

MyVar += 5;

It's also quicker this way since the compiler is optimised to deal with these faster than in the first statement.
The last but one line also has some odd looking syntax, the "%" symbol in C++ is the operator for modulus division and is the same as the VB "Mod" statement. You could instead write:

if (FirstBrush == 1) FirstBrush = 0; else FirstBrush = 1;

However it's a bit clunky so I've stuck to the original method.

The modulus version works as follows:

0 Mod 2 = 0
1 Mod 2 = 1
2 Mod 2 = 0

So at first we start off at 0, then add one and modulus divide by 2 to get 1, the next iteration adds 1 again to get 2 then modulus divides by 2 to get 0 so the number is swapped back and fourth.

At this point, each time the inner loop runs, the ThisBrush variable is going to store the opposite brush than the first one used on the last line (Or 0 if it's the first iteration of the loop) so we'll just have to deal with swapping the brushes on this line only and not what happens on the next.
Before we do that thought we'll need to draw this checker in the current colour so assign the fill area some dimensions:

// Set fill area
FillArea.left = LoopX;
FillArea.top = LoopY;
FillArea.right = LoopX + inSize;
FillArea.bottom = LoopY + inSize;

This simply set's the top left of the fill area to the current (X,Y) coordinate and sets the bottom left position to this coordinate offset by the size both horizontally and vertically. Now we must fill this checker with the current brush colour so we'll need to make a call to FillRect():

// Fill checker
FillRect(inDC, &FillArea, (ThisBrush == 1)? BrushB: BrushA);

More strange syntax here again, this time there's a question mark in there too which means the same as an Iif() (Immediate-if) statement in VB. The syntax is:

Condition? TruePart: FalsePart

However I usually put the condition in parenthesis to set it apart from the rest of the statement and adhere to C++'s normal if syntax.
This line would be the same as the following line in VB:

Call FillRect(inDC, FillArea, Iif(ThisBrush = 1, BrushB, BrushA))

So we've filled this checker now, all we need to do to finish this inner loop off is to swap the checker colour:

ThisBrush = (ThisBrush + 1) % 2; // Switch Brushes

This uses the same syntax as in the outer loop when swapping the first brush for the line.
Now to finish the function off we must clean up the two brushes after the loops have finished doing their stuff:

// Clean up
DeleteObject(BrushA);
DeleteObject(BrushB);

And return a value indicating success:

// Return success
return 1;

And that's all there is to it, after 5 and a half pages we have a finished checker routine, so let's see if it works in VB. First off add the function name to the function definition list so VB will be able to see it, then head over to VB and we'll add the new function header.

The function header in C++ looks as follows:

int _stdcall CheckerDC(HDC inDC, int inSize,
int inWidth, int inHeight,
COLORREF inColA,
COLORREF inColB)

So let's work our way through the header and write the corresponding VB version. "int" means it returns a Long as we know it ends in "As Long" and the function name is "CheckerDC" so we know it starts off with "Private Declare Function CheckerDC"
The library name comes next (With the two different paths for development and release versions of the DLL) then comes the parameter list. You can see that none of the parameters have a multiplication sign next to them so none are pointers and will all be passed by-value. They also conveniently all map to Longs so the rest of the declaration is simply a matter of swapping around the syntax:

Private Declare Function CheckerDC Lib "[DLLName].dll" ( _
ByVal inDC As Long, ByVal inSize As Long, _
ByVal inWidth As Long, ByVal inHeight As Long, _
ByVal inColA As Long, ByVal inColB As Long) As Long

To demonstrate the routine at work we can simply add a picture box to the form then add this code:

Private Sub Form_Load() ' Persist drawing
Picture1.AutoRedraw = True
Picture1.ScaleMode = vbPixels
End Sub

Private Sub
Form_Resize()
Call Picture1.Move(0, 0, Form1.Width - 120, Form1.Height - 400)
Call CheckerDC(Picture1.hDC, 12, Picture1.ScaleWidth, _
Picture1.ScaleHeight, &H707070, &H909090)
End Sub

When you run the application, you should have a PhotoShop-style transparency grid being drawn for you, if not then go back and check your code to make sure everything's working, if you get an error then run through all the checks in the previous chapters which should iron out the problems and you can also download my code and compare that to your own.

I've included a couple of appendices at the end as a quick reference to some things you may need, all of this and a lot more is in the help files though

Finished code for chapter 5:
Form DLL

Back to chapter 4
Back to the index