Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.
Version 9 (modified by smjg, 11 years ago)
a few more updates

WindowsApi Translation Instructions

Note: Some modules may still need to be updated to the new translation instructions, but these instructions should be followed in all further work at translating the headers. Search for "RU:" to find things that have changed.

Title comment

Should take this form:

/***********************************************************************\
*                               winbase.d                               *
*                                                                       *
*                       Windows API header module                       *
*                                                                       *
*             Translated from MinGW API for MS-Windows 4.0              *
*                                                                       *
*                       Placed into public domain                       *
\***********************************************************************/

where winbase.d is the name of the translated header file, and 4.0 is the version number of the MinGW headers used.

Module naming

The name of each module shall be win32.qwert, where qwert.h is the name of the original header file.

Imports

Declare imports to be private. Exception: For a few modules, the whole point is to import other modules, and in these, the imports must be declared public.

Constant #defines to enum blocks

Convert #defines that define integral constants into enum blocks that group the constants logically. If you can determine the appropriate type of the constants (you may need to read the MSDN documentation to do so), then specify it as the base type of the enum. RU: Always use enum, not const, in order to force inlining.

If the enum block so defined contains consecutive values, they do not need to be explicitly specified, as D will automatically assign consecutive values to them (with some exceptions such as the HKEY constants defined in winreg.d).

Indent the constants within an enum block with one tab character. In each enum block, align the equals signs in a column, using spaces rather than tabs.

Example:

enum : uint {
	WM_DDE_FIRST     = 0x03E0,
	WM_DDE_INITIATE  = WM_DDE_FIRST,
	WM_DDE_TERMINATE,
	WM_DDE_ADVISE,
	WM_DDE_UNADVISE,
	WM_DDE_ACK,
	WM_DDE_DATA,
	WM_DDE_REQUEST,
	WM_DDE_POKE,
	WM_DDE_EXECUTE,
	WM_DDE_LAST      = WM_DDE_EXECUTE
}

Sometimes in the original headers, a logical group of constants is interrupted with other declarations (e.g. constants related to specific Windows messages). Move these interruptions to below the whole group.

Struct naming

Remove struct tag names. Instead, define every struct with its first typedef'd name. Make any other names aliases of this.

Indent a struct's members by one tab character. Align the member names in a column using spaces.

Example:

struct VALENTW {
	LPWSTR ve_valuename;
	DWORD  ve_valuelen;
	DWORD  ve_valueptr;
	DWORD  ve_type;
}
alias VALENTW* PVALENTW;

Some structs have their own size in bytes as the first member. The name of this member varies from struct to struct; these names include cbSize, dwSize and lStructSize. Use a member initialiser here.

A few structs use bit fields. Because D doesn't have bit fields, they must be simulated using property getters/setters. See dde.d for an example. Use bool for one-bit members; otherwise use the smallest integer type that will accommodate the required number of bits. Setters should return their arguments.

Some structs end with a one-element array, designed to be followed immediately in memory by more elements of the same type. For these, declare a non-array member named with a leading underscore, and use a property getter to return its address to prevent bounds checking. For example:

struct MIB_IPADDRTABLE {
    DWORD         dwNumEntries;
    MIB_IPADDRROW _table;

    @property MIB_IPADDRROW* table() { return &_table; }
}

Constant pointers

RU: Declare const pointers D-style, for example

const(GUID)* pguid;

Since we are now supporting D2 only, the old CPtr template is no longer used.

COM interfaces

Translate DECLARE_INTERFACE constructions into D interfaces. Be sure to omit inherited members. See unknwn.d for an example. The macros used to access interface functions become unnecessary and should be removed.

As interfaces and classes in D are in fact references (pointers) to the actual data, one indirection level must be removed in the type definition when translating from C++ to D (because it becomes implicit).

Therefore the pointer to an interface in C++, e.g.:

typedef IUnknown *LPUNKNOWN;

becomes in D an alias to the interface itself:

alias IUnknown LPUNKNOWN;

Consolidate aliases

Declare type aliases as aliases, not typedefs. Exception: RU: Use the DECLARE_HANDLE template to define handle types as follows:

mixin DECLARE_HANDLE!("HGDIOBJ");
mixin DECLARE_HANDLE!("HBITMAP", HGDIOBJ); // to define one handle type as a subtype of another

