==============================================================================
C-Scene Issue #2
Ahhh Windows programming.
Robert Bresner, aka wawb
==============================================================================
Ahhh Windows programming. It had to come up sooner or later, I'm just
sorry it's me that was called upon for the task. Sure, WinAPI is WinAPI and
looks pretty much the same regardless to what language or compiler you're
using (e.g. I learned the LineDDA routines by stealing some Delphi code), but
this is CScene, so obviously, C WinAPI is the best. Here's some advice for you,
in case you should some day be called upon to write an article about
programming: Don't use a word processor that spell/grammer checks as you type.
Word97 is choking here.
Right, down to business. This article is about loading resources from an .rc
file and using them in your program. So, if you don't understand the basics of
an MS Windows program - the message loop, for example - then you should pretty
much not understand this article. Get thee to a bookstore and buy one of the
books mentioned at the bottom of this article. Also, if you don't have at least
a casual understanding of C programming, then this stuff may all be out of
your league. Get K&R2. Learn. Books are your friends. Just can't say that
enough, sometimes, you know?
Currently, I use Microsoft Visual C++ 5.0. Paid for and everything. MSVC has a
very nice resource editor. That is, it has a nice little program that lets
you draw bitmaps, create menus, create dialogs, and other various resources.
Here's a list of common resources:
Accelerators
Bitmaps
Cursors
Dialog
Icon
Menu
String Table
Toolbar
Version
At least, those are the resources in the MS Resource Editor. Now, I have a
small program in which I've imported some custom resources, like "AVI", "WAV",
and "MAZE". Here's a small portion of my .rc file:
IDI_ICON ICON DISCARDABLE "icon1.ico"
WAV_OMG WAVE DISCARDABLE "omg.wav"
AVI_LOGO AVI DISCARDABLE "logo.avi"
BMID_LOGO BITMAP DISCARDABLE "logo.bmp"
BMID_MAZE1 MAZE DISCARDABLE "maze1.wmz"
Now, that's just the way MSVC does things. The first column is the ID of the
resource. The second column is the type of resource it is. "ICON" and "BITMAP"
are common resources, whereas "WAVE", "MAZE" and "AVI" are custom resources.
When I imported the files into my project, I had to specifically name what kind
of files they were. The third column is a memory thing, and not important, the
fourth column is the external file which is being loaded as the resource.
Bitmaps, Cursors, Icons, and custom resources can all be external files. With
Borland's resource editor, they can all be internal as well, and end up as a
bunch of hex code. Having external files as resources allows you to edit them
with other programs. For example, it's a hell of a lot easier to scan in a
photograph than it is to draw that photograph in a resource editor.
Make sense?
Here's what a dialog looks like inside a (microsoft) .rc file:
DLG_WAIT DIALOGEX 0, 0, 98, 27
STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE
EXSTYLE WS_EX_TOOLWINDOW
FONT 14, "MS Sans Serif", 0, 0, 0x1
BEGIN
CTEXT "Loading the valid filenames. Please Wait.",WID_WAITMSG,
4,4,90,19,SS_SUNKEN
END
That "DLG_WAIT", the very first param of a Dialog resoure, is the ID of the
resource. In fact, let's just get this straight: In the .rc file, the first
param of any resource is that resource's ID. It can be an integer, a #define,
or a bit of text. My advice: Use the #define method.
When you create a resource in Microsoft's Resource editor, it modifies
the resource.h file by #define'ing a new ID, then it assigns that new ID to
the new resource. It keeps track of the numbering itself, and you dont have to
worry about it, cause you're just using the #defines. get it? You dont care
that the About Dialog you've created has an ID of 52431 when you can just
remember DLG_ABOUT. Who cares the your bitmap is 821 when you can just remember
BMID_LOGO?
It makes code more readable, and it makes for fewer mysterious function call
failures. (I cant tell you how many times I used the wrong number in a call to
LoadBitmap(), then spent time wondering why the function failed. Eventually I
adopted the #define way of doing things, and I've never, mark thee well, NEVER
had that problem since.)
Dialog's are normally not external files. This code above, when used, creates a
dialog with that "Please wait" text inside. Menus, String tables, Version, and
Accelerators are normally internal resources.
Why am I wasting time with all this crap? Do you care? Of course not. That's
the point. Resources simplify your life. If you didnt have a dialog resource,
you'd have to write a bunch of code just to get a window with your child
controls to display, then create some sub-message handling loops to get the
window to act like a dialog. If you didn't have bitmap resources, you'd have
to draw bitmaps on the screen pixel by pixel. Oh, you'd love that, wouldn't
you?
So, you know the what, the where, the why. You're the Who. Let's discuss When
and How. Excited? For the sake of simplicity, I'm just going to deal with the
more common resources: Menus, Dialogs, Bitmaps, Icons, and Cursors. And, if I
feel right at the end of all that, I'll touch on the custom resources, like
AVI, WAVE, and MAZE.
Take a look at the following functions:
HICON LoadIcon(HINSTANCE, LPCTSTR);
HBITMAP LoadBitmap(HINSTANCE, LPCTSTR);
HMENU LoadMenu(HINSTANCE, LPCTSTR);
HCURSOR LoadCursor(HINSTANCE, LPCTSTR);
Pretty self explanatory, right? Each function returns a handle (or a pointer)
to a certain type of object. Each function takes 2 parameters:
HINSTANCE - the instance of the app. If you dont have it handy try
GetWindowLong(hwndParent, GWL_HINSTANCE);
LPCTSTR - the identifier for the resource you want to load.
See, the HINSTANCE param tells the application to look within itself for
resources, since once a program is compiled and linked, all the resources in
the project's .rc file are compiled and linked inside as well. The LPCTSTR
param is identifying which resource specifically to load. Now, LPCTSTR is
something like Long Pointer to a something Terminated String. I'll have to
check my hungarian notation chart later. But, if you remember, in the .rc
files, the Microsoft Resource editor is #defining a bunch of IDs for you.
Those IDs are integers. So, luckily, there's a macro which will allow you to
use the integer IDs as the second parameter of all those functions above:
MAKEINTRESOURCE. Literally, that macro will make an integer into a LPSTR
resource id. MAKE. INT. RESOURCE. Clever. In winuser.h:
#define MAKEINTRESOURCE(i) (LPSTR)((DWORD)((WORD)(i)))
Technically, The MAKEINTRESOURCE macro creates a "pseudo-pointer", with zero
for a segment identifier and the integer value for the offset value, then the
whole thing is cast as a LPSTR.
Tada. So, a call to load a bitmap might look like this:
hbmMyBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(BMID_MYBITMAP));
Now hbmMyBitmap is a valid bitmap handle which you can use for blitting, or
adding to ImageLists, or whatever you crazy kids do these days. Likewise:
hmMyMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MID_MYMENU));
hcMyCursor = LoadCursor(hInstance, MAKEINTRESOURCE(CID_MYCURSOR));
hiMyIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IID_MYICON));
You can use this wonderful MAKEINTRESOURCE macro for loading up Dialogs, too.
int DialogBox(HINSTANCE, LPCTSTR, HWND, DLGPROC);
HWND CreateDialog(HINSTANCE, LPCTSTR, HWND, DLGPROC);
Isn't that interesting how all these functions have the same parameters? Sure
it is. Again, with the dialog creating functions that use resources, LPCTSTR
is referring to an ID in the .rc file. And, again, you can use
MAKEINTRESOURCE().
A La Peanut Butter Sandwiches:
DialogBox(hInstance, MAKEINTRESOURCE(DLG_ABOUT), hwndParent,
AboutDlgProc);
CreateDialog(hInstance, MAKEINTRESOURCE(DLG_WAIT), hwndParent,
WaitDlgProc);
See? Isnt that fun?
Now. Remember that any objects you Load, you must Destroy. The dialog box
resources are automatically loaded and destroyed, but Cursors, Icons, Bitmaps,
and Menus are not.
Oh, alright... A Little something about stuff like WAVEs and AVIs.
Look at these again:
WAV_OMG WAVE DISCARDABLE "omg.wav"
AVI_LOGO AVI DISCARDABLE "logo.avi"
The resource editor doesnt care what these files are. It doesnt care that it's
never heard of a "WAVE" or an "AVI". Any resource that it doesnt understand,
it will just display to you in a really nice hex editor. What matters is only
that I can do this:
PlaySound(MAKEINTRESOURCE(WAV_OMG), (HMODULE)GetWindowLong(hwnd,
GWL_HINSTANCE), SND_RESOURCE | SND_ASYNC);
Animate_Open(hwndLogoAvi, MAKEINTRESOURCE(AVI_LOGO));
As you can see, I'm using the same MAKEINTRESOURCE macro for those functions.
Now, for something like:
BMID_MAZE1 MAZE DISCARDABLE "maze1.wmz"
I actually get to use LoadResource(). My completely custom maze resource has
no routine for loading, has no functions that will use it. So, I have to do it
myself. I use LoadResource with a bunch of casting, and voila I have a filled
2D array. Then I have a bunch of functions which take a pointer to a 2d
array as a param. You can do it too. Go ahead and try. Happy clouds, happy
trees. In other words, it's too big a pain to show and explain all that code.
However, below is a sample program for you to enjoy. It demonstrates all of
the topics I've discussed in this article, which are: Loading and using
resources from an .rc file. Tada.
You'll have to supply the external files named in the .rc file, or, if you'd
like a full working copy of the program and the resources, all zipped up and
ready to send, drop me an email at wawb@azheart.com and I'll send the .zip out
to ya.
And if you have any questions, feel free to email me. But read the following
books first:
(For basic C knowledge)
Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language, Prentice
Hall, 1988, 2nd edition, ISBN 0-13-110362-8.
(For MSVC++ 4.x and up users)
"Programming Windows 95", Charles Petzold w/ Paul Yao. ISBN 1-55615-676-6,
Microsoft Press
(For Borland Users)
"Teach Yourself Windows 95 Programming in 21 Days, 2nd Edition", Charles
Calvert.
ISBN 0-672-30531-3, Sams Publishing
--------------
// resource.h
// Microsoft Developer Studio generated include file.
// Used by AhhW.rc
//
#define IDR_AVI1 101
#define IDR_WAV1 102
#define IDB_BITMAP1 103
#define BMID_MADDIE 103
#define IID_MYICON 107
#define DLG_ABOUT 108
#define MID_MENU 109
#define CID_MYCURSOR 112
#define WAV_OMG 113
#define BID_OK 1001
#define MID_ABOUT 40001
#define MID_QUIT 40002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 114
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1002
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
========================
// ahhW.cpp
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include "resource.h"
#define szAhh "Ahh32"
BOOL WINAPI AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI AhhWndProc(HWND, UINT, WPARAM, LPARAM);
void DrawBitmapHere (HDC PaintDC, int x, int y, HBITMAP TheBitmap, DWORD dRap,
BOOL bCenter)
{
BITMAP BitData;
HDC BitmapDC=CreateCompatibleDC(PaintDC);
HBITMAP OldBitmap=SelectBitmap(BitmapDC,TheBitmap);
if(dRap == NULL)
dRap=SRCCOPY;
GetObject(TheBitmap,sizeof(BITMAP),&BitData);
if(bCenter) {
x -= BitData.bmWidth / 2;
y -= BitData.bmHeight / 2;
}
BitBlt(PaintDC,x,y,BitData.bmWidth,BitData.bmHeight,BitmapDC,0,0,dRap);
SelectBitmap(BitmapDC,OldBitmap);
DeleteDC(BitmapDC);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR szCmd, int nCmd)
{
MSG msg;
WNDCLASS W;
HICON hIcon; // handle to my Icon
HCURSOR hCursor; // handle to my cursor
HBRUSH hBrush; // handle to brush
// Load the Resources
hIcon = LoadIcon(hI, MAKEINTRESOURCE(IID_MYICON));
hCursor = LoadCursor(hI, MAKEINTRESOURCE(CID_MYCURSOR));
// create a brush for background
hBrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
memset(&W,0,sizeof(WNDCLASS));
W.style = 0;
W.hInstance = hI;
W.hbrBackground = hBrush;
W.hIcon = hIcon;
W.hCursor = hCursor;
W.cbClsExtra = 0;
W.cbWndExtra = 0;
W.lpszMenuName = MAKEINTRESOURCE(MID_MENU);
W.lpfnWndProc = AhhWndProc;
W.lpszClassName = szAhh;
RegisterClass(&W);
CreateWindow(szAhh, szAhh,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 400, 400,
NULL, NULL, hI, NULL);
while( GetMessage( &msg, NULL, 0, 0 ) ) // Usual message loop stuff
{
if(IsDialogMessage(msg.hwnd, &msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// REMEMBER TO DESTROY THE OBJECTS YOU LOADED!
DeleteObject(hBrush);
DeleteObject(hCursor);
DeleteObject(hIcon);
return( FALSE );
}
BOOL WINAPI AboutDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG: {
PlaySound(MAKEINTRESOURCE(WAV_OMG), (HMODULE)GetWindowLong(hdlg, GWL_HINSTANCE), SND_RESOURCE | SND_ASYNC);
}
return TRUE;
case WM_COMMAND:
switch(wParam) {
case BID_OK:
EndDialog(hdlg, BID_OK);
break;
}
break;
}
return FALSE;
}
LRESULT WINAPI AhhWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HBITMAP hbmBitmap;
hbmBitmap=LoadBitmap((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
MAKEINTRESOURCE(BMID_MADDIE));
BeginPaint(hwnd, &ps);
DrawBitmapHere(ps.hdc, 0, 0, hbmBitmap, 0, 0);
EndPaint(hwnd, &ps);
DeleteObject(hbmBitmap);
}
break;
case WM_CREATE:
PlaySound(MAKEINTRESOURCE(WAV_OMG),
(HMODULE)GetWindowLong(hwnd, GWL_HINSTANCE),
SND_RESOURCE | SND_ASYNC);
return TRUE;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch(wParam) {
case MID_QUIT:
PostQuitMessage(0);
break;
case MID_ABOUT:
DialogBox((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
MAKEINTRESOURCE(DLG_ABOUT),
hwnd, (DLGPROC)AboutDlgProc);
break;
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// ahhW.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
BMID_MADDIE BITMAP DISCARDABLE "maddie.bmp"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
DLG_ABOUT DIALOG DISCARDABLE 0, 0, 126, 118
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",BID_OK,34,97,50,14
CTEXT "Oh my goodness.",IDC_STATIC,7,7,112,30,SS_CENTERIMAGE
END
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IID_MYICON ICON DISCARDABLE "icon1.ico"
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
DLG_ABOUT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 119
TOPMARGIN, 7
BOTTOMMARGIN, 111
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
MID_MENU MENU DISCARDABLE
BEGIN
POPUP "Ahh"
BEGIN
MENUITEM "&About...", MID_ABOUT
MENUITEM SEPARATOR
MENUITEM "E&xit", MID_QUIT
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Cursor
//
CID_MYCURSOR CURSOR DISCARDABLE "cid_mycr.cur"
/////////////////////////////////////////////////////////////////////////////
//
// WAVE
//
WAV_OMG WAVE DISCARDABLE "omg.wav"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
=============
By the way, I used to use Borland, but I dont anymore.
The C code above will work fine with Borland, but the .rc file will not.
C Scene Official Web Site :
http://cscene.oftheinter.net
C Scene Official Email :
cscene@mindless.com
This page is Copyright © 1997 By
C Scene. All Rights Reserved