Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




WIN31

software


CONTENTS

__________ ______ ____ __________ ______ ____ _____________

Changes to windows.h from 3.0 to Message cracker examples . . 26



3.1 . . . . . . . . . . . . . . . 1 Message handler function

Catching coding errors at signatures . . . . . . . . . 28

compile-time: STRICT . . . . . . . 1 Improving reusability:

Why STRICT? . . . . . . . . . . 2 Template_DefProc . . . . . . 28

Compiling 3.0 applications . . . 3 Private and registered window

New typedefs, constants, and messages . . . . . . . . . . 29

helper macros . . . . . . . . . 4 Message crackers and window

COMSTAT structure change . . . 5 instance data . . . . . . . . 30

Making your code STRICT Message crackers and dialog

compliant . . . . . . . . . . . 6 procedures . . . . . . . . . 33

STRICT conversion hints . . . 10 Message crackers and window

Common compiler warnings and subclassing . . . . . . . . . 34

errors . . . . . . . . . 16416x2323q . . . 11 Dialog procedures: A better

Macro APIs and message crackers . 14 way . . . . . . . . . . . . . 36

Macro APIs . . . . . . . . . . 15 Executing default dialog

3.1-only macro APIs . . . 18 procedure functionality . . 37

Control message APIs . . . . . 18 Returning message results from

Control API Examples . . . . 18 dialog procedures . . . . . 37

Message cracker macros . . . . 22 How it works . . . . . . . 37

Using message crackers and A simplified example using

forwarders . . . . . . . . . 23 predefined macro APIs . . . 39

Saving time and improving Converting existing code to use

readability with HANDLE_MSG . 24 message crackers . . . . . . 41

How message crackers work . 25

i

TABLES

__________ ______ ____ __________ ______ ____ _____________

1: New handle types . . . . . . . 5

ii

===========================================================================

Changes to windows.h from 3.0 to 3.1

===========================================================================

The windows.h header file included with Borland C++ 3.1

contains various features that make application

development faster and easier by helping you find

problems as you compile your code. These improvements

include:

o STRICT option provides stricter type checking,

helping you find type mismatch errors quickly.

o windows.h has been completely reorganized so that

related functions, types, structures, and constants

are grouped together.

o New UINT type used for 32-bit Windows upward

compatibility

o New unique typedefs for all handle types, such as

HINSTANCE and HMODULE.

o Various constants and typedefs missing in the 3.0

windows.h have been added.

o Windows 3.0 compatibility: windows.h can be used to

compile applications that run under Windows 3.0.

o Proper use of "const" for API pointer parameters and

structure fields where pointer is read-only.

If you have ObjectWindows, also see OWL31.DOC in your

OWL\DOC directory for details about how these changes

affect ObjectWindows and your ObjectWindows

applications.

===========================================================================

Catching coding errors at compile-time: STRICT

===========================================================================

The new windows.h supports an option called STRICT that

enables the strictest possible compiler error checking.

Strict compile-time checking helps you find programming

errors when you compile your application, rather than

at runtime.

The idea is that you define STRICT before including

windows.h, which causes the various types and function

prototypes in windows.h to be declared in a way that

enforces very strict type checking. For example,

without STRICT, it is possible to pass an HWND to a

function that requires an HDC without any kind of

compiler warning: with STRICT defined, this results in

a compiler error.

Specific features provided by the STRICT option

include:

o Strict handle type checking (you can't pass an HWND

where an HDC is declared).

o Correct and more consistent declaration of certain

parameter and return value types (for example,

GlobalLock returns void FAR* instead of LPSTR).

o Fully prototyped typedefs for all callback function

types (for example, dialog procedures, hook

procedures, and window procedures)

o Windows 3.0 backward compatible: STRICT can be used

with the 3.1 windows.h for creating applications that

will run under Windows 3.0.

o The COMM DCB and COMSTAT structures are now declared

in an ANSI compatible way.

Why STRICT? =======================================================

The best way to think of STRICT is as a way for you to

get the most out of the error checking capabilities

built into Borland C++. STRICT is of great benefit

especially with code under development, because it

helps you catch bugs right away when you compile your

code, rather than having to track it down at runtime

with a debugger. By catching certain kinds of bugs

right away, it's less likely that you'll ship your

applications with bugs that weren't encountered in

testing.

STRICT also makes it easier to migrate your code to the

32-bit Windows platform later, because it will help you

locate and deal with type incompatibilities that will

arise when migrating to 32 bits.

- 2 -

It's not very difficult to convert your application to

use STRICT, and it can be done in stages if needed.

In order to take advantage of the STRICT option, you

will probably have to make some simple changes to your

source code (described in detail later).

We think you'll find that STRICT makes modifying,

maintaining, and even reading your code much easier,

and well worth the effort to convert your application.

Compiling 3.0 =======================================================

applications

Unless you define STRICT, your 3.0 applications will

compile with windows.h without serious modifications.

The type declarations for many of the Windows APIs and

callback functions have changed; those changes are

backward compatible for C code, but not for C++ code.

If you use C++, you'll notice compile- and link-time

errors in many Windows API functions because of changes

to types like WORD to UINT. See the following sections

for more information.

All of the features of Borland C++ 3.1 can be used to

develop applications that will run under Windows 3.0.

There are two things you must do:

1. Define WINVER to 0x0300 before including windows.h

This ensures that only 3.0 compatible functions,

structures, and definitions are available for use.

You can do this in your makefile with

-DWINVER=0x0300 in the compiler command line, in the

IDE in the Options|Compiler|Code Generation|Defines

input box, or in your code by adding "#define WINVER

0x0300" before you include windows.h.

2. Use the -30 parameter to BRC or RC.

This marks your executable as a 3.0 application, so

that Windows 3.0 won't prevent it from running with

a "This application requires a later version of

Windows" message. You will typically run RC twice in

your makefile: Once to produce the .res file (with

the -r switch), and a second time to combine your

linked .exe and .res files into the final

application. The -30 parameter must be used with the

- 3 -

second invocation of RC. See Chapter 4 in the

Borland C++ User's Guide for instructions on how to

add the -30 parameter to your IDE projects.

New typedefs, =======================================================

constants, and

helper macros The following typedefs and constants have been added to

windows.h. All are 3.0 compatible:

WINAPI Used in place of FAR PASCAL in API declarations. If you

are writing a DLL with exported API entry points, you

can use this for your own APIs.

CALLBACK Used in place of FAR PASCAL in application callback

routines such as window procedures and dialog

procedures

LPCSTR Same as LPSTR, except used for read-only string

pointers. Typedefed as const char FAR*.

UINT Portable unsigned integer type whose size is determined

by host environment (16 bits for Win 3.1). Synonym for

"unsigned int". Used in place of WORD except in the

rare cases where a 16-bit unsigned quantity is desired

even on 32-bit platforms.

LRESULT Type used for declaration of all 32-bit polymorphic

return values.

LPARAM Type used for declaration of all 32-bit polymorphic

