/
porting.txt
102 lines (94 loc) · 6.61 KB
/
porting.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
== How the examples were ported ==
A few notes about the issues found when porting C code to D can be read about here:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.announce&article_id=20764
I've made a stub file which contains the basic WinMain function.
Originally I've made a batch file build script but replaced this with a more safer and faster D
build script which doesn't assume you have all the dependencies and will warn you about
errors.
The porting process is:
- Run Uncrustify on the file to format the code to a specific look.
A configuration file that was used with Uncrustify is provided in the root directory.
- Copy the contents of the stub file, paste it at the top of an existing .c project. Compare to
the .c file's winmain function and see if there's anything different (i.e. the app
might have keyboard translators, or it creates menus and windows programatically,
etc.). Copy any new or changed stuff to our D MyWinMain function.
- If the app uses resources uncomment the "import resource;" statement.
- Change 'appName' string to match the .c file's 'szAppName'. This is important as resources are
usually tied to a name and won't load unless you specify the same name.
- Convert '->' to '.'.
- Change all TCHAR[] types to either strings, and then use toUTF16z when calling WinAPI
functions, or change them to wchar[] if they're going to be written to.
- To call WinAPI functions which require the character length, I've used std.utf.count,
which returns the number of code points. Using the .length field directly would be
incorrect, as that returns the code unit count, not the code point count.
- Change "LRESULT CALLBACK WndProc" to "extern (Windows) LRESULT WndProc"
- Add default cases to all switch statements.
- Change fallbacks to "goto case;" statements (fallbacks are banned in 2.054+)
- Change sizeof(var) to var.sizeof
- Replace inline wsprintf calls such as:
TextOut(hdc, 0, y += cyChar, szBuffer, wsprintf(szBuffer, ("lfWidth = %i"), lf.lfWidth));
to this (also replace any special format specifiers with %s):
szBuffer = format("lfWidht = %s", lf.lfWidth);
TextOut(hdc, 0, y += cyChar, szBuffer.toUTF16z, szBuffer.count);
- Pass static and dynamic arrays by using their .ptr field.
- Floats and Doubles are default-initialized to NaN. This causes hardware halts or throws
exceptions if we forget to initialize them, based on the configuration. A good way to catch
exactly where a fault occured isto use the DDBG debugger.
- Replace GetWindowLong and SetWindowLong with the 64-bit compatible GetWindowLongPtr and
SetWindowLongPtr.
At this point I try to compile the module and typically get some of the following errors:
- linker errors meas we're probably missing a pragma call. E.g. if you import
'import win32.wingdi;', you should also add 'pragma(lib, "gdi32.lib");'. The WindowsAPI
modules each have a pragma at the top which you can copy into your projects.
- Change implicit casts to explicit casts, e.g. (HBRUSH)GetStockObject to
cast(HBRUSH)GetStockObject
- Change assignment of strings to fields that expect pointers by using .toUTF16z
- Change any malloc and free calls to GC.malloc and GC.free (there are plenty left)
- 'PNTR var = GC.malloc()' itself has to be replaced with 'PNTR var =
cast(typeof(var))GC.malloc().
- Change C-style struct initializers with explicit D ctor calls. This is always
problematic to convert, but it helps if your editor supports macros or simultaneous
multiline editing.
- Change C-style multidimensional arrays to D ones. Sometimes it's a simple conversion of
TCHAR[][] to string[].
- Replace calls to TEXT("string") to either ("string") or "string". Either will work,
while the conversion to latter might be harder to automate unless you're a happy regex
hacker.
- Fix implicit conversions of 0 to void*, this won't compile in D so we use null here
instead.
- Sometimes calls to LOWORD and HIWORD have to be casted to a short or some other type.
In some C examples this was missing and produced erratic GUI behavior due to various
implicit unsigned int<>short conversions. (See Chap7/BlokOut2/BlokOut2.c and its
executable for an example. Try making a selection beyond the border of the window to
see the bug.)
- Change static vars to enums where appropriate. Some statics are used as immutable
values in C, in those cases we can use enums (this might not always work due to CTFE
limitations).
- Replace comparisons with null with 'is null' and '!is null'.
- Add any missing imports. There are usually two cases:
1. We're missing a winapi prototype, so we find it in the WindowsAPI library and do an
import to a specific module.
2. There was a .c file with various functions which is used in multiple examples.
Sometimes its missing in the current directory, so I had to copy it from the other
examples.
- Some API calls write to a static array by giving them the .ptr field of such an array.
To retrieve a D string out of such an array, we have to treat it as a wchar*
zero-terminated string. So I've had to write a fromWStringz function for this purpose.
This should all be avoided by writing better code, code like this will be improved later.
Additional changes:
- Put BeginPaint and EndPaint next to each other by using 'scope(exit) EndPaint' after
the call to BeginPaint. Same thing for SelectObject - DeleteObject.
- Speed issues: There was an example or two where the speed of drawing shapes was
extremely fast (the book was written for 1998 machines). In those cases I've added a
call to Thread.sleep() in the example's drawing loop.
- Replace for loops with foreach loops.
Final steps:
- Make sure the example compiles runs the same as the C example (unless the C example was
buggy to begin with). There might be runtime bugs that need to be taken care of, such as:
- Missing menus
- Missing or incorrect icons (e.g. forgot to replace a call like 'LoadIcon(NULL,
IDI_APPLICATION);' to 'LoadIcon(NULL, IDI_INFORMATION);
- Incorrect background color (e.g. used 'wndclass.hbrBackground = cast(HBRUSH)
GetStockObject(WHITE_BRUSH);' instead of 'wndclass.hbrBackground = cast(HBRUSH)
GetStockObject(BLACK_BRUSH);'
- I could have forgotten to uncomment "import resource;" in the code.