Source code for this article may be downloaded by clicking
here.
RAD development environments provide an easy
way to work in Windows. Proof of their effectiveness
is that many (if not most) programmers today
working in Windows have never dealt with
the CreateWindow() function, or crafted a
switch...case statement for a Windows messaging
loop.
Not that this is bad: One of the major benefits
of RAD tools is they they hide complexity so
that development goes faster. However, there
can be advantages in getting close to the
raw API level. Programs are smaller, since
the extra abstraction layer is missing. Likewise,
they are often faster.
It opens opportunities to work with legacy
code, which occasionally is written at this
low level. And finally, being familiar with
API-level Windows is good for a programmer,
since it is the stuff every Windows program
is ultimately made of.
VCL makes development a great deal easier,
and working at the API-only level
is much more complicated. Nevertheless, there
can be occasions when the advantages outweigh
the disadvantages. If you've weighed the
pros and cons, and decided you need a project
done solely at the API level, you've come
to the right place, because C++Builder lets
you do it, and gives you all the benefits
of its IDE as well -- including Codeguard!
ROLLING YOUR OWN
Doing API-only programming is ridiculously
simple with C++Builder.
First, create and save
a new project. Then go into the Project Manager
and remove the main form. What's left is
the cpp file named after the project (typically
Project1.cpp). Looking at it, you'll see
something like this:
#include <vcl.h>
#pragma hdrstop
USERES("Project1.res");
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR,int)
{
try
{
Application->Initialize();
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
Your whole program is now under 20 lines,
and of course it lacks anything visual. But the WinMain()
function is going to be the starting point
for our new program.
We need to remove the VCL from the program,
most notably the Application object. (We need
to keep the <vcl.h> reference in since that provides us with
the Windows API definitions and helps with
managing multiple files in the Project Manager.)
We remove the VCL by replacing the WinMain code:
int WINAPI WinMain( HINSTANCE hInstance, // handle - curr. instance
HINSTANCE hPrevInstance, // handle - prev. instance
LPSTR lpCmdLine, // pointer to command line
int nCmdShow ) // show state of window
{
g_hInstance=hInstance; // save instance for button creation later
MSG msg ;
WNDCLASS wndclass; // set up window
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hIcon = LoadIcon(hInstance,TEXT("PROGRAM_ICON"));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) COLOR_WINDOW;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WINDOW_NAME;
RegisterClass (&wndclass) ;
// now create main window
HWND hWnd = CreateWindow ( WINDOW_NAME, WINDOW_CAPTION,
WS_POPUPWINDOW | WS_CAPTION
| WS_SYSMENU | WS_MINIMIZEBOX,
g_windRect.left,
g_windRect.top,
g_windRect.right-g_windRect.left,
g_windRect.bottom-g_windRect.top,
NULL,NULL, hInstance, NULL);
ShowWindow(hWnd,SW_SHOW); // display window and process messages
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
We now also have to include a Windows message
procedure (specified in wndclass.lpfnWndProc as WndProc), so we add that as well:
LRESULT CALLBACK _export WndProc(HWND hWnd,
UINT message,
UINT wParam,
LONG lParam)
{
switch (message)
{
case WM_CREATE: // init - create quit button along window bottom
{
RECT rect;
GetClientRect(hWnd,&rect);
CreateWindow("button","Goodbye,World",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
5,
rect.bottom/2+5,
rect.right-10,
rect.bottom/2-10,
hWnd,0,g_hInstance,NULL);
}
return 0L;
case WM_PAINT: // center a message on top half of window
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd,&ps);
RECT rect;
GetClientRect(hWnd,&rect);
SetTextAlign(hDC,TA_CENTER|TA_BASELINE); // make text center itself
char *text="Hello World";
rect.bottom/=2; // set to top half of client window
ExtTextOut(hDC, // output text
rect.right/2,
rect.bottom/2,
ETO_OPAQUE,
&rect,
text,
strlen(text),
NULL);
EndPaint(hWnd,&ps);
}
return 0L;
case WM_COMMAND: // handle button press by quitting
SendMessage(hWnd,WM_CLOSE,0,0);
return 0L;
case WM_CLOSE:
DestroyWindow(hWnd); // perform wm_destroy
return 0L;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0L;
}
return DefWindowProc (hWnd, message, wParam,lParam);
}
This, then, is your typical "hello world" program
in C++Builder -- about 100 lines. It will compile
to just over 100K. Compare that to the equivalent
in VCL, which weighs in at more than 300K, and you realize the
advantages for certain programs. (Of course,
if you compile without statically linking
the libraries or VCL the code is only about
20K, but then the libraries will need to
be shipped, increasing the total size.)
However, the number of lines now needed highlights
the complexity that the VCL classes usually hide. As
always, there is a tradeoff in coding.
MESHING C++BUILDER WITH LOW-LEVEL API CODE
The code is actually very straightforward -- WinMain
creates an ordinary window, and the
WM_CREATE case in the message handler creates
a button across the bottom half. In addition,
the WM_PAINT case draws across the top half
of the window with the traditional "Hello
World" message. This simple example includes
the basics of API-level windowing, and can
serve as the basis for your own projects.
One detail shown in the downloadable source
code that is not obvious here is the icon
setting for wndclass.hIcon:
LoadIcon(hInstance,TEXT("PROGRAM_ICON"));
This icon refers to a resource in a separate
file. C++Builder still manages the project,
even at this level, and you can add resource
files and access them. In the project, "extra.rc"
is the resource for the icon, but it could include
other items as well. Of course, as a program
grows, you are not limited to a single cpp
file, the same as in any other C++Builder project.
A HANDY WAY TO DEVELOP
Lest you think this is all theoretical, it
isn't; I currently both sell and give away
software developed at the API-only level.
While it is quite a bit harder to write than
VCL-based code, it is also much smaller, which is especially handy
for distribution across the Internet. The
C++Builder IDE gives me a straightforward
compile environment, a familiar editor, and
with version 5 I have the invaluable aid
of CodeGuard for memory-leak checking. With
this flexibility, I've been able to retire
my Borland C++ 4.5 compiler (except for the occasional
DOS work) and work exclusively in C++Builder,
with enhanced productivity.
So if you need a small, light program, or
just want to try coding at the bare metal
level, try API-only coding. With C++Builder,
it's as easy as it gets.
Connect with Us