parameters.

WPARAM Type used for declaration of all 16-bit polymorphic

parameters.

MAKELPARAM(low, Macro used for combining two 16-bit quantities into an

high) LPARAM.

MAKELRESULT(low, Macro used for combining two 16-bit quantities into an

high) LRESULT.

MAKELP(sel, off) Macro used for combining a selector and an offset into

a FAR VOID* pointer.

SELECTOROF(lp) Macro used to extract the selector part of a far ptr.

Returns a UINT.

- 4 -

OFFSETOF(lp) Macro used to extract the offset part of a far ptr.

Returns a UINT.

FIELDOFFSET(type, Macro used for calculating the offset of a field in a

field) data structure. The type parameter is the type of

structure, and field is the name of the field whose

offset is desired.

-------- ----- ------ ----- ----- ------------

New handle types Typedef Meaning

-------- ----- ------ ----- ----- ------------

HINSTANCE Instance handle type

HMODULE Module handle type

HLOCAL Local handle type

HGLOBAL Global handle type

HTASK Task handle type

HFILE File handle type

HRSRC Resource handle type

HGDIOBJ Generic GDI object handle type

(except HMETAFILE)

HMETAFILE Metafile handle type

HDWP DeferWindowPos handle

HACCEL Accelerator table handle

3.1 only HDRVR Driver handle

-------- ----- ------ ----- ----- ------------

----- ----- -------- The 3.0 declaration of the COMSTAT structure was not

COMSTAT structure ANSI compatible: ANSI does not allow the use of

change BYTE-sized bitfield declarations. To allow windows.h to

----- ----- -------- be used with full ANSI compliance, the COMSTAT

structure has changed. The 7 bit fields below are now

accessed as byte flags of the single status field:

-------- ----- ------ ----- ----- ------------

Old field name Bit of status field

-------- ----- ------ ----- ----- ------------

fCtsHold CSTF_CTSHOLD

fDsrHold CSTF_DSRHOLD

fRlsdHold CSTF_RLSDHOLD

fXoffHold CSTF_XOFFHOLD

fXoffSent CSTF_XOFFSENT

- 5 -

fEof CSTF_EOF

fTxim CSTF_TXIM

-------- ----- ------ ----- ----- ------------

No change is required if you are compiling with WINVER

set to 0x0300 and are not using STRICT.

If you have code that accesses any of these fields,

here's how you have to change your code:

Old code New code

-------- ----- ------ -------- ----- ------ ---------

if (comstat.fEof || ...) if ((comstat.status & CSTF_EOF) || ...)

comstat.fCtsHold = TRUE; comstat.status |= CSTF_CTSHOLD;

comstat.fTxim = FALSE; comstat.status ~= CSTF_TXIM;

Be careful to properly parenthesize "&" expressions.

See windows.h for more details.

Making your code =======================================================

STRICT compliant

Using STRICT with your existing Windows application

code is not very difficult. Here's what you need to do:

o Decide what you want be STRICT compliant.

The first step is to decide what you want to be

STRICT compliant.

STRICT is most valuable with newly developed code or

code that you're maintaining or changing regularly.

If you have a lot of stable code that has already

been written and tested, and is not changed or

maintained very often, you may decide that it's not

worth the trouble to convert to STRICT.

If you are writing a C++ application, you don't have

the option of applying STRICT to only some of your

source files. Because of the way C++ "type safe

linking" works, you may get linking errors if you mix

and match STRICT and non-STRICT source files in your

application.

o Enable strict compiler error checking

- 6 -

First, turn on all Borland C++'s warning and error

messages. In the IDEs, you can use Options|Compiler|

Messages|Display|All; for the command-line compiler,

use the -w switch. Do this without turning on STRICT

for now.

In you're writing applications in C, you might want

to compile them as C++ to take advantage of C++'s

stricter type checking and type-safe linking. You can

do this by renaming .C files to .CPP or by using the

IDEs' Options|Compiler|C++ Options|C++ Always option

or the command-line compiler's -p switch.

o Change your code to use new STRICT types

First you need to go through your own source and

header files and change type declarations to use the

new types defined in windows.h. Below are the common

types that should be changed:

-------- ----- ------ ----- ----- ----------

Old type New type(s)

-------- ----- ------ ----- ----- ----------

HANDLE HINSTANCE, HMODULE, HGLOBAL,

HLOCAL, etc. as appropriate

WORD UINT (EXCEPT where you really

want a 16-bit value even on a

32-bit platform) or WPARAM

LONG LPARAM or LRESULT as appropriate

FARPROC WNDPROC, DLGPROC, HOOKPROC, etc.

as appropriate (MakeProcInstance

and FreeProcInstance calls

require casting)

-------- ----- ------ ----- ----- ----------

See "Strict Conversion Notes" below for particular

things to watch out for when changing your code.

The UINT type is important for 32-bit Windows

migration. On 16-bit windows, WORD and UINT are

identical: 16 bit unsigned quantities. On a 32-bit

platform, a UINT will be 32 bits and WORD is 16 bits.

- 7 -

This allows much more efficient code to be generated

on the 32-bit platform where UINTs are used. You

should use WORD in your code ONLY in those places

where you want a 16 bit value, even on a 32-bit

platform.

Because C++ mangles function names, any callback

C++ users note! function whose arguments have changed between Windows

3.0 and 3.1 (from WORD to UINT, for example) will

generate link-time errors. You must either change the

argument types or replace the 3.1 version of

windows.h with the 3.0 version (in win30.h). It's

highly recommended that you change the parameter

types.

You may also want to use CALLBACK instead of FAR

PASCAL in the declaration of your various callback

functions, though this isn't necessary.

You may be able to save yourself some work by not

converting the body of your window and dialog

procedures to use WPARAM, LPARAM, and LRESULT right

away, since those types are compatible with WORD and

LONG, even in STRICT.

o Make sure your functions are declared before use

To compile as C++ or with all warnings enabled, all

of your application functions must be properly

declared before they are used. It's best to have all

your declarations in an include file, rather than

declaring them in your source files as needed: it's

much easier to maintain your code this way should you

need to change any of the declarations in the future.

Chances are you will need to be making changes to the

function declarations as you change over to the new

STRICT data types.

While it's not strictly necessary to do so, it's a

good idea to provide function parameter names in your

function prototypes. This makes header files much

easier to read, and provides a degree of

self-documentation.

o Recompile without STRICT and fix resulting warnings

Without defining STRICT anywhere, recompile your

application and fix any warnings that result. You can

- 8 -

use the Borland C++ IDE's Search|Next Error (Alt+F7)

command to speed up those fixes.

Some common compiler warnings--and how you should

deal with them--are described later in this section.

Use the rules found there to make the appropriate

changes to your source.

o Run the app to make sure all is well.

After you've gotten your application to compile

cleanly as C++ or with all warnings enabled, it's a

good idea to run your app and put it through it's

paces to make sure all is well.

o Define STRICT

After you've made a first pass and gotten things to

