| 17 | | |
|---|
| 18 | | |
|---|
| 19 | | Instructions |
|---|
| 20 | | ------------ |
|---|
| 21 | | 1. Module naming |
|---|
| 22 | | |
|---|
| 23 | | The name of each module shall be win32.qwert, where qwert.h is the name of the original header file. This may change in the future. |
|---|
| 24 | | |
|---|
| 25 | | |
|---|
| 26 | | 2. Constant #defines to enum blocks |
|---|
| 27 | | |
|---|
| 28 | | Convert #defines that define integral constants into enum blocks that group the constants logically. If you can determine the appropriate type of the constants, then specify it as the base type of the enum. |
|---|
| 29 | | |
|---|
| 30 | | 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. |
|---|
| 31 | | |
|---|
| 32 | | 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. |
|---|
| 33 | | |
|---|
| 34 | | Example: |
|---|
| 35 | | |
|---|
| 36 | | enum : uint { |
|---|
| 37 | | WM_DDE_FIRST = 0x03E0, |
|---|
| 38 | | WM_DDE_INITIATE = WM_DDE_FIRST, |
|---|
| 39 | | WM_DDE_TERMINATE, |
|---|
| 40 | | WM_DDE_ADVISE, |
|---|
| 41 | | WM_DDE_UNADVISE, |
|---|
| 42 | | WM_DDE_ACK, |
|---|
| 43 | | WM_DDE_DATA, |
|---|
| 44 | | WM_DDE_REQUEST, |
|---|
| 45 | | WM_DDE_POKE, |
|---|
| 46 | | WM_DDE_EXECUTE, |
|---|
| 47 | | WM_DDE_LAST = WM_DDE_EXECUTE |
|---|
| 48 | | } |
|---|
| 49 | | |
|---|
| 50 | | |
|---|
| 51 | | 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. |
|---|
| 52 | | |
|---|
| 53 | | Group constants of non-integer types (e.g. magic values of handles or string pointers) using a const declaration. Example: |
|---|
| 54 | | |
|---|
| 55 | | const HKEY |
|---|
| 56 | | HKEY_CLASSES_ROOT = cast(HKEY) 0x80000000, |
|---|
| 57 | | HKEY_CURRENT_USER = cast(HKEY) 0x80000001, |
|---|
| 58 | | HKEY_LOCAL_MACHINE = cast(HKEY) 0x80000002, |
|---|
| 59 | | HKEY_USERS = cast(HKEY) 0x80000003, |
|---|
| 60 | | HKEY_PERFORMANCE_DATA = cast(HKEY) 0x80000004, |
|---|
| 61 | | HKEY_CURRENT_CONFIG = cast(HKEY) 0x80000005, |
|---|
| 62 | | HKEY_DYN_DATA = cast(HKEY) 0x80000006; |
|---|
| 63 | | |
|---|
| 64 | | |
|---|
| 65 | | 3. Struct naming |
|---|
| 66 | | |
|---|
| 67 | | Remove struct tag names. Instead, define every struct with its first typedef'd name. Make any other names aliases of this. |
|---|
| 68 | | |
|---|
| 69 | | Indent a struct's members by one tab character. Align the member names in a column using spaces. |
|---|
| 70 | | |
|---|
| 71 | | Example: |
|---|
| 72 | | |
|---|
| 73 | | struct VALENTW { |
|---|
| 74 | | LPWSTR ve_valuename; |
|---|
| 75 | | DWORD ve_valuelen; |
|---|
| 76 | | DWORD ve_valueptr; |
|---|
| 77 | | DWORD ve_type; |
|---|
| 78 | | } |
|---|
| 79 | | alias VALENTW* PVALENTW; |
|---|
| 80 | | |
|---|
| 81 | | Some structs have their own size in bytes as the first member, generally called cbSize, dwSize or lStructSize. Use a member initialiser here. |
|---|
| 82 | | |
|---|
| 83 | | 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. |
|---|
| 84 | | |
|---|
| 85 | | Some structs end with a one-element array, designed to be followed immediately in memory by more elements of the same type. Name such struct members with a leading underscore, and use a property getter to return just the pointer in order to prevent bounds checking. (A |
|---|
| 86 | | |
|---|
| 87 | | |
|---|
| 88 | | 4. COM interfaces |
|---|
| 89 | | |
|---|
| 90 | | 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 may therefore be removed. |
|---|
| 91 | | |
|---|
| 92 | | |
|---|
| 93 | | 5. Consolidate aliases |
|---|
| 94 | | |
|---|
| 95 | | Declare type aliases as aliases, not typedefs. The only exception is |
|---|
| 96 | | |
|---|
| 97 | | typedef void* HANDLE; |
|---|
| 98 | | |
|---|
| 99 | | since handles aren't interchangeable with pointers. Define all specific handle types to be aliases of HANDLE. |
|---|
| 100 | | |
|---|
| 101 | | 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. |
|---|
| 102 | | |
|---|
| 103 | | |
|---|
| 104 | | 6. Declare functions as extern (Windows) |
|---|
| 105 | | |
|---|
| 106 | | 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. |
|---|
| 107 | | |
|---|
| 108 | | This doesn't apply to macros converted to functions (see below). |
|---|
| 109 | | |
|---|
| 110 | | |
|---|
| 111 | | 7. Consolidate ANSI/Unicode selection into version blocks |
|---|
| 112 | | |
|---|
| 113 | | 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. |
|---|
| 114 | | |
|---|
| 115 | | As an exception, reduce string constants to a single declaration of type TCHAR[], bypassing the need to put such a constant in a version block. |
|---|
| 116 | | |
|---|
| 117 | | 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. |
|---|
| 118 | | |
|---|
| 119 | | |
|---|
| 120 | | 8. Translate conditional compilation based on Windows version support |
|---|
| 121 | | |
|---|
| 122 | | 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. |
|---|
| 123 | | |
|---|
| 124 | | Unlike with the C headers, the programmer is expected to specify both the minimum Windows 9x version and the minimum Windows NT version, so both _WIN32_WINDOWS and _WIN32_WINNT are defined in any project. Conditional compilation must therefore, in general, involve checking the values of both constants - either directly, using the && operator, or with the help of the WINVER and _WIN32_WINNT_ONLY constants defined for syntactic sugar. |
|---|
| 125 | | |
|---|
| 126 | | Rather than relying on the conditionals in the MinGW headers, it is a good idea to look on http://msdn.microsoft.com/ to see which Windows versions support each entity that is CC'd. |
|---|
| 127 | | |
|---|
| 128 | | Also available is the _WIN32_IE constant, for functions that rely on a particular version of Internet Explorer being installed. |
|---|
| 129 | | |
|---|
| 130 | | Exception: To import modules conditionally, always use the version identifiers directly. This is in order to support Build. |
|---|
| 131 | | |
|---|
| 132 | | |
|---|
| 133 | | 9. Other conditional compilation |
|---|
| 134 | | |
|---|
| 135 | | Use the built-in version (Win32) and version (Win64) to deal with _WIN64 conditional blocks. |
|---|
| 136 | | |
|---|
| 137 | | Treat STRICT as always defined. |
|---|
| 138 | | |
|---|
| 139 | | 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. |
|---|
| 140 | | |
|---|
| 141 | | |
|---|
| 142 | | 10. Convert function-like macros to functions |
|---|
| 143 | | |
|---|
| 144 | | 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!) |
|---|
| 145 | | |
|---|
| 146 | | For type-generic macros, use a template. |
|---|
| 147 | | |
|---|
| 148 | | Don't just leave the macro expansion verbatim; make some effort to make it look more like a function definition by: |
|---|
| 149 | | * removing pointless parentheses and casts |
|---|
| 150 | | * using line breaks and indentation as you might normally when writing a function |
|---|
| 151 | | |
|---|
| 152 | | |
|---|
| 153 | | 11. Remove leftover preprocessor directives |
|---|
| 154 | | |
|---|
| 155 | | Remove any preprocessor directives, such as #if..#elseif..#endif and any #defines, that have been deemed unnecessary. |
|---|
| 156 | | |
|---|
| 157 | | |
|---|
| 158 | | 12. Whitespace conventions |
|---|
| 159 | | |
|---|
| 160 | | Declare pointers D-style, i.e. a space after the '*', no space before. |
|---|
| 161 | | |
|---|
| 162 | | 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. |
|---|
| 163 | | |
|---|
| 164 | | Always indent the contents of a { ... } block of any kind by one tab character below the level of that in which it is contained. |
|---|
| 165 | | |
|---|
| 166 | | The opening '{' doesn't have a line to itself, and is separated from the function signature, struct identifier, etc. by one space. |
|---|
| 167 | | |
|---|
| 168 | | Use one space after a comma (e.g. in function signatures), no space before. |
|---|
| 169 | | |
|---|
| 170 | | |
|---|
| 171 | | 13. Section heading comments (optional) |
|---|
| 172 | | |
|---|
| 173 | | Comments may be used to create logical section headings within a module. They shall look like this: |
|---|
| 174 | | |
|---|
| 175 | | // Property sheet |
|---|
| 176 | | // -------------- |
|---|
| 177 | | |
|---|
| 178 | | |
|---|
| 179 | | 14. Deprecate functions (optional) |
|---|
| 180 | | |
|---|
| 181 | | 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. |
|---|
| 182 | | |
|---|
| 183 | | 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. |
|---|
| 184 | | |
|---|
| 185 | | |
|---|
| 186 | | 15. Always check that the translated module compiles |
|---|
| 187 | | |
|---|
| 188 | | After translating, compile the module to check for errors. |
|---|
| 189 | | |
|---|
| 190 | | 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. |
|---|
| 191 | | |
|---|
| 192 | | It is a good idea to try compiling under all meaningful configurations of Windows versions. This can be done by using the testcompile.bat file included in the distribution. |
|---|