ObjFW 98 SE: Bringing Objective-C to Windows 98 SE and NT 4.0
Created: 17.05.2020 20:34 UTC
Yesterday, I got the idea to port ObjFW to Windows NT 4.0. Considering the lowest supported Windows version so far was Windows XP, this seemed like it would not be too much work. However, the biggest problem was getting a toolchain that still supports Windows NT 4.0!
Just using an old MSYS2 on Windows XP (I had a Windows XP VM around anyway for
writing
IPX/SPX[1,
2]
support) and copying the resulting binaries to Windows NT 4.0 did not work: It
complained about not having AddVectoredExceptionHandler
and
RemoveVectoredExceptionHandler
. Of course: According to MSDN,
those were introduced in Windows XP. Grepping the GCC sources did not yield any
results, but grepping the entire MinGW sources did: It's used in winpthread.
And luckily, it's only used for thread names, so commenting out two lines and
recompiling it was enough. winpthread is linked by GCC by default, because the
exception handling runtime needs it - and we really want exceptions for ObjFW.
After the compiler no longer created binaries that had missing symbols on
Windows NT 4.0, I got an OFInvalidFormatException
loop: MinGW's
implementation of asprintf
seems to call into
snprintf
and expects it to return the number of bytes that would
have been written if a sufficiently large buffer were provided. But on Windows
NT 4.0, it only returns -1 if the buffer is too small. And trying to print the
error of course calls into it again, hence the loop. Unfortunately, my own
asprintf
implementation relied on the exact same
snprintf
behavior - it's part of the C89 aleady, after all! So I
had to work around it
- easy enough. After that, I was able to get a "Hello world" using
of_stdout
and OFApplication
:
And a few minor changes later, all tests were running successfully:
Well, not all of them: I had to disable sockets, as with them, something would
try to load icmp.dll
, which does not exist on Windows NT 4.0. I'll
look into that another time.
Later that evening, I wanted to take things further and thought: If we have
Windows NT 4.0 now, why not Windows 98 SE as well? This is more difficult,
though: Windows has two versions of almost every API: An A
version
and a W
version. A
wrongly stands for ASCII, while
W
stands for wide. Meaning the A
version takes all
arguments in the native, locale-dependent codepage, while the W
version always uses Unicode. The W
versions were only introduced
with Windows NT and ObjFW used the W
versions exclusively. Since
the Windows console is quite bad and needs some nasty hacks
to be made bearable at all, it meant I could not even get a "Hello world"
without porting those hacks to the A
APIs first. So I added a
fallback for when the
W
version returns ERROR_CALL_NOT_IMPLEMENTED
(all
W
APIs return that error on Windows 95/98/ME), got a working
"Hello world" and called it a day (unfortunately I forgot to take a screenshot,
as it got quite late).
The next day, I decided that calling the W
version first and only
falling back to the A
version if it fails with
ERROR_CALL_NOT_IMPLEMENTED
doesn't make a lot of sense: After all,
there is never gonna be a non-NT version of Windows that supports Unicode. So I
decided to add an API
to detect if ObjFW is running on Windows NT and
used that instead.
While I already had a "Hello world" working, it was not one using
OFApplication
, but just using of_stdout
in
main
. So in order to get that working, I
ported
OFApplication
to the A
APIs and finally got a
proper "Hello world". So now it was time to
port everything else to
the A
APIs and voilĂ , all tests are running successfully:
Well, not all of them: For Windows 98 SE, in addition to disabling sockets, I
also had to disable DLLs and threads. For DLLs, I have not figured out yet why
it doesn't work, but for threads, I already know what it is and how to fix it
(WaitForSingleObject
does not support waiting for threads).
And the coolest thing: This now results in a binary that works on anything from Windows 98 SE to Windows 10, using all features as available!
I guess that's not too shabby for a weekend and I'll see that I get all the remaining issues fixed. And who knows, maybe ObjFW 3.11 for Workgroups is next? ;)