compile cleanly without STRICT, it's time to turn it

on and make the next round of changes.

If you've decided that you want to make your entire

app STRICT, then the best and easiest thing to do is

define STRICT in your makefile, by passing the

-DSTRICT flag to the compiler or to use the Options|

Compiler|Code Generation|Defines input box. If you

want to do it on a per-source file basis, then simply

define STRICT in the source file before you include

windows.h.

o Recompile and clean up resulting errors

Once you've made the changes to your window and

dialog procedures as outlined above, you're ready to

recompile everything.

After turning on STRICT you'll probably get new

errors and warnings that you'll need to go through

and clean up.

You might also get link-time errors because of

C++ users note! mismatched function parameter types. Check your

function prototypes and definititions carefully.

The list of warnings and errors below will explain

how to deal with most of the problems that arise.

- 9 -

STRICT conversion =======================================================

hints

1. Always declare function pointers with the proper

function type, rather than FARPROC. You'll need to

cast function pointers to and from the proper

function type when using MakeProcInstance,

FreeProcInstance, and other functions that take or

return a FARPROC:

BOOL CALLBACK DlgProc(HWND hwnd, UINT msg,

WPARAM wParam,

LPARAM lParam);

DLGPROC lpfnDlg;

lpfnDlg=(DLGPROC)MakeProcInstance(DlgProc, hinst);

...

FreeProcInstance((FARPROC)lpfnDlg);

2. Take special care with HMODULEs and HINSTANCEs. For

the most part, the Kernel module management

functions use HINSTANCEs, but there are a few APIs

that return or accept only HMODULEs.

WinMain and 3. If you've copied any API function declarations from

LibMain are two windows.h, they may have changed, and your local

common examples. declaration may be out of date. Remove your local

declaration.

4. Properly cast the results of LocalLock and

GlobalLock to the proper kind of data pointer.

Parameters to these and other memory management

functions should be cast to LHANDLE or GHANDLE, as

appropriate.

5. Properly cast the result of GetWindowWord and

GetWindowLong and the parameters to SetWindowWord

and SetWindowLong.

6. When casting SendMessage, DefWindowProc, and

SendDlgItemMsg or any other function that returns an

LRESULT or LONG to a handle of some kind, you must

first cast the result to a UINT:

HBRUSH hbr;

hbr = (HBRUSH)(UINT)SendMessage(hwnd, WM_CTLCOLOR,

- 10 -

..., ...);

7. The CreateWindow and CreateWindowEx hmenu parameter

is sometimes used to pass an integer control ID. In

this case you must cast this to an HMENU:

HWND hwnd;

int id;

hwnd = CreateWindow("Button", "Ok", BS_PUSHBUTTON,

x, y, cx, cy, hwndParent,

(HMENU)id, //Cast required here

hinst, NULL);

8. Polymorphic data types (WPARAM, LPARAM, LRESULT,

void FAR *) should be assigned to variables of a

known type as soon as possible. You should avoid

using them in your own code when the type of the

value is known. This will minimize the number of

potentially unsafe and non-32-bit-portable casting

you will have to do in your code. The macro APIs and

message cracker mechanisms provided in windowsx.h

will take care of almost all packing and unpacking

of these data types, in a 32-bit portable way.

9. Become familiar with the common compiler warnings

and errors that you're likely to encounter as you

convert to STRICT.

Common compiler =======================================================

warnings and

errors Here are some common compiler warnings and errors you

might get when trying to make your application compile

cleanly as C++ or with all messages enabled, with or

without STRICT.

These are also the kinds of warnings and errors you'll

receive as you maintain STRICT source code.

Warning: Function should return a vlue

This warning means that a function declared to return a

value does not return a value. In older, non-ANSI C

code, it was common to declare functions that did not

return a value with no return type:

foo(i)

int i;

- 11 -

Functions declared in this manner are treated by the

compiler as being declared to return an "int". If the

function does not return anything, it should be

declared "void":

void foo(int i)

Warning: Call to function <function> with no prototype

This means that a function was used before it was fully

prototyped, or declared. It can also arise when a

function that takes no arguments is not prototyped with

void:

void bar(); /* Should be: bar(void) */

main()

Error: Lvalue required

Error: Type mismatch in parameter

These errors indicate that you are trying to assign or

pass a non-pointer type when a pointer type is

required. With STRICT defined, all handle types as well

as LRESULT, WPARAM, and LPARAM are internally declared

as pointer types, so trying to pass an int, WORD, or

LONG as a handle will result in these errors.

These errors should be fixed by properly declaring the

non-pointer values you're assigning or passing. In the

case of special constants such as (HWND)1 to indicate

"insert at bottom" to the window positioning

functions, you should use the new macro such as

HWND_BOTTOM.

Only in rare cases should you suppress a type mismatch

error with a cast: This can often generate incorrect

code.

- 12 -

Error: Type mismatch in parameter <parameter>

foo.c(335) : warning C4049: 'argument' : indirection to different types

These warnings indicate that you are passing or

assigning a pointer of the wrong type. This is the

warning you get if you pass the wrong type of handle to

a function. This is because under STRICT all handle

types are defined as pointers to unique structures.

To suppress these warnings, fix the type mismatch error

in your code. Once again, it's dangerous to suppress

these warnings with a cast, since there may be an

underlying type error in your app.

Error: Type mismatch in redeclaration of <parameter>

This error will result if you have inconsistent

declarations of a variable, parameter, or function in

your source code.

Warning: Conversion may lose significant digits

This warning results when a value is converted by the

compiler, such as from LONG to int. You're being warned

because you may lose information from this cast.

If you're sure there are no information-loss problems,

you can suppress this warning with the appropriate

explicit cast to the smaller type.

Warning: Non-portable pointer conversion

This error results when you cast a near pointer or a

handle to a 32-bit value such as LRESULT, LPARAM, LONG

or DWORD. This warning almost always represents a bug,

because the hi-order 16 bits of the value will contain

a non-zero value. The compiler first converts the

16-bit near pointer to a 32-bit far pointer by placing

the current data segment value in the high 16 bits,

then converts this far pointer to the 32-bit value.

To avoid this warning and ensure that a 0 is placed in

the hi 16 bits, you must first cast the handle to a

UINT:

HWND hwnd;

LRESULT result = (LRESULT)(UINT)hwnd;

In cases where you DO want the 32-bit value to contain

a far pointer, you can avoid the warning with an

explicit cast to a far pointer:

- 13 -

char near* pch;

LPARAM lParam = (LPARAM)(LPSTR)pch;

Error: Size of the type is unknown or zero

This error results from trying to change the value of a

void pointer with + or +=. These typically result from

the fact that certain Windows functions that return

pointers to arbitrary types (such as GlobalLock and

LocalLock) are defined to return void FAR* rather than

LPSTR.

To solve these problems, you should assign the void*

value to a properly- declared variable (with the

appropriate cast):

BYTE FAR* lpb = (BYTE FAR*)GlobalLock(h);

