windows-nt/Source/XPSP1/NT/base/mvdm/wow16/inc/header.txt

365 lines
16 KiB
Plaintext
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
Header File Organization
------------------------
This document describes the rules for public Windows 3.1 header files. These
rules are designed to promote consistency, clarity, Win32 compatibility, ANSI
compatibility, motherhood, and apple pie a la mode.
In the past, windows.h has been fairly randomly organized: it wasn't very
easy to look in the file and figure out how constants, types, and functions
are related. However, the new windows.h is much more rationally organized,
and it's now far easier to understand and modify. In interests of
consistency, readability, and maintainability, it's important that all of our
public header files are consistently organized.
* Include a copyright banner at the top of the file. Something like:
/*****************************************************************************\
* *
* header.h - Brief description of purpose of header file *
* *
* Version 3.10 * *
* *
* Copyright (c) 1992, Microsoft Corp. All rights reserved. *
* *
\*****************************************************************************/
If this header file has been released before, the copyright date
should be something like: 1985-1992.
* Arrange your header in functional groups, like windows.h. Try to
keep related types, structures, constants and functions as close
together as possible in the header. Separate functional groups
within the header with a banner comment, as in windows.h.
* Within a functional group, general typedefs and constants should come
first, followed by logically organized function prototypes.
* Constants or types used by only one or two functions should be
declared near the function.
* Make sure that everything defined in the header file is included by
default: don't require people to #define things to get certain
definitions.
* If you do want to break up your header file, use the #define NOXXX
convention used by windows.h. Try not to have too many groups
controled by NOXXX #defines, because they get confusing and hard to
deal with. Compiler speed and memory capacity is not the problem it
once was, especially with precompiled headers.
* Constants designed to be ANDed or ORed should be defined in hex.
The number of digits should reflect the data size: 2 for bytes,
4 for words, and 8 for longs. Long hex constants should have
an appended L, e.g., 0x12345678L.
* Ordinal constants values (e.g., 1, 2, 3, 4) should be declared
in decimal.
* Provide a comment on all #else's and #endif's that suggests its
corresponding #ifdef: e.g.
#ifdef FOO
...
#else /* FOO */
#endif /* !FOO */
* Precede the header file with #pragma pack(1), and terminate with
#pragma pack(). This ensures that the structures declared in the
header will be packed properly, regardless of what compiler packing
options the user is using for his own code. Because the Windows RC
compiler chokes on #pragma statements in .rc files, it's a good idea
to include this (and any other #pragmas) in an #ifndef RC_INVOKED.
#ifndef RC_INVOKED
#pragma pack(1) /* Assume byte packing throughout */
#endif /* RC_INVOKED */
and:
#ifndef RC_INVOKED
#pragma pack() /* Revert to default packing */
#endif /* RC_INVOKED */
* Prevent multiple inclusion of your header file with the following
construct:
#ifndef _INC_MYHEADER
#define _INC_MYHEADER
...body of header...
#endif /* _INC_MYHEADER */
This is the convention used by the C runtimes. For each header there
is a #define that can be used to determine whether the header has
already been included.
Win32 Upward Compatibility
--------------------------
Part of the goal of 3.1 is to provide a more unified API that will scale with
minimal pain to 32 bits in Win32. To this end, there are a few things you
have to worry about in your headers (and in your code, but that's a different
story...)
In 32-bit Windows, almost all 16 bit parameters, return values, and field
types have been widened to 32 bits. This allows us to generate much more
efficient code on the 386 and on RISC machines.
We need a way of declaring the quantities that will "float" to 32 bits in
32-bit Windows. It turns out that the C language already provides for this
capability: the "int" type, for example, is 16 bits on 16 bit platforms, but
is 32 bits on 32 bit platforms. "short" is always 16 bits on any platform,
"long" is always 32 bits, and "char" is always 8 bits.
So, functions and structures with "int" declarations are already portably
declared. The problem, though is with the WORD type. "WORD" has become an
industry-wide synonym for a 16 bit unsigned quantity. But, it's also used
widely in Windows header files.
Enter the UINT type. The new UINT type is typedef'd as "unsigned int": an
unsigned value that is 16 bits on 16 bit platforms, and floats to 32 bits on
32 bit platforms. In the 3.1 headers, UINT is used in place of WORD wherever
the size of the return value, parameter, or field will change depending on
the platform.
This is a rule that applies to code you write too: on 32 bit platforms, use
of the UINT type rather than WORD will generate faster smaller code. But be
careful of hard-coded size dependencies on WORD: be sure to use sizeof()
instead of constants, etc.
In some cases there may be structure fields whose size WON'T be changing in
32-bit windows, perhaps because the structure is used in a file format and
compatibility is required. If you know ahead of time that this is the case,
be sure to use short and WORD to indicate 16 bit quantities across platforms.
There are a few of these exceptions with the 3.1 bitmap information
structures in windows.h. If you don't know, then use UINT and int.
The new WPARAM, LPARAM, and LRESULT types, used for polymorphic or arbitrary
parameters and return values (e.g., the SendMessage() function), also provide
a useful degree of platform isolation. The WPARAM type is similar to UINT in
that its size varies with the platform. WPARAM should be used in function
parameter, return value, AND structure declarations, even though its size may
vary. The use of these types indicates to the programmer that the value must
be cast and assigned to the proper type before use.
Hence, the following rules:
* Use int and UINT instead of short or WORD, UNLESS you know for sure
that the quantity will remain 16 bits in 32-bit Windows. The Windows
HIWORD and LOWORD macros use WORD, for example. Be sure to check your
uses of short as well as WORD: there are probably a few lurking out
there that should be changed to int. Use int FAR* or UINT FAR* instead
of LPINT or LPWORD.
* Use the LPARAM, WPARAM, and LRESULT types instead of WORD, LONG, or
DWORD as appropriate.
ANSI Compatibility
------------------
Public header files should be ANSI compliant so that people can take
advantage of the highest compiler warning levels possible. This also helps
ensure that our header files work with a wider range of development tools.
* Don't define constants, typedefs, or functions named with a preceding
underscore. This violates the ANSI namespace conventions. There are
a few violations of this rule already in existence (e.g., _lread), but
try not to create any new problems. (The rules are actually more
complicated than "don't use underscores", but you're safe if you keep
away from them).
* Don't use "//" style comments in the header: these are convenient
but non-ANSI, and warning level 4 complains.
* Always test your header file by compiling it with the -W4 compiler
option to ensure that it's ANSI-compatible.
* Make sure that you have no identifier conflicts with the following
C library header files (NOTE: This list may be incomplete. It's
a good start, though).
assert.h
ctype.h
errno.h
float.h
limits.h
locale.h
math.h
setjmp.h
signal.h
stdarg.h
stddef.h
stdio.h
stdlib.h
string.h
time.h
* Structure declarations should be declared with the "tag" prefix, rather
than a leading underscore, as shown below:
typedef struct tagFOO
{
int i;
UINT u;
} FOO;
* Declare fully-prototyped typedefs for all callback functions. By
convention, the type name should be all caps and end in PROC. For
example, the window procedure callback function typedef from windows.h:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
Windows 3.0 Backward Compatibility
----------------------------------
In order to allow users to develop applications with 3.1 headers that will
still run on 3.0, users can #define the WINVER constant to be equal to the
version number of Windows they are compiling against. For 3.0, this would be
0x0300. This constant should be used to ensure that new, non-3.0 compatible
features are not declared when the user is compiling a 3.0 application. Keep
in mind that this version number is hex, not decimal (to be compatible with
the GetExpWinVer() API).
Some of you may own headers that are designed to work with windows 3.0 as
well as 3.1: in this case, you won't have some of the new 3.1 typedefs and
macros defined (e.g., UINT). You can use #ifdef _INC_WINDOWS to determine
whether you've included the 3.1 windows.h. Because yours may not be the only
header that will want to define certain types like UINT and LPCSTR, you
should #define these to be WORD and LPSTR, respectively, since you cannot
typedef something twice. The other option, of course, is to have separate
3.0 and 3.1 versions of your header.
* New, non-3.0 compatible declarations and definitions should be inside
#ifdef (WINVER >= 0x030a)/#endif so that the 3.1 headers can be used
to create 3.0-compatible applications.
* If your header must be compatible with the 3.0 windows.h, use #ifdef
_INC_WINDOWS around #definitions that define the missing types. The
3.0 windows.h file did not #define _INC_WINDOWS.
Use #define rather than typedef to ensure that other headers can
safely do the same thing. Here's an example that will handle
most of the common problems:
#ifndef _INC_WINDOWS /* If not included with 3.1 headers... */
#define LPCSTR LPSTR
#define WINAPI FAR PASCAL
#define CALLBACK FAR PASCAL
#define UINT WORD
#define LPARAM LONG
#define WPARAM WORD
#define LRESULT LONG
#define HMODULE HANDLE
#define HINSTANCE HANDLE
#define HLOCAL HANDLE
#define HGLOBAL HANDLE
#endif /* _INC_WINDOWS */
C++ Compatibility
-----------------
To be able to use functions declared in your header directly from C++, you
need to do one thing:
* Bracket the header file typedefs inside an extern "c" {} block,
conditionally using the __cplusplus #define:
Near the beginning of your header:
#ifdef __cplusplus
extern "C" { /* Assume C declarations for C++ */
#endif /* __cplusplus */
And at the end:
#ifdef __cplusplus
}
#endif
STRICT Compatibility and Windows 3.0 Backward Compatibility
-----------------------------------------------------------
One of the most important features of STRICT is that handle types are no
longer defined as WORDs. They are declared in such a way that will cause a
compiler error if you try to pass the wrong type of handle or a non-handle
value to a function, for example. It's important that all of our handle
types be declared this way when the user #defines STRICT.
A number of new types and such have been defined in windows.h, such as
HINSTANCE, HGLOBAL, and HLOCAL, which should be used where appropriate in
place of the generic HANDLE type. HANDLE should be used only in cases of
an arbitrary handle type.
The WPARAM, LPARAM, and LRESULT types should be used for arbitrary or
polymorphic parameters or return values. Typedefs exist for all callback
functions, which are used in place of FARPROC.
In most cases, functions declared with these types are fully 3.0 compatible
unless STRICT is #defined. It may sometimes be necessary to use #ifdef
STRICT/#else/#endif to provide 3.0-compatible, non-STRICT declarations in
some cases.
* Use DECLARE_HANDLE() to declare handle types. If you have polymorphic
API parameters (or structure fields) that are designed to accept more
than one type of handle (e.g., the GDI SelectObject function), there
are a few tricks you can employ. 1) Declare a generic handle type
like HGDIOBJ as void _near*, which will accept any handle type. The
HANDLE type can be used for this purpose. 2) if the number of
polymorphic types is small, and there are lots of cases where they can
be used polymorphically, use DECLARE_HANDLE to declare one handle
type, and typedef the rest to be the same as the first one (e.g,
HMODULE and HINSTANCE in windows.h).
* Structure and function declarations should use the appropriate STRICT
type, rather than the generic HANDLE,
* Declare arbitrarily or polymorphic types with LPARAM, WPARAM, and
LRESULT instead of WORD, LONG, or DWORD. This indicates to a
programmer that these values should not be used directly, but should
instead be cast and assigned to the proper type of value before use.
* Declare arbitrarily or polymorphic pointer types with void FAR*
instead of LPSTR or BYTE FAR*. The nice thing about the void FAR*
type is that you can pass any type of pointer to it without having to
cast first.
* If any of the above STRICT rules result in declarations that are
not compatible with previously released versions of the header file,
use #ifdef STRICT/#else/#endif to ensure that both declarations
are present.
* Use WINAPI instead of FAR PASCAL for declaring APIs. Use CALLBACK
instead of FAR PASCAL in callback function typedefs.
* Be sure to use "const" where appropriate in your pointer parameters.
If the pointer is read-only, then it should be const. If the function
writes through the pointer, it must not be const. For const
zero-terminated string pointers, use LPCSTR instead of LPSTR.
* Don't declare NPXXX or SPXXX pointer parameter types for new structures.
(but don't remove them if they've already been defined in a shipped
header). Users are encouraged to use "*", const, _near, _far, and
_huge explicitly where appropriate. Now that our headers contain
"const" pointer types, having LP, NP, and const pointer type variants
for every structure would just clog the compiler up with typedefs.
* Spell out pointer declarations, rather than using the LPXXX type form.
This allows for use of const and _huge where appropriate, without
having to define lots of new typedefs:
SetFoo(const FOO FAR* pfoo);
GetFoo(FOO FAR* pfoo);
* Use parameter names in your API function prototypes. This greatly
contributes to the readability and usefulness of your header, at
very little cost. Make sure all your APIs and callback declarations
are fully prototyped. Use the same naming conventions as in our
documentation (contact gregro or ralphw for a summary of those
conventions). NOTE: As of this writing, windows.h does not yet
include function prototype names.