CS-460/560, Wk1c
Spring, 2000
R. Eckert
MICROSOFT WINDOWS WIN32 API PROGRAMMING --
AN EXAMPLE OF EVENT-DRIVEN, GRAPHICS-ORIENTED PROGRAMMING
EXAMPLE--user moves mouse cursor over a pgm's window area
and clicks left mouse button.
Windows decodes HW signals from mouse
Figures out which pgm user has selected
Sends a message to pgm:
"User has clicked over (X,Y)"
"Do something and return control to me"
Pgm reads message data, does what's needed, returns control to Windows
OVERVIEW OF THE STRUCTURE OF A WINDOWS PROGRAM
(2 main tasks)--
Initial activities
Process messages from Windows (the message loop)
PSEUDOCODE--
Initialize variables, memory space
Create & Show the program's Window
Loop
Fetch any message sent from Windows to this program
If message is QUIT
Terminate program, return control to Windows
If message is something else
Do appropriate actions based on the message ID and parameters
Return control to Windows
End Loop
ESSENTIAL PARTS OF A WINDOWS PROGRAM--
I. THE SOURCE PROGRAM (.C FILE):
A. The WinMain() function--
0. Include files, variable declarations, initialization, etc.
1. Register the class from which the program window will be derived.
2. Create a window based on a registered class.
3. Show the window and cause it to update (repaint) its client area.
4. The Message Loop--Get messages from Windows and dispatch back to
Windows for forwarding to the correct callback message-processing
function; Messages are usually the result of user actions.
B. The WndProc() function--The callback function that acts on messages
from Windows.
II. THE RESOURCE SCRIPT (.RC FILE):
Resource data--Windows static data.
Separate from code and dynamic data
Compiled by a separate "Resource Compiler"
Examples: Keyboard Accelerators, Bitmaps, Cursors, Dialog Box
Definitions, Fonts, Icons, Menus, String Tables.
Separation of static resources and program code-->
Reduced memory demands
Separate tasks of programmer and designer
Changes can be made to user interface without touching the code
NOW WE LOOK AT THE DETAILS (Refer to the EXAMPLE application)--
I. THE SOURCE PROGRAM (.C FILE)
#include <windows.h> /* window's header file - always included */
/* contains constant and function definitions */
A. THE WinMain() FUNCTION--
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow);
Every Windows program must have a WinMain--like main(), starts first.
-returns an int exit code to Windows which does nothing with it.
-PASCAL: l-to-r parm passing on stack, faster than C convention.
4 parameters passed from Windows to WinMain():
-hinstance: instance handle--an ID # created by Windows when app starts.
Each instance has a unique handle, identifies application's data.
[Handle--a pointer to a pointer. Locates an object in memory even though
Windows moves things around.]
-hPrevInstance: if another copy of program is started, will contain the
hInstance value for the last copy started; NULL (0) if no other copy
running. (Under Win32 this is not used.)
-lpszCmdLin: pointer to a char string containing the command line
arguments passed to the program. (like argc, argv in a DOS C program).
-nCmdShow: an integer passed to the program's ShowWindow() function.
Windows is telling the application whether its window is to appear
minimized, as an icon, normal, or maximized when it's first displayed.
Hungarian Notation--used very commonly in Windows programs:
-Help to clarify types of variables
-Precede name with key letters representing its type
-Named after Hungarian Microsoft programmer, Charles Simonyi
prefix data type
---------------------------------------------------
by BYTE (unsigned char)
b BOOL (int, use only TRUE and False, values: 1 and 0)
c char
dw DWORD (double word, 4-byte unsigned long int)
fn function
h handle. An ID value Windows uses internally to keep track of memory
blocks, window ID values, etc.
i int (two byte signed)
l long (4 bytes)
n short (int) near pointer
p pointer
s character string
sz null-terminated character string
w word (two bytes)
can be combined -- lpsz: long pointer to a null-terminated string
1. REGISTERING THE WINDOW CLASS (New Windows Classes)
New windows are created from "classes". All windows of a given class have
same properties: backgrond color, cursor shape, pass Windows messages to
the same function. Can use predefined classes (e.g. "BUTTON") or define and
register a new class.
There can be many instances of the same class.
Windows can only be created if their class has been "registered"--
RegisterClass()--takes one parameter--a ptr to a WNDCLASS structure. The
program must fill in the members of the class describing the window class
before calling RegisterClass().
typedef struct tagWNDCLASS
{
UINT style; /*bitwise or'd binary flags specify window style*/
LRESULT CALLBACK lpfnWndProc)(); /*ptr to the msg-processing function*/
int cbClsExtra; /*count of extra bytes after window class, usually 0*/
int cbWndExtra; /*count of extra bytes after window instance, " */
/*both could be used to reserve memory for extra data*/
HINSTANCE hInstance; /*creating program's instance handle*/
HICON hIcon; /*handle to the window class icon */
HCURSOR hCursor; /*handle to the window class cursor shape*/
HBRUSH hBackground; /*window class background brush*/
LPSTR lpszMenuName /*pointer to resource name of class menu*/
LPSTR lpszClassName; /*ptr to name of the window's class*/
} WNDCLASS;
To register the class of our window, we must set up the members of a WNDCLASS
variable and call RegisterClass() as follows:
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (hInstance, "MyIcon") ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = "MyMenu" ;
wndclass.lpszClassName = "MyClass" ;
if (!RegisterClass (&wndclass))
return 0 ;
If the call to RegisterClass(&wndclass) is successful, a non-zero result will
be returned. If not, we should return a 0 to Windows, which causes execution to
terminate.
-wndclass fields for EXAMPLE.C window:
.style-- CS_HREDRAW|CS_VREDRAW--bitwise or operator==> specifies that
windows of this class should be redrawn if either the horizontal or
verticdal size changes.
CS_HREDRAW = 00000010B
CS_VREDRAW = 00000001B
or'd value = 00000011B
These are examples of binary flags--each bit means something, Bitwise or
operation is a way to tell Windows that several conditions are to be set.
Way to specify combinations. (See Help/Search/WNDCLASS).
.lpfnWndProc--WndProc: the name (address) of the function to be called when
windows sends messages; could have any name you like, but that name must be
registered. Here the name used is WndProc. This function must be declared --
usually at the top of the source file or, more commonly, in a header file
included at the top of the source file.
.cbClsExtra & .cbWndExtra--no extra memory allocated for this class.
.hInstance--specifies which instance the WinProc() lies in.--it's this
instance (passed as a parameter by Windows to WinMain()). Used as a
unique identification for the application calling RegisterClass().
.hIcon, .hCursor--use Windows load functions to use some predefined objects.
IDI_APPLICATION--constant meaning the std windows icon;
IDC_ARROW--constant meaning the standard arrow pointer.
(These are defined in the windows.h file)
Examples: LoadIcon (NULL, IDI_APPLICATION);
LoadCursor (NULL, IDC_ARROW);
.hbrBackground--use Windows GetStockObject() to obtain a solid white brush
which paints the background color of the window. This is a handle to a
brush -- a graphics term refering to the pattern of pixels used to fill
an area. A brush is an example of a Windows "Graphics Device Interface"
(GDI) object. Many other GDI objects are used to "paint" on the client
area of a window.
.lpszMenuName--"MyMenu": The menu is described in the resource script file.
(See EXAMPLE.RC)
.lpszClassName--the name given to this window class; will be used when we
create a window based on the class.
After registering the new window class, the program must create the window.
Any number of windows based on this class can be created.
2. CREATING THE PROGRAM WINDOW
CreateWindow() creates the program's window & returns a handle (a unique ID)
value of type HWND (for the window created)
Arguments to CreateWindow():
window class name
window caption
window style (Boolean OR of different predefined style masks)
initial x position in pixels
initial y position
initial width
initial height
parent window handle (if main window, no parent ==> NULL)
window menu handle (NULL if no menu or if class menu is to be used)
program instance handle (passed in from Windows)
creation parameters (can be used to access extra data, usually NULL)
HWND hWnd ;
hWnd = CreateWindow ("MyClass","Example", WS_OVERLAPPEDWINDOW,
100, 50, 400, 200, NULL, NULL, hInstance, NULL) ;
3. SHOW THE WINDOW AND CAUSE ITS CLIENT AREA tO BE UPDATED
ShowWindow (hWnd,nCmdShow); -- makes window just created visible on screen
-hWnd: specifies which window to make visible
-nCmdShow: specifies how (normal, minimized, etc.)
-set by Windows environment when program is started;
-value is passed in from Windows;
-"normal" can be overridden: start program from the program
manager "file/run" & select "Run minimized"
CAUSE THE WINDOW'S CLIENT AREA TO BE UPDATED (PAINTED WITH THe BACKGROUND
BRUSH:
UpdateWindow (hWnd);
4. THE MESSAGE LOOP
Any time the user interacts with the window, the Windows OS sends a message
to the window's WndProc(). The interaction could be mouse movement, a mouse
button click, a keyboard key press, a resizing or moving operation that causes
some area to be exposed (with a resultant need to be repainted), or any number
of other operations. (Windows sends many other messages as well.)
A message consistgs of some data placed in a block of memory:
A C structure--
typedef struct tagMSG
{
HWND hwnd; /* handle of target window */
UINT message; /* message ID value--always a WM_* constant */
WPARAM wParam; /* a double word of data passed in the message */
LPARAM lParam; /* a second double word of data in the message */
DWORD time; /* time in msec message was sent */
POINT pt; /* mouse cursor (x,y) position when sent */
} MSG;
where
typedef struct tagPoint
{
int nx;
int ny;
} POINT;
Windows fills in all this data before message is sent.
A Windows program must continually check for incoming messages.
This is done by using a small program loop called the message loop.
One possibility: Use GetMessage()
GetMesssage() function---
reads the next message from the application's message queue and fills
the fields of a variable of type MSG.
We want put our call to GetMessage() in a loop that continually retrieves
the message:
while (GetMessage(&msg, NULL, 0, 0))
{ ... }
-if Windows has sent the program a message, GetMessage() puts all the
message data into the structure pointed to by msg.
BOOL GetMessage (lpMsg, hWnd, uFirstMsg, uLastMsg);
lpMsg--address of structure where Windows should put the message data.
hWnd--handle of the window from which the message was sent (NULL ==>
receive messages from all windows belonging to the application that made
the call to GetMessage().)
last 2 parameters--specify a range of message values to be retrieved
(0==>all)
-If no messages, Windows retains control of the system and does other stuff
(with other programs) until a message to the program is generated. Only
then does GetMessage() return to your program. This is the key to
cooperative multitasking!
-For all messages other than WM_QUIT (generated by the WndProc(), as we'll
see, after user actions like a double click on System menu button or Alt-F4
keyboard combination), TRUE (non-zero) is returned to the application's
message loop and the while loop continues.
-GetMessage() returns 0 if the mesage is a WM_QUIT. This causes the while
loop to exit. Then the return (msg.wParam) returns control to Windows,
effectively terminating the application.
-All well-behaved Windows applications must have a message loop!
-GetMessage() is one of the few functions that returns control to Windows,
thus letting other applications run. If a program never yields control to
Windows, no other programs are allowed to run--very bad!
MESSAGE PROCESSING
while (GetMessage (&msg, NULL, 0, 0)) /* message loop */
{
TranslateMessage (&msg) ; /* translate keyboard messages */
DispatchMessage (&msg) ; /* send message to WndProc() */
}
return (msg.wParam) ;
TranslateMessage (&msg)--
-This function "cooks" keyboard input and converts raw key codes to ANSI
codes that can be used by the program.
-Actually it converts WM_KEYDOWN messages (sent when any key goes down)
to WM_CHAR messages that recognize specific keys and key combinations,
e.g., SHIFT-anything.
-These WM_CHAR messages are processed in a well-defined way by the
default Window procedure.
-Gives a keyboard alternative to using the mouse to select.
-Most Windows programs use TranslateMessage() in their message loops.
DispatchMessage (&msg)--
-Since message processing will be done in the function WndProc(), the
program must tell Windows to pass any message data to that function.
-This is done with DispatchMessage (&msg), which sends the message on to
Windows. Windows, in turn, forwards it to the function specified in the
lpfnWndProc member of the WNDCLASS structure--the WndProc().
-Data is sent along in the msg structure that was filled in by
GetMessage().
-This happens each time a message is received. Messages are continuously
retrieved from the program's queue and dispatched to its WndProc().
B. THE WINDOW PROCEDURE--
This is a "callback" function (named so because it is called by Windows).
It contains a big switch/case statement that looks at the message ID of the
current message and acts appropriately on any "interesting" messages.
LRESULT CALLBACK WndProc (HWND hWnd, UINT wMessage,
WPARAM wParam, LPARAM lParam)
Parameters--same as first four fields of the MSG structure--
-the window the message is associated with;
-what the message is;
-any data associated with the message (wParam & lParam message
parameters): this data is specific for each type of message.
wParam--"word" parameter; (16 bits under Win16)
lParam--long param (32 bits)
[This changed under Win32 -- both 32 bits]
The WndProc() in EXAMPLE.C looks for the following messages:
WM_COMMAND--User interacted with a menu item, wParam=Menu item ID).
WM_LBUTTONDOWN--User pressed the left mouse button (lParam contains the
x,y coordinates).
WM_RBUTTONDOWN--User pressed the right mouse button.
WM_CHAR--User pressed a key/key combination that corresponds to a valid
ANSI code (wParam contains the ANSI code).
WM_DESTROY--Generated when the user double clicks on the system menu,
chooses "Close" from the system menu, or presses ALT-F4. This message means
that Windows is in the process of removing the window from the screen.
Any other messages are handled by DefWindowProc(), the default Window
procedure.
It is essential that our programs trap the WM_DESTROY message; if not the
GetMessage() loop will carry on forever, even though the window is gone!
Response to WM_DESTROY--
-program calls PostQuitMessage(0).
-PostQuitMessage() causes Windows to send a WM_QUIT message to the
program's message queue. Recall that when this is encountered,
GetMessage() returns a 0 (the only message for which it does that).
-This causes the program to exit WinMain()'s while loop.
-It then hits return (msg.wParam).
-Value returned is that accompanying the WM_QUIT message (0), and
control goes to Windows (doesn't use the 0), which releases memory used
by the program.
If it's some other message, the program calls DefWindowProc()--the default
procedure. This carries out the default actions for the message received.
e.g., pressing the left mouse button while cursor is over title bar-->
default dragging action. (Movement, repainting all done by Windows default
function.) The DefWindowProc() function is in Windows.
Our EXAMPLE.C program code overrides default window procedure action for
WM_COMMAND, WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_CHAR, and
WM_DESTROY messages.
C. THE RESOURCE SCRIPT (.RC FILE): adding a menu--
Add a MENU BAR with "Rectangle", "Circle" and "Quit" items.
Recall that resources are static data that the linker combines with our
compiled application. Resources are defined in a script (.rc) file that is
compiled by a resource compiler into a .res file.
EXAMPLE.RC--will have a header file included (gives values to named
constants that identify the menu items).
Most straight-forward way of setting up the resource script file--use a
text editor:
/* EXAMPLE.RC resource file */
#include "example.h"
MyMenu MENU
BEGIN
MENUITEM "&Circle", IDM_CIRCLE
MENUITEM "&Rectangle", IDM_RECTANGLE
MENUITEM "Clear &Screen", IDM_CLEAR
MENUITEM "&Quit", IDM_QUIT
END
The resource script file can also be prepared visually using Microsoft's
Developer Studio resource editors. This is especially useful when many resources (in
addition to a menu) are to be incorporated in the application.
Notice that this file has some constants--IDM_CIRCLE, IDM_RECTANGLE, IDM_CLEAR,
IDM_QUIT
These are defined in the EXAMPLE.H header file:
#define IDM_CIRCLE 1
#define IDM_RECTANGLE 2
#define IDM_CLEAR 3
#define IDM_QUIT 10
In the .C program file the menu items are referred to by these constant
names. But they need to have ID numbers so the program can determine which
item was selected.
Use of constant names makes program more readable and less prone to using
the wrong ID value.
The values and names can be anything (although Microsoft has certain
conventions when you use the resource workshop visually).
MyMenu is the name that will be used in the .C program to refer to the menu
resources that are defined in the .RC file.
Keyword MENUITEM specifies that the following text in quotes will appear on
the window's main menu bar.
-The '&' means that the following character, when used in conjunction
with ALT from keyboard and 'cooked', will generate a message indicating
that the item has been selected. The character following the '&' will be
underlined on the window's menu bar.
The header file is also included in the .C program file so that the same ID
values are known both to the C and the resource compiler.
In the EXAMPLE.C program, the lpszMenuName field of the wndclass structure
is set to "MyMenu"--the menu described in the resource file--when the
window class is registered.
WndProc()--now responds to user interaction with menu items.
-Whenever a menu item is selected with the mouse or its keyboard
alternative, Windows sends the application a WM_COMMAND msg;
-wParam contains the ID number of the menu item selected.
-we can do a switch/case on wParam to perform the correct action.
Before examining the EXAMPLE.C program, we need to see how graphics and text
output work in Windows.
TEXT AND GRAPHICS OUTPUT -- Displaying something in a window
------------------------------------------------------------
Under DOS--character modes are fixed size/font
-Fast, but limited display possibilities
DOS systems have video boards that support several graphics modes, but--
-Drawing commands may depend on graphics mode.
-Each program that does graphics has to have different logic for each
graphics mode==>lots of duplicate code.
-Program portability problems.
Windows--always runs in a graphics mode.
-Slower, but more flexible
-everything (including text) drawn one pixel at a time, so:
-Any size/shape is possible
THE DEVICE CONTEXT AND DEVICE INDEPENDENCE
Design goal of Windows: Device Independence
-Same program should work using different hardware without modification
-Windows takes care of hardware interface
-Programmer can concentrate on the pgm
How to achieve this--
-Windows pgms don't send data directly to HW devices (screen, printer)
-Uses the "Graphics Device Interface" (GDI) as an intermediary
-Draws on an abstact surface called a "device context" (DC)
Device Context--
-Associated with a physical device:
(display screen, printer, bitmap, metafile)
-Abstracts the physical device it represents
-Commands to draw on a DC are same regardless of HW (video card, printer)
-GDI translates these to HW commands to output to the physical device
-DC is accessed with a "handle to a DC"
-Must be "gotten" from Windows using GetDC() or BeginPaint()
-Is specific to a given window ==> app can only draw in its client area
-DC specifies attribute settings for drawing (e.g., colors)
-Attribute settings specify HOW drawing primitives will look
e.g., background color, text color, etc.
-Advantage: when drawing, don't have to send a whole bunch of parameters
-Contains drawing objects (pen, brush, bitmap, font, etc.)
also determine how primitives look (pen--drawing color)
-Common DCs come from a Windows cache of only 5!
app should release it when done, or disastrous things could happen!
-Released with ReleaseDC() or EndPaint()
The GDI--Graphics Device Interface--
-Part of Windows that converts drawing function calls to HW commands
-Located in GDI.EXE (a DLL)
-Has many graphics functions:
-Functions that draw Graphics/Text primitives
-Functions to change values of attributes (settings) in a DC
-Has several drawing "objects" (pens, brushes, bitmaps, fonts)
-Shared by all running apps ==> a GDI error by one pgm can crash Windows
-GDI may make use of a device driver pgm for certain devices
-.DRV pgms assist GDI in converting graphics commands to HW commands
-Must be provided by mfg of new devices (graphics cards, etc.)
SOME GDI ATTRIBUTE SETTINGS AND OBJECTS--
ATTRIBUTE DEFAULT FUNCTION
------------------------------------------------------------------------
Background color white SetBkColor() color of empty spaces
Background mode OPAQUE SetBkMode() or TRANSPARENT
Brush Origin (0,0) SetBrushOrigin() pattern origin
Clipping Region whole surf. SelectClipRgn() where output displayed
Color Palette DEFAULT_PALETTE SelectPalette() color palette
Current Position (0,0) MoveToEx() Start coord of line
Drawing Mode R2COPYPEN SetROP2() How to combine w/ bkgnd
Mapping Mode MM_TEXT SetMapMode() units/scaling in DC
Polygon Fill Mode ALTERNATE SetPolyFillMode() how polygons filled
Text Char Spacing 0 SetTextCharacterExtra()
Text Color Black SetTextColor() color of text
OBJECT DEFAULT FUNCTION
------------------------------------------------------------------------
Bitmap none SelectObject() image object
Brush WHITE_BRUSH SelectObject() area fill object
Font SYSTEM_FONT SelectObject() text font object
Pen BLACK_PEN SelectObject() line-drawing object
Objects must be created before being selected into a DC!
Drawing basics (graphics and text): Steps your pgm must perform--
1. Obtain a DC
2. Obtain (Create) and select GDI objects, if required (e.g., a pen)
3. Set drawing attributes, if necessary
4. Call drawing functions
5. Deselect and Delete any GDI objects that were created
6. Release the DC -- in same state in which it was obtained
hDC = GetDC (hWnd); --
-Retrieves a DC handle for the client area of the specified window
-Identified by the value returned in hDC
-This will be used for all subsequent drawing on that DC
-GetDC() allows app to draw on the DC at any time
Another function that gets a DC: BeginPaint(hWnd, &ps); --
-Only used in response to a WM_PAINT msg--
issued when some are of the window has been exposed and thus needs
to be repainted
-Returns a DC for the client area of the window to draw on
-And a ptr to a paint structure (PAINTSTRUCT)
-This contains info about the area of the window that needs to be repainted:
typedef struct tagPAINTSTRUCT
{
HDC hdc; /* device context handle */
BOOL fErase; /* should background be redrawn? TRUE/FALSE */
RECT rcPaint; /* rectangular area to update */
BOOL fRestore; /* reserved for internal use by Windows */
BOOL fIncUpdate; /* reserved */
BYTE rgbReserved[16]; /* reserved */
} PAINTSTRUCT;
ReleaseDC (hWnd, hDC); --
-Each DC takes about 800 bytes of memory, can only have a total of 5
common DCs in memory at a time (one per window at any given time)
-So you must release the DC as soon as you've done your drawing
-If not, bad things can happen AFTER app has been running a while
-Used in conjunction with GetDC()
-EndPaint() does the equivalent for a DC obtained with BeginPaint()
WINDOWS RGB COLOR MODEL--
Uses four-byte numbers to represent colors
Simplest method--direct color:
typedef DWORD COLORREF;
------------------------------------------------------------
| 0 | Blue (0-255) | Green (0-255) | Red (0-255) |
------------------------------------------------------------
MSB=0:
==> RGB color used (default)
-other bytes specify R, G, B intensities
-dithering done for colors that don't match system colors
MSB<>0:
-other bytes specify an index into a color lookup table (palette)
-R,G,B values found at that entry in the lookup table
-indirect color
MACRO RGB() allows us to specify Red, Green Blue intensities.
Generates a COLORREF value (can be used in color-setting ftns) e.g.--
COLORREF cr;
cr = RGB (0,0,255); /* blue */
Definition of RGB():
#define RBG(r,g,b)
((DWORD)(((BYTE)(r) | ((WORD)(g) << 8)) | (((DWORD)(BYTE(b)) << 16)))
Example use in a program--
-SetTextColor(hDC,RGB(255,0,0)) changes text drawing color to red
-SetBkColor(hDC,RGB(0,0,255)) changes the color of the background around
each character to blue
GDI Objects--pens, brushes, bitmaps, fonts.
-Used to "draw" or "paint" on a device context.
-Created with functions like:
CreatePen (PS_STYLE, nWidth, cColRef);
CreateSolidBrush (cColRef);
-There are lots more (see on-line help).
-To be used they must be "selected into" the DC with SelectObject():
HGDIOBJ SelectObject(HDC, HGDIOBJ);
HGDIOBJ--handle to a GDI object.
parameter--handle to new object being selected into the DC
value returned--handle to object being displaced from DC
When selected, the data associated with the object is made available to the
DC for immediate use with graphics drawing functions:
Some GDI drawing functions:
[In each of the following (x1,y1), (x2,y2) are the coordinates of
diagonally opposite corners of the bounding rectangle]
Arc (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
[draws a portion of an ellipse with the current pen]
Chord (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
[paints a filled portion of an ellipse bounded by ellipse border & line
using the current brush to fill the area, current pen for border]
Ellipse (hDc, x1,y1,x2,y2);
[paints an ellipse with the current brush]
MovetoEx (hDC,x1,y1,lpPoint);
[establish position of start of line]
LineTo (hDC,x1,y1);
[draws a line from start point to specified point with current pen]
Pie (hDC,x1,y1,x2,y2,xArcStart,yArcStart,xArcEnd,yArcEnd);
[paints a filled area of ellipse bounded by ellipse arc and a wedge
using the current brush for area, current pen for border]
Polygon (hDC,points_array,nCount);
[paints a closed polygon using the current brush and pen]
Polyline (hDC,points_array,nCount);
[draws a series of one or more conected lines with the current pen]
Rectangle (hDC,x1,y1,x2,y2);
[paints a rectangle with the current brush and pen]
SetPixel (hDC,x1,y1,colref);
[Turns the pixel (point) at x1,y1 to the color specified by colref]
There are several more drawing primitives (see on-line help).
When an object is selected, Windows allocates space for the object in the
program's data segment; this spaced is limited, so pgm should delete the
object when it's done drawing with it--use DeleteObject()
If you don't--they will use up memory after the program that created them
has terminated! -- if too many undeleted objects, could crash Windows!
But to delete an object--
-first, object has to be selected out of the DC
-(trying to delete an object still selected into a DC could crash pgm)
To do this, select the original object back into the DC--
-displaces former object
-then former object can be deleted
A typical sequence with objects:
HPEN hOldPen, hNewPen;
HDC hDC;
hDC = GetDC(hWnd); /* Get a device context to draw on */
hNewPen = CreatePen(PS_SOLID, 3, RGB(0,0,7)); /*Create pen to draw with */
hOldPen = SelectObject(hDC, hNewPen); /* Select new pen into DC ..*/
/* & save handle to old one*/
/* DO SOME DRAWING STUFF WITH THE PEN HERE */
SelectObject(hDC,hOldPen); /* Displace NewPen from DC */
/* Now we can delete it*/
DeleteObject(hNewPen); /* Delete the new pen */
ReleaseDC(hWnd,hDC); /* Get rid of device context */
Stock objects--predefined in Windows; obtain with GetStockObject();
GetStockObject() retrieves a handle to a predefined stock pen/brush/font--
-Stock objects are maintained by Windows
-Should not be deleted!
Example--
SelectObject (hDc, GetStockObject(BLACK_PEN));
Stock
Object Type Choices
-----------------------------------------------------
Pen BLACK_PEN, WHITE_PEN, NULL_PEN
Brush DKGRAY_BRUSH, GRAY_BRUSH, BLACK_BRUSH, LTGRAY_BRUSH,
NULL_BRUSH, WHITE_BRUSH
Font ANSI_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT,
OEM_FIXED_FONT, SYSTEM_FIXED_FONT, SYSTEM_FONT
ANSI_FIXED_FONT useful for tables w/ characters that need to line up.
ANSI_VAR_FONT--smallest of the stock fonts (good for limited space)
An object can be displaced out of the DC for deletion by selecting a stock
object into the DC; i.e. replace last 3 lines of code above with:
SelectObect(hDC, GetStockObject(BLACK_PEN));
DeleteObject(hNewPen);
ReleaseDC(hWnd, hDC);
THE EXAMPLE.C APPLICATION MESSAGE PROCESSING): Details of WndProc()--
WM_COMMAND (menu item clicked)--
wParam==IDM_CIRCLE (User clicked on "Circle" menu item) ==>
Get a device context, create a blue pen for the outline and a
crosshatched magenta brush for the interior of the circle, select
these objects into the device context, and call the Ellipse() ftn
to draw the circle. After that, displace the pen and brush from
the device context, delete them, and release the device context.
wParam==IDM_RECTANGLE (user clicked on "Rectangle" menu item) ==>
Get a CD, create a red pen and a solid cyan brush, select them
into the DC, call the Rectangle() ftn, select the objects out of
the DC, delete them, and release the DC.
wParam==IDM_CLEAR (User clicked on "Clear Screen" menu item) ==>
call InvalidateRect() which causes Windows to send a WM_PAINT message.
This message means that the client area needs to be repainted. (It is
also generated any time the window is exposed.) The Default Window
Procedure responds to this message by repainting the client area with
the class background brush, effectively erasing the window's client
area.
wParam==IDM_QUIT (User clicked on "Quit") ==> program calls
DestroyWindow(), which causes Windows to destroy the window.
WM_LBUTTONDOWN (Left mouse button pressed) ==>
-Get x,y coordinates of cursor from lParam w/ LOWORD & HIWORD macros;
-Get a device context to draw on;
-Output the letter "L" at (x,y) on the device context w/ TextOut();
-Release the device context.
WM_RBUTTONDOWN (Right mouse button pressed) ==>
-Same as above, but outputs the letter "R".
WM_CHAR (A key or key combination was pressed that corresponds to an ANSI
character) ==>
-Get a device context to write on;
-copy the character from the wParam into cBuf;
-Output it to the upper left hand corner of the window's client area w/
TextOut();
-Release the device context.
WM_DESTROY (User double clicked on System menu or hit Alt-F4 ==>
-Post a WM_QUIT message to the application's queue
-This will cause the program to exit the event loop and return to Windows
as described above.
USING MICROSOFT DEVELOPER STUDIO TO CREATE A VISUAL C++ APPLICATION
Startup:
1. From Windows (NT or 95) click 'Start' on Task Bar
2. Select 'Programs | Microsoft Visual C++ | Microsoft Visual C++5.0'
3. Close the "Tip of the Day" box (if it comes up)
You should now have a window containing three subwindows:
left: the Project Workspace Window
right: the Editor Window
bottom: the Output Window
Creating the Project:
1. Select 'File | New'
2. Click on 'Projects' Tab
3. Select 'Win32 Application'
4. Name the project (e.g. example)
The system will use the directory stated in 'Location' as the
parent directory for the project directory that it will create.
You can change this parent directory as you like. Be sure to
use drive C. The project directory will have the name you give
in this step, and all files created by Developer Studio will be
in that directory.
5. Click 'OK'
Note there are now three tabs in the project workspace window:
ClassView -- to look at the classes in a C++ pgm
FileView -- to look at the various files in the project
InfoView -- to access on-line help
Inserting the source files into the project (quick and dirty method):
1. Use the Developer Studio editor (or any other editor) to create
the .c (.cpp), .h and .rc files. Save them in the directory that
contains your other project files--i.e., the directory that was
created above by Developer Studio. If the source files already
exist, you should copy them into the project's directory.
2. From the menu select 'Project | Add to Project | Files'
3. Make sure the 'Look In' box contains the directory with your files.
The files should appear in the larger box below.
4. Select the .c or .cpp file(s) (e.g., example.cpp) and the .rc file
(e.g., example.rc) and click 'OK'. If you now click on the FileView
tab in the Workspace Window, you will see that the files have been
included in the project. To see them in the Editor Window, click on
the file name from the FileView window.
Building the Project:
1. Select 'Build | Build project_name.exe' (e.g., Build example.exe)
The Project will be compiled/linked.
Messages (errors) will appear in the Output Window.
Cleanup:
The entire project (LOTS of files--over 3 Megabytes worth!) will be
in the directory you chose when you created the project on C drive.
The most important files in this directory are the .dsp (Designer Studio
Project) and .dsw (Designer Studio Workspace) files. You should copy
them (along with all your source files) to your diskette. In future
sessions, the project can be brought into Designer Studio by copying the
.dsp, .dsw, and source files to the C drive (in a temporary directory)
and selecting 'File | OpenWorkspace'. Open the temporary directory,
select 'Files of Type:' 'Project' from the lower list box, and double
click on your .dsp or .dsw file in the upper box. You can then rebuild
the project.
After building a project, the project directory will will have a
DEBUG subdirectory. The executable (.exe) will be in that DEBUG
subdirectory. You should also copy it to you diskette.
After you are sure that you have all your source files, your .dsp and
.dws files, and your .exe file on your diskette, delete the project
directory on drive C: to clean it up.
Exiting Developer Studio:
Select 'File | Exit'