lpb += sizeof(DWORD);

Error: Not an allowed type

This error typically results from trying to dereference

a void pointer. This usually results from directly

using the return value of GlobalLock or LocalLock as a

pointer. To solve this problem, assign the return value

to a variable of the appropriate type (with the

appropriate cast) before using the pointer:

BYTE FAR* lpb = (BYTE FAR*)GlobalLock(h);

*lpb = 0;

Warning: Parameter <parameter> is never used

This message can result in callback functions when your

code does not use certain parameters. You can either

turn off this warning, or use the argsused pragma to

suppress it.

===========================================================================

Macro APIs and message crackers

===========================================================================

The macro APIs, message crackers and control APIs are

defined in the file windowsx.h. The new handle types,

structures, and helper macros as well as the STRICT

option are a part of the standard windows.h.

- 14 -

Macro APIs =======================================================

windowsx.h contains a number of new APIs implemented as

macros that call other APIs. Generally they make your

code both easier to read and write, and they can save

you lots of typing. These macros are all portable to

32-bit Windows.

void FAR* WINAPI GlobalAllocPtr(WORD flags, DWORD cb)

Same as GlobalAlloc, except that it returns a far

pointer directly.

void FAR* WINAPI GlobalReAllocPtr(void FAR* lp, DWORD cbNew, WORD flags)

Same as GlobalReAlloc, except that it takes and returns

a far pointer.

BOOL WINAPI GlobalFreePtr(void FAR* lp)

Same as GlobalFree, except used with far pointer

alloced (or realloced) with functions above.

BOOL WINAPI GlobalLockPtr(void FAR* lp)

Same as GlobalLock, except used with far pointer.

BOOL WINAPI GlobalUnlockPtr(void FAR* lp)

Same as GlobalUnlock, except used with far pointer.

HMODULE WINAPI GetInstanceModule(HINSTANCE hInstance);

Maps an instance handle to a module handle.

void WINAPI UnlockResource(HGLOBAL hResource);

Unlocks a global resource handle locked with

LockResource.

BOOL WINAPI DeletePen(HPEN hpen)

Deletes a pen (with proper typecasting)

HPEN WINAPI GetStockPen(int i);

Returns one of the stock pens indicated by i (properly

cast to HPEN).

HPEN WINAPI SelectPen(HDC hdc, HPEN hpenSelect)

Selects a pen and returns previously selected pen (with

proper type casting).

BOOL WINAPI DeleteBrush(HBRUSH hbr)

- 15 -

Deletes a brush (with proper typecasting)

HBRUSH WINAPI GetStockBrush(int i);

Returns one of the stock brushes indicated by i

(properly cast to HBRUSH).

HBRUSH WINAPI SelectBrush(HDC hdc, HBRUSH hbrSelect)

Selects a brush and returns previously selected brush

(with proper type casting).

BOOL WINAPI DeleteFont(HFONT hfont)

Deletes a font (with proper typecasting)

HFONT WINAPI GetStockFont(int i);

Returns one of the stock fonts indicated by i (properly

cast to HFONT)

HFONT WINAPI SelectFont(HDC hdc, HFONT hfontSelect)

Selects a font and returns previously selected font

(with proper type casting).

BOOL WINAPI DeleteBitmap(HBITMAP hbm)

Deletes a bitmap (with proper typecasting)

HBITMAP WINAPI SelectBitmap(HDC hdc, HBITMAP hbmSelect)

Selects a bitmap and returns previously selected bitmap

(with proper type casting)

BOOL WINAPI DeleteRgn(HRGN hrgn)

Deletes a region (with proper typecasting)

int WINAPI CopyRgn(HRGN hrgnDst, HRGN hrgnSrc);

Copies hrgnSrc to hrgnDst.

int WINAPI IntersectRgn(HRGN hrgnResult, HRGN hrgnA, HRGN hrgnB);

Intersects hrgnA with hrgnB, setting hrgnResult to the

result.

int WINAPI SubtractRgn(HRGN hrgnResult, HRGN hrgnA, HRGN hrgnB);

Subtracts hrgnB from hrgnA, setting hrgnResult to the

result.

int WINAPI UnionRgn(HRGN hrgnResult, HRGN hrgnA, HRGN hrgnB);

Computes the union of hrgnA and hrgnB, setting

hrgnResult to the result.

int WINAPI XorRgn(HRGN hrgnResult, HRGN hrgnA, HRGN hrgnB);

- 16 -

XORs hrgnA with hrgnB, setting hrgnResult to the

result.

void WINAPI InsetRect(RECT FAR* lprc, int dx, int dy)

Insets the edges of a rectangle by dx and dy.

HINSTANCE WINAPI GetWindowInstance(HWND hwnd)

Returns the instance handle associated with a window.

DWORD WINAPI GetWindowStyle(HWND hwnd)

Returns the window style of a window.

DWORD WINAPI GetWindowExStyle(HWND hwnd)

Returns the extended window style of a window

int WINAPI GetWindowID(HWND hwnd)

Returns the window ID of a window.

void WINAPI SetWindowRedraw(HWND hwnd, BOOL fRedraw)

Disables or enables drawing in a window, without hiding

the window.

WNDPROC WINAPI SubclassWindow(HWND hwnd, WNDPROC lpfnWndProc)

Subclasses a window by storing a new window procedure

address. Returns previous window procedure address.

BOOL WINAPI IsMinimized(HWND hwnd)

Returns TRUE if hwnd is minimized

BOOL WINAPI IsMaximized(HWND hwnd)

Returns TRUE if hwnd is maximized

BOOL WINAPI IsRestored(HWND hwnd)

Returns TRUE if hwnd is restored.

BOOL WINAPI IsLButtonDown(void)

Returns TRUE if the left mouse button is down.

BOOL WINAPI IsRButtonDown(void)

Returns TRUE if the right mouse button is down.

BOOL WINAPI IsMButtonDown(void)

Returns TRUE if the middle mouse button is down.

- 17 -

3.1-only macro APIs

=======================================================

void WINAPI MapWindowPoints(HWND hwndFrom, HWND hwndTo, POINT FAR* lppt,

WORD cpt);

Maps cpt points at *lppt from the coordinate system of

hwndFrom to that of hwndTo.

void WINAPI MapWindowRect(HWND hwndFrom, HWND hwndTo, RECT FAR* lprc)

Maps a rectangle from the coordinate system of hwndFrom

to that of hwndTo.

Control message =======================================================

APIs

New APIs have been added for use in dealing with the

various controls. These APIs are implemented as macros

that call SendMessage, and they take care of packing

the various parameters into wParam and lParam and

casting the return value as needed.

These macros are fully portable to 32-bit Windows: The

32-bit versions will transparently take into account

any differences in parameter packing on the 32-bit

platform.

These macros make your source code smaller and more

readable. They're especially valuable with STRICT in

order to prevent type errors and incorrect message

parameter passing.

There is a 1-to-1 correspondence between a control API

and a window message or window manager API. In the

