==============================================================================
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