Where multiple aliases for the same type appear in the same module (or logical section thereof), consolidate them into a single alias declaration. For structs, these should be placed immediately below the struct definition.

Declare functions as extern (Windows)

Remove attributes such as WINAPI from function prototypes, replacing them with extern (Windows). Where several functions are declared together, use an extern (Windows) attribute block.

This doesn't apply to macros converted to functions (see below).

Consolidate ANSI/Unicode selection into version blocks

Where #ifdef UNICODE is used to select A/W versions of functions and other identifiers, replace with version (Unicode). Use only aliases, rather than enums or const declarations, within these version blocks. These should be defined in one place at the end of each module, or at the end of some logical section within the module.

Exception: reduce string constants to a single declaration of type TCHAR[], bypassing the need to put such a constant in a version block.

Any aliases based on identifiers defined in these version blocks (e.g. pointer type aliases without A/W) should be declared after the version blocks.

Translate conditional compilation based on Windows version support

Every module that uses this conditional compilation must privately import win32.w32api, which defines the constants used to set the minimum version of Windows an application supports.

RU: Previously, we based this on two compile-time contstants: _WIN32_WINDOWS and _WIN32_WINNT. Now that Windows 9x is no longer supported either by Microsoft or by DMD, we consider only _WIN32_WINNT, and ignore _WIN32_WINDOWS. Also, any check for _WIN32_WINNT >= 0x500 can be omitted, since we are not supporting anything older than Windows 2000.

Rather than relying on the conditionals in the MinGW headers, it is a good idea to look on MSDN to see which Windows versions support each entity that is CC'd.

Also available is the _WIN32_IE constant, for functions that rely on a particular version of Internet Explorer being installed.

Exception: To import modules conditionally, always use the version identifiers directly. This is in order to support Bud.

Other conditional compilation

Use the built-in version (Win32) and version (Win64) to deal with _WIN64 conditional blocks.

Treat STRICT as always defined.

For other #ifdefs designed to be specified by the programmer, leave the directive in, commented out. This is pending decision on which to include and which to leave out, and how to name them.

Convert function-like macros to function templates

Use the appropriate parameter and return types. If necessary, consult the API docs to find out what these are. (Watch out for parameters that are documented as LPSTR but should actually be LPTSTR!)

RU: Always declare them as templates, so that they will be inlined and so not rely on a project-specific .lib file. For example:

WORD MAKELANGID()(USHORT p, USHORT s) { return cast(WORD)((s << 10) | p); }

For type-generic macros, parameterise the template accordingly.

Don't just leave the macro expansion verbatim - make some effort to make it look more like a function definition by:

  • removing pointless parentheses and casts
  • using line breaks and indentation as you might normally when writing a function

Remove leftover preprocessor directives

Remove any preprocessor directives, such as #if..#elseif..#endif and any #defines, that have been deemed unnecessary.

Whitespace conventions

Declare pointers D-style, i.e. a space after the '*', no space before.

Function definitions, struct/enum definitions, etc. should be separated by a blank line. Exceptions: One-line functions (such as bitfield setters/getters) may be placed together without intervening blank lines. Alias declarations of a struct or enum follow its definition without a blank line, and have a blank line below them.

Always indent the contents of a { ... } block of any kind by one tab character below the level of that in which it is contained.

The opening '{' doesn't have a line to itself, and is separated from the function signature, struct identifier, etc. by one space.

Use one space after a comma (e.g. in function signatures), no space before.

Section heading comments (optional)

Comments may be used to create logical section headings within a module. They shall look like this:

// Property sheet
// --------------

Deprecate functions (optional)

If you discover when reading the documentation for a function, structure, etc. that it is intended only for compatibility with 16-bit Windows versions, you may mark it as deprecated. Group deprecated function prototypes within a block under a deprecated attribute block. Be sure to deprecate the ANSI/Unicode aliases as well.

Although bothering with this is optional for the time being, it is preferred that if you do it at all, then you check the whole module for deprecated structures and functions.

Always check that the translated module compiles

After translating, compile the module to check for errors.

Sometimes there will be errors due to undefined types or other identifiers. These compiled in C because of the nature of C preprocessor macros, but fail in D where they are treated symbolically. To deal with this, use a private import.

Use the testcompile.bat file in the distribution to try compiling under all meaningful configurations of Windows versions.