interests of brevity, the control APIs are simply

listed below: For more information, you can check out

the macro definitions in windowsx.h and the

documentation for the corresponding window message.

Some of the new control APIs are usable with Windows

3.1 only, and are not available if you #define WINVER

0x0300.

----- ----- -------- Here is an example showing how these new APIs are used.

Control API First, here is some code that uses old-style

Examples SendMessage calls to print all the lines in an edit

----- ----- -------- control:

- 18 -

void PrintLines(HWND hwndEdit)

}

Using control APIs, this code would be simplified as

follows:

void PrintLines(HWND hwndEdit)

}

The new style code is much easier to read (and write),

doesn't generate compiler warnings, and doesn't have

any non-portable casts.

Here is a complete list of the control APIs. See

windowsx.h for more info.

Static_Enable(hwnd, fEnable)

Static_GetText(hwnd, lpch, cchMax)

Static_GetTextLength(hwnd)

- 19 -

Static_SetText(hwnd, lpsz)

Static_SetIcon(hwnd, hIcon)

Static_GetIcon(hwnd, hIcon)

Button_Enable(hwnd, fEnable)

Button_GetText(hwnd, lpch, cchMa

Button_GetTextLength(hwnd)

Button_SetText(hwnd, lpsz)

Button_GetCheck(hwnd)

Button_SetCheck(hwnd, check)

Button_GetState(hwnd)

Button_SetState(hwnd, state)

Button_SetStyle(hwnd, style, fRedraw)

Edit_Enable(hwnd, fEnable)

Edit_GetText(hwnd, lpch, cchMax)

Edit_GetTextLength(hwnd)

Edit_SetText(hwnd, lpsz)

Edit_LimitText(hwnd, cchMax)

Edit_GetLineCount(hwnd)

Edit_GetLine(hwnd, line, lpch, cchMax)

Edit_GetRect(hwnd, lprc)

Edit_SetRect(hwnd, lprc)

Edit_SetRectNoPaint(hwnd, lprc)

Edit_GetSel(hwnd)

Edit_SetSel(hwnd, ichStart, ichEnd)

Edit_ReplaceSel(hwnd, lpszReplace)

Edit_GetModify(hwnd)

Edit_SetModify(hwnd, fModified)

Edit_LineFromChar(hwnd, ich)

Edit_LineIndex(hwnd, line)

Edit_LineLength(hwnd, line)

Edit_Scroll(hwnd, dv, dh)

Edit_CanUndo(hwnd)

Edit_Undo(hwnd)

Edit_EmptyUndoBuffer(hwnd)

Edit_SetPasswordChar(hwnd, ch)

Edit_SetTabStops(hwnd, cTabs, lpTabs)

Edit_SetWordBreak(hwnd, lpfnWordBreak)

Edit_FmtLines(hwnd, fAddEOL)

Edit_GetHandle(hwnd)

Edit_SetHandle(hwnd, h)

Edit_GetFirstVisible(hwnd)

ScrollBar_Enable(hwnd, flags)

ScrollBar_Show(hwnd, fShow)

ScrollBar_SetPos(hwnd, pos, fRedraw)

ScrollBar_GetPos(hwnd)

- 20 -

ScrollBar_SetRange(hwnd, posMin, posMax, fRedraw)

ScrollBar_GetRange(hwnd, lpposMin, lpposMax)

ListBox_Enable(hwnd, fEnable)

ListBox_GetCount(hwnd)

ListBox_ResetContent(hwnd)

ListBox_AddString(hwnd, lpsz)

ListBox_InsertString(hwnd, lpsz, index)

ListBox_AddItemData(hwnd, data)

ListBox_InsertItemData(hwnd, lpsz, index)

ListBox_DeleteString(hwnd, index)

ListBox_GetTextLen(hwnd, index)

ListBox_GetText(hwnd, index, lpszBuffer)

ListBox_GetItemData(hwnd, index)

ListBox_SetItemData(hwnd, index, data)

ListBox_FindString(hwnd, indexStart, lpszFind)

ListBox_FindItemData(hwnd, indexStart, data)

ListBox_SetSel(hwnd, fSelect, index)

ListBox_SelItemRange(hwnd, fSelect, first, last)

ListBox_GetCurSel(hwnd)

ListBox_SetCurSel(hwnd, index)

ListBox_SelectString(hwnd, indexStart, lpszFind)

ListBox_SelectItemData(hwnd, indexStart, data)

ListBox_GetSel(hwnd, index)

ListBox_GetSelCount(hwnd)

ListBox_GetTopIndex(hwnd)

ListBox_GetSelItems(hwnd, cItems, lpIndices)

ListBox_SetTopIndex(hwnd, indexTop)

ListBox_SetColumnWidth(hwnd, cxColumn)

ListBox_GetHorizontalExtent(hwnd)

ListBox_SetHorizontalExtent(hwnd, cxExtent)

ListBox_SetTabStops(hwnd, cTabs, lpTabs)

ListBox_GetItemRect(hwnd, index, lprc)

ListBox_SetCaretIndex(hwnd, index)

ListBox_GetCaretIndex(hwnd)

ListBox_SetAnchorIndex(hwnd, index)

ListBox_GetAnchorIndex(hwnd)

ListBox_Dir(hwnd, attrs, lpszFileSpec)

ListBox_AddFile(hwnd, lpszFilename)

3.1 only ListBox_SetItemHeight(hwnd, index, cy)

3.1 only ListBox_GetItemHeight(hwnd, index)

ComboBox_Enable(hwnd, fEnable)

ComboBox_GetText(hwnd, lpch, cchMax)

ComboBox_GetTextLength(hwnd)

ComboBox_SetText(hwnd, lpsz)

ComboBox_LimitText(hwnd, cchLimit)

- 21 -

ComboBox_GetEditSel(hwnd)

ComboBox_SetEditSel(hwnd, ichStart, ichEnd)

ComboBox_GetCount(hwnd)

ComboBox_ResetContent(hwnd)

ComboBox_AddString(hwnd, lpsz)

ComboBox_InsertString(hwnd, index, lpsz)

ComboBox_AddItemData(hwnd, data)

ComboBox_InsertItemData(hwnd, index, data)

ComboBox_DeleteString(hwnd, index)

ComboBox_GetLBTextLen(hwnd, index)

ComboBox_GetLBText(hwnd, index, lpszBuffer)

ComboBox_GetItemData(hwnd, index)

ComboBox_SetItemData(hwnd, index, data)

ComboBox_FindString(hwnd, indexStart, lpszFind)

ComboBox_FindItemData(hwnd, indexStart, data)

ComboBox_GetCurSel(hwnd)

ComboBox_SetCurSel(hwnd, index)

ComboBox_SelectString(hwnd, indexStart, lpszSelect)

ComboBox_SelectItemData(hwnd, indexStart, data)

ComboBox_Dir(hwnd, attrs, lpszFileSpec)

ComboBox_ShowDropdown(hwnd, fShow)

3.1 only ComboBox_GetDroppedState(hwnd)

3.1 only ComboBox_GetDroppedControlRect(hwnd, lprc)

3.1 only ComboBox_GetItemHeight(hwnd)

3.1 only ComboBox_SetItemHeight(hwnd, cyItem)

3.1 only ComboBox_GetExtendedUI(hwnd)

3.1 only ComboBox_SetExtendedUI(hwnd, flags)

Message cracker =======================================================

macros

The message cracker macros provide a convenient,

portable, and type-safe mechanism for dealing with

window messages, their parameters, and their return

values.

The basic idea is that instead of having to pick apart

message parameters with casts and HIWORD/LOWORD and

such, you simply declare and implement a function that

has the properly typed parameters and return value.

The message crackers efficiently pick apart the message

parameters, call your function, and return the

appropriate value from the window message.

Message forwarder macros allow you to forward a message

via DefWindowProc, SendMessage, or CallWindowProc. The

macros do the work of packing explicitly typed

- 22 -

arguments into wParam and lParam and calling the

appropriate function.

With these macros, you don't have to worry about what

parameters go where and what kind of casting you need

to do. They are also portable to 32-bit Windows (where

some of the message parameters have changed).

----- ----- -------- For each window message, there are two macros: A

Using message cracker and a forwarder. To see how these macros work,

crackers and let's use the WM_CREATE message as an example. Here is

forwarders a code fragment showing how a window procedure could

----- ----- -------- use message crackers to handle the WM_CREATE message.

For now, our WM_CREATE message processing will simply

call DefWindowProc.

NOTE: The following examples use STRICT-style

declarations, but you can use message crackers without

STRICT.

// Message handler function prototype (declared in a .h file)

BOOL MyCls_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct);

// Window procedure for class "MyCls" (defined in a .c file)

LRESULT _export CALLBACK MyCls_WndProc(HWND hwnd, UINT msg,

WPARAM wParam,

LPARAM lParam)

}

// WM_CREATE message handler function.

// For now, just calls DefWindowProc.

BOOL MyCls_OnCreate(HWND hwnd,

CREATESTRUCT FAR* lpCreateStruct)

Some important points:

o You must declare and implement a function to handle

the message, which must have a particular "signature"

(the order and type of the parameters, and the type

of the return value, if any).

o You pass this message handler function as the last

parameter to the HANDLE_WM_XXX function. This

function must be declared and fully prototyped before

being used with a message cracker.

o You must always return the value returned by the

HANDLE_WM_XXX function, even if the message handler

function is declared void.

o The FORWARD_WM_XXX function always has the same

signature (parameters and return type) as its

corresponding message handler function, with the

addition of the last parameter, which is the API or

function to be used to forward the message.

o You don't need to use message crackers to handle all

your messages. Old-style message handling code can be

mixed with message crackers in the same window

procedure.

o By convention, message handler functions are named

"Class_OnXXX", where Class is the window class name

and XXX is the name of the corresponding message,

minus the "WM_" and using mixed case instead of all

caps. This is just a convention: you can use any name

you like for the function (although you can't alter

its signature).

----- ----- -------- The HANDLE_MSG macro can be used to reduce the amount

Saving time and of "noise" in your window procedures and save yourself

improving some typing. The HANDLE_MSG macro replaces the "case

readability with WM_XXX:", the HANDLE_WM_XXX call, and the return. So,

HANDLE_MSG instead of

----- ----- --------

case WM_CREATE:

return HANDLE_WM_CREATE(hwnd, wParam, lParam, MyCls_OnCreate);

- 24 -

you can just type:

HANDLE_MSG(hwnd, WM_CREATE, MyCls_OnCreate);

HANDLE_MSG is optional. Some people prefer to "spell

out" the goings-on in their window procedures, and

others prefer the convenience and brevity of the

HANDLE_MSG form.

HANDLE_MSG requires that you name your window procedure

message parameters wParam and lParam. The examples in

this document use HANDLE_MSG.

----- ----- -------- To see how message crackers work, we'll take a look at

How message the definition of the message cracker macros as found

crackers work in windowsx.h (these definitions are somewhat

----- ----- -------- simplified for the sake of clarity):

// BOOL Cls_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct)

#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) \

((fn)(hwnd, (CREATESTRUCT FAR*)lParam) ? 0L : (LRESULT)-1L)

#define FORWARD_WM_CREATE(hwnd, lpCreateStruct, fn) \

(BOOL)(DWORD)(fn)(hwnd, WM_CREATE, 0, (LPARAM)lpCreateStruct)

The comment shows the signature of the message handler

function that you must declare and implement.

Essentially all these macros do is convert the wParam

and lParam message parameters to and from specific,

explicitly typed parameters and invoke a function.

The HANDLE_WM_CREATE macro calls the handler function

with the appropriate parameters, obtained by casting

wParam and lParam appropriately. The return value of

the handler value is mapped to the proper LRESULT

return value, in this case 0L or -1L. If the handler

function returns no value, then 0L is returned.

The FORWARD_WM_CREATE macro calls the supplied message

function (which must have the same signature as

DefWindowProc) with the proper hwnd, wParam, and lParam

parameters calculated from the parameters supplied to

the macro.

The HANDLE_MSG macro is quite simple too:

- 25 -

#define HANDLE_MSG(hwnd, message, fn) \

case message: return HANDLE_##message(fn, hwnd, wParam, lParam)

It simply does the "case" for you, and returns the

result of the proper HANDLE_WM_XXX function. The

message parameter names wParam and lParam are

hard-wired into this macro.

----- ----- -------- Here is a more detailed example of a window procedure

Message cracker for the "Template" class that uses message crackers and

examples message forwarders:

----- ----- --------

// Excerpt from header file for class Template

// Window procedure prototype

LRESULT _export CALLBACK Template_WndProc(HWND hwnd, WORD msg,

WPARAM wParam,

LPARAM lParam)

// Default message handler

#define Template_DefProc DefWindowProc

// Template class message handler functions,

// declared in a header file:

void Template_OnMouseMove(HWND hwnd, int x,

int y, UINT keyFlags);

void Template_OnLButtonDown(HWND hwnd, BOOL fDoubleClick,

int x, int y, UINT keyFlags);

void Template_OnLButtonUp(HWND hwnd, int x,

int y, UINT keyFlags);

HBRUSH Template_OnCtlColor(HWND hwnd, HDC hdc,

HWND hwndChild, int type);

// Exerpt from c source file for class Template

// Template window procedure implementation.

LRESULT _export CALLBACK Template_WndProc(HWND hwnd, WORD msg,

WPARAM wParam,

LPARAM lParam)

}

// Message handler function implementations:

void Template_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)

void Template_OnLButtonDown(HWND hwnd, BOOL fDoubleClick,

int x, int y, UINT keyFlags)

void Template_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)

HBRUSH Template_OnCtlColor(HWND hwnd, HDC hdc,

HWND hwndChild, int type)

}

- 27 -

----- ----- -------- For the most part, the OnXXX message handler function

Message handler parameters have the same name and type as those shown

function in the documentation of the corresponding window

signatures message. To find out exactly what those messages are,

----- ----- -------- look at the commented function prototype in windowsx.h

before the message cracker for the message you're

interested in.

There are a few cases where the OnXXX functions work a

bit differently than the corresponding window message:

o OnCreate (and OnNCCreate) must return TRUE if all is

well, or the window will not be created and

CreateWindow will return NULL.

o The signatures for OnKey and On?ButtonDown functions

are a little different from their corresponding

messages. OnKey handles both key up and key down

messages with the fDown parameter. The On?ButtonDown

functions handle double click messages too, with the

fDoubleClick parameter (though you must be sure to

register your window class with CS_DBLCLKS if you

want to handle double clicks).

o The OnChar function is not passed the virtual key or

key flags information, as this information is not

usable in a WM_CHAR handling. This is because

different virtual keys can generate the same WM_CHAR

messages, and certain key macro processors will

generate WM_CHAR messages with no virtual key or

flags.

----- ----- -------- For every window class, there is a function that must

Improving be called to perform the default processing for that

reusability: window. Normally, this call is DefWindowProc, but if

Template_DefProc you're subclassing a window, it's CallWindowProc, or if

----- ----- -------- you're implementing an MDI child window, it's

DefMDIChildProc, etc.

A very common programming mistake is to copy code from

another window procedure without making the appropriate

change to the default message handler function. This

can lead to subtle, hard to track down bugs.

- 28 -

This is the purpose of the Template_DefProc macro

defined and used in the example above. Every class

should have an appropriate XXX_DefProc macro (or

function) defined which will perform default message

processing.

In the example above, the default message handler is

DefWindowProc, so Template_DefProc is defined as

follows:

#define Template_DefProc DefWindowProc.

For an MDI child window, it might be:

#define MdiWnd_DefProc DefMDIChildProc

The advantage of this scheme is that to steal code from

another window procedure, you need only change the

class name prefix, and the proper default handling will

be taken care of automatically.

----- ----- -------- Message crackers and forwarders work well with new

Private and window messages that you define. You must write a

registered window message cracker and forwarder macro for the new message

messages -- the easiest way to do this is to copy and modify

----- ----- -------- existing macros from windowsx.h.

If your new message value is a constant (e.g.,

WM_USER+100), then you can use HANDLE_MSG to handle the

message in your window procedure. However, if your new

message is registered with RegisterWindowMessage,

HANDLE_MSG can't be used, because variables cannot be

used as switch statement case values: only constants

can. In this case, you can handle it as follows:

// In Template class initialization code:

UINT WM_NEWMESSAGE = 0;

WM_NEWMESSAGE = RegisterWindowMessage("WM_NEWMESSAGE");

...

// In Template_WndProc: window procedure:

LRESULT _export CALLBACK Template_WndProc(HWND hwnd, WORD msg,

WPARAM wParam,

- 29 -

LPARAM lParam)

}

----- ----- -------- It's very common for a window to have some additional

Message crackers "instance data" associated with it that is kept in a

and window separate data structure allocated by the application.

instance data This separate data structure is associated with its

----- ----- -------- corresponding window by storing a pointer to the

structure in a specially-named window property or in a

window word (allocated by setting the cbWndExtra field

of the WNDCLASS structure when the class is

registered).

The message crackers fully support this style of

programming by allowing you to pass a pointer to the

instance data as the first parameter to the message

handlers instead of a window handle. The following

example should make this clear:

// Window instance data structure.

// Must include window handle field.

typedef struct _FOO

FOO;

// "Foo" window class was registered with

// cbWndExtra = sizeof(FOO*), so we can use

// a window word to store back pointer.

// Window properties can also be used.

// These macros get and set the hwnd -> FOO* backpointer.

// Use GetWindowWord or GetWindowLong as appropriate based

// on the default size of data pointers.

- 30 -

#if (defined(__SMALL__) | defined(__MEDIUM__))

#define Foo_GetPtr(hwnd) \

(FOO*)GetWindowWord((hwnd), 0)

#define Foo_SetPtr(hwnd, pfoo) \

(FOO*)SetWindowWord((hwnd), 0, (WORD)(pfoo))

#else

#define Foo_GetPtr(hwnd) \

(FOO*)GetWindowLong((hwnd), 0)

#define Foo_SetPtr(hwnd, pfoo) \

(FOO*)SetWindowLong((hwnd), 0, (LONG)(pfoo))

#endif

// Default message handler

#define Foo_DefProc DefWindowProc

// Message handler functions, declared with a FOO* as their

// first argument, rather than an HWND. Other than that,

// their signature is identical to that shown in windowsx.h.

BOOL Foo_OnCreate(FOO* pfoo, CREATESTRUCT FAR* lpcs);

void Foo_OnPaint(FOO* pfoo);

// Code to register the Foo window class:

BOOL Foo_Init(HINSTANCE hinst)

// The window procedure for class "Foo". This demonstrates how

// instance data is attached to a window and passed to the

// message handler functions. It's fully STRICT enabled,

// and Win 3.0 compatible.

- 31 -

LRESULT CALLBACK _export Foo_WndProc(HWND hwnd, UINT msg,

WPARAM wParam,

LPARAM lParam)

else

}

if (msg == WM_NCDESTROY)

switch (msg)

}

----- ----- -------- Dialog procedures are different from window procedures

Message crackers in that they return a BOOL indicating whether the

and dialog message was processed rather than an LRESULT. For this

procedures reason, you can't use HANDLE_MSG: You must invoke the

----- ----- -------- message cracker macro explicitly.

Here's an example that shows how you'd use message

crackers in a dialog procedure:

BOOL MyDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus,

LPARAM lParam);

void MyDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl,

UINT codeNotify);

BOOL _export CALLBACK MyDlg_DlgProc(HWND hwndDlg, UINT

msg,

WPARAM wParam,

LPARAM lParam)

- 33 -

}

If you'd like to process messages that return values

such as WM_ERASEBKGND in your dialog procedure, or

would like to make it easier to share code between your

window procedures and your dialog procedures, you may

want to make use of the techniques shown later in

"Dialog Procedures: A Better Way."

----- ----- -------- Message crackers can also be used to simplify window

Message crackers subclassing code. With message crackers, unprocessed

and window messages must be forwarded using the appropriate

subclassing FORWARD_WM_* macro. When you are subclassing a window,

----- ----- -------- the proper way to forward unprocessed messages is by

calling CallWindowProc, passing it the previous window

procedure address, along with the four standard window

message parameters.

The FORWARD_WM_* macros can't be used directly with

CallWindowProc, because they can only invoke functions

having the standard window procedure signature:

CallWindowProc has an extra WNDPROC parameter.

This problem is handled easily by simply declaring

XXX_DefProc as a function instead of a macro. Your

function must call CallWindowProc instead of calling

DefWindowProc:

- 34 -

Here's an example showing how this works:

// Global variable that holds the previous window

// procedure address of the subclassed window:

WNDPROC Foo_lpfnwpDefProc = NULL;

// Code fragment to subclass a window

// and store previous wndproc value:

void SubclassFoo(HWND hwndFoo)

// Default message handler function

// This function invokes the superclasses' window procedure.

// It must be declared with the same signature as any window

// procedure, so it can be used with the FORWARD_WM_* macros.

LRESULT Foo_DefProc(HWND hwnd, UINT msg,

WPARAM wParam, LPARAM lParam)

// Foo window procedure. Everything here is the same as in the

// normal non-subclassed case: the differences are encapsulated // in

Foo_DefProc.

LRESULT CALLBACK Foo_WndProc(HWND hwnd, UINT msg,

WPARAM wParam, LPARAM lParam)

}

// Message handlers

void Foo_OnChar(HWND hwnd, UINT ch, int cRepeat)

else

}

----- ----- -------- There are two longstanding sources of confusion and

Dialog procedures: bugs in Windows dialog procedures: 1. There is no way

A better way to return a value from a message handled by a dialog

----- ----- -------- procedure, and 2. it's not possible to execute the

default dialog behavior for a message before executing

your own code in your dialog procedure.

Here is a simple solution to both of these problems

that is compatible with both Windows 3.0 and 3.1. It

unifies the way window procedures and dialog procedures

are coded, and it works very nicely with message

crackers.

Note These techniques, like message crackers, are completely

optional. You can use these techniques with or without

message crackers.

You can code a dialog procedure just as if it were a

window procedure: you have the same freedom to return

values and execute the default dialog processing

messages as you do with window procedures. It's also

easier to copy or share code between dialog and window

procedures.

- 36 -

Executing default dialog procedure functionality

=======================================================

Although Windows provides the DefDlgProc API, it can't

be used as is for our purposes because its

implementation calls the dialog procedure again. If we

called it from our dialog procedure, the dialog

procedure would be called again, recursively, until we

run out of stack space and crash. To prevent this

infinite recursion, we need only detect that we're

being called recursively and return FALSE, which will

cause the default processing to be performed.

Returning message results from dialog procedures

=======================================================

Windows 3.0 and 3.1 both support a general mechanism

for returning values from messages handled in dialog

procedures. Essentially, you store the return value

with SetWindowLong, which will get returned from the

message when your dialog procedure returns TRUE.

There are some special cases you have to worry about:

in some cases, the return value must be returned in

place of the BOOL return value.

How it works

=======================================================

Here is some code that shows how all this comes

together:

// function prototypes in header file..

BOOL CALLBACK _export MyDlg_OldProc(HWND hwndDlg, UINT msg,

WPARAM wParam,

LPARAM lParam);

LRESULT MyDlg_DlgProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam);

// implementation in .c file..

// static (or global) variable for preventing infinite recursion

- 37 -

static BOOL fMyDlgRecurse = FALSE;

BOOL CALLBACK _export MyDlg_OldProc(HWND hwndDlg, UINT msg,

WPARAM wParam,

LPARAM lParam);

result = MyDlg_DlgProc(hwndDlg, msg, wParam, lParam);

// Here if we handled the message, and want to return result.

switch (msg)

}

LRESULT MyDlg_DlgProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam);

}

You must declare a static (or global) BOOL variable

that is initialized to FALSE. It's safe to use the

same global variable for all your dialogs, even if one

dialog procedure brings up another dialog box.

What's important is that the same boolean variable be

used in the "OldDlgProc" and before the call to

DefDlgProx: a local BOOL variable must NOT be used, or

infinite recursion will result.

A simplified example using predefined macro APIs

=======================================================

Three macro APIs are defined in windowsx.h that

drastically simplify the code shown above. They are

SetDlgMsgResult, DefDlgProcEx, and

CheckDefDlgRecursion.

Here's the same dialog code, this time using these

macro APIs:

// prototypes..

BOOL CALLBACK _export MyDlg_OldProc(HWND hwndDlg, UINT msg,

WPARAM wParam,

LPARAM lParam);

LRESULT MyDlg_DlgProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam);

// implementation..

- 39 -

static BOOL fDefDlgEx = FALSE;

BOOL CALLBACK _export MyDlg_OldProc(HWND hwndDlg, UINT msg,

WPARAM wParam,

LPARAM lParam);

LRESULT MyDlg_DefProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam);

LRESULT MyDlg_DlgProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam);

}

As mentioned earlier, it's safe to use the same boolean

fDefDlgEx variable for all your dialog procedures, or

you can define one for each of your dialogs. What's

important is that the same boolean variable be used for

the CheckDefDlgRecursion call AND the DefDlgProcEx call

in a given dialog procedure: a local BOOL variable must

NOT be used, or infinite recursion will result.

You can also use the same _DefProc function declaration

for all of your dialog procedures that use the same

fDefDlgEx variable: for example, you could implement

the following function:

LRESULT CommonDlg_DefProc(HWND hwndDlg, UINT msg,

WPARAM wParam, LPARAM lParam)

then, for each dialog class, #define something like:

#define MyDlg_DefProc CommonDlg_DefProc

----- ----- -------- Converting existing code over to use message crackers

Converting is not particularly difficult. Here are some

existing code to suggestions that should help:

use message

crackers o Have a look at the sample app MAKEAPP for some more

----- ----- -------- detailed examples of the use of message crackers and

message forwarders.

o It's a good idea (though not necessary) to first

convert your code to use STRICT. With STRICT

enabled, the compiler can help you find parameter

type mismatch and other errors much easier.

o It's best to declare all your message handler

functions in an include file, rather than declaring

them directly in the .c file that uses them. You

don't have to use the ClassName_OnXXX naming

convention for your function, but we've found that

it's quite a helpful way to organize the code for a

window class.

o When declaring or implementing a message forwarder

function, just use your editor to search for and copy

the message handler function prototype comment from

windowsx.h and paste it into your source code. This

way you don't have to type it from scratch.

o Use the FORWARD_WM_* macros to send or forward

non-control messages to other windows by passing

SendMessage as the first parameter.

o If you have defined your own private window messages,

you should define a message cracker and forwarder for

each. This is pretty simple to do: Just copy an

existing cracker and forwarder from windowsx.h and

edit it. Be sure to fully parenthesize your use of

macro parameters, and be careful with the casts you

use.

Converting existing window and dialog procedures to

message crackers can be a fair amount of work,

- 41 -

especially if the window and dialog procedures are

large. You can mix and match old-style message

handlers with message crackers, so you may want to use

message crackers for new message handlers, or for

existing code you plan on modifying extensively.

Converting dialog procedures over to the new-style

LRESULT-returning dialog procedures is also something

that isn't required for all your dialog procedures.

You can convert those that you plan to modify, or that

contain code that you may want to reuse in other dialog

procedures.

- 42 -


Document Info


Accesari: 2154
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )