Building Win16 GUI Applications in C (2014)
transmissionzero.co.uk
transmissionzero.co.uk
https://blogs.msdn.microsoft.com/oldnewthing/20080207-00/?p=...
http://www.geary.com/fixds.html
...and if you think the 16-bit segmented architecture is annoying, you probably haven't worked with a bank-switched 8-bit system. ;-)
So seeing that EXPORTS and MakeProcInstance() existed solely for the purpose of loading the DS register, and finding that in application code the correct value was already in the SS register and could simply be copied over into DS? That was quite a surprise. Why were we all going to so much work when it wasn't necessary at all?
Ever since then, I've had my eye out for places where the conventional wisdom tells us to write code we don't need.
One that I've seen a lot lately - especially in StackOverflow answers - is an unnecessarily complicated way of creating closures in JavaScript.
Many JavaScript programmers seem to believe that the way you create a closure is by writing a function that returns a function. That works, of course, but unless you really need a function that returns a function, it's a complication you can do without. After all, a simple function call can create a closure all by itself.
For example, someone is writing a Google Maps API app that takes an array of data about different places, loops through the array with a for loop, and creates a marker for each of those places along with a click event handler for each marker.
Experienced JavaScript programmers can probably guess what the problem is going to be. I'll simplify the code a bit here to illustrate:
var places = [
{ name: "One", lat: 34.1, lng: -80.7 },
{ name: "Two", lat: 35.2, lng: -81.8 }
];
var infowindow = new google.maps.InfoWindow();
for( var i = 0; i < places.length; i++ ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
places[i].lat,
places[i].lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( places[i].name );
infowindow.open( map, marker );
});
}
The problem here, of course, is that when the click callback function is called asynchronously, the for loop has already run to completion, and the i and marker variables no longer have the correct values for the marker that was clicked.Inevitably, someone will now answer this question by suggesting a closure, and illustrating how you get a closure by using a function that returns a function:
for( var i = 0; i < places.length; i++ ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
places[i].lat,
places[i].lng
),
map: map
});
// Changes are below, using a function that returns a function
google.maps.event.addListener( marker, 'click', (function( marker, i ) {
return function() {
infowindow.setContent( places[i].name );
infowindow.open( map, marker );
}
})( marker, i );
}
While that works, it's harder to understand than it needs to be. It's like using EXPORTS and MakeProcInstance() to load the DS register.There is a simpler way:
for( var i = 0; i < places.length; i++ ) {
addMarker( places[i] );
}
function addMarker( place ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
place.lat,
place.lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( place.name );
infowindow.open( map, marker );
});
}
By simply calling a function in the loop, we create the closure we need, and we get simpler and more readable code too.Of course you can achieve the same effect by using array.forEach(), because that calls a function and gives you a closure automatically:
places.forEach( function( place ) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(
place.lat,
place.lng
),
map: map
});
google.maps.event.addListener( marker, 'click', function() {
infowindow.setContent( place.name );
infowindow.open( map, marker );
});
});
Here's a search that will show numerous StackOverflow answers that promote the complicated function-returning-a-function pattern:https://www.google.com/search?q=stackoverflow+maps+api+marke...
...and I never thought I'd see x86 segment registers and JavaScript closures mentioned in the same comment.
Invoking an already existing function instance doesn't make a new closure - it keeps its existing one. Evaluating a statement containing the 'function' keyword does make a new closure.
A closure links to the enclosing function's scope, all the way back to the root/global scope (the window object on browsers).
Memory leaks can occur when closure scopes link infinitely, so it's handy to use the function-returns-function pattern (but not the way you described it) to 'reset' the closure links to something near the root scope.
JS module systems like CommonJS have done well to encourage very tidy closure scope chains even in extremely complex javascript programs.
first "bad" example will work just fine if you replace "var i/marker" with "let i/marker"
Though OpenSource often tends to move forward without thinking much about compatibility, that's right. I guess, it's not an interesting topic for hackers, who work in their free time.
They never pause for breath. Like, you cannot see the application think. Ever. Whenever you ask it to do something, it responds instantly. You click the mouse, and by the next frame the app has updated and is waiting for your instructions.
It's astonishing how much nicer this makes them to use.
(And it's interesting that the Xaw classic design is... um... well, it's as ugly as hell, but it's a kind of functional ugliness which you stop seeing after a bit. Motif, on the other hand, remains awful. The really interesting one of the old widget sets is Open Look, which still looks elegant and polished today: http://www.daylight.com/dayhtml/doc/thor/thor.sample3.gif)
I wonder if there's any mileage in resurrecting one of these old widget systems? Update it to work with modern languages and desktop environments, make sure the accessibility's up to scratch (Motif got this right; don't know about Xaw and Open Look), make sure that modern workflow menus-and-windows apps can be written using them...
On the other hand, if your actual premise is open source, there's simply less incentive of avoiding breaking binary compatibility. I bet there were a few angry words and stomped feet in the last decades at Microsoft because of having to maintain that.
Not that glib/gtk are poster childs for even source-level compatibility, not even within one major version. There's a reason why several projects are switching to Qt.
(I'm doing my Cato bit here: I still think that they should've done an Xt toolkit instead of inventing the wheel yet again. There certainly was some experience there, given that GIMP 0.x was written for Motif.)
...Linux 0.01 didn't come out until 1991, so you'd be hard-pressed to find a compatible binary from 1990. ;)
I recently wrote a simple 32 bit Windows app that runs on 3.11 (with the 32 bit subsystem installed) through Windows 10. On the more recent Windows versions it supports theming and it's per-monitor high DPI aware. That is to say, it looks and works like a modern app.
To do this I had to build it as a freestanding binary with MingGW and do some version probing and dynamic DLL lookups but altogether it wasn't too hard.
Should I get Visual Studio 6.0 or is a MinGW workflow [1] the way to go? Is MSDN still usable as a Win32 reference or is there some old go-to Win32 bible that I should look for?
[1] http://www.transmissionzero.co.uk/computing/win32-apps-with-...
MSDN is still the go to place for all Win32 documentation although it can be tricky to find exactly what you want as MS prefer to push you towards .NET related stuff. Even finding plain C++ can be a pain with them pushing you towards Android C++ development by default for some reason?!
For software you can go with MinGW-W64 or Visual Studio 2015 Community (you will need to do a custom install and select the VC++ tools as they are not included by default anymore).
I used theForger's Win32 tutorials back in the day to get started http://winprog.org/tutorial/
The recent versions of VS are somewhat more difficult for generating single-file standalone executables which run on OSes older than Vista.
If you want to build single exe for Windows XP you need to install the specific C++ tools for XP option in Visual Studio. Having not had to use it I do not know if this allows you to build an exe that does not rely on the latest VC++ redist though?
Though there's a shortcoming: if your application consists of EXE file(s) calling into your own DLL(s), then in /MT or /MTd mode you have multiple instances of libc in the process address space, and so tricks like "malloc() in dll, free() in exe" or "open() in dll, close() in exe" will produce weird runtime errors. That won't be a problem if DLL interfaces are strictly C functions and structs, i.e. without classes, STL, smart pointers, exceptions, RTTI-related stuff etc.
The other option (which I believe MinGW uses) is to link with msvcrt.dll, something which MS doesn't officially support and rather strongly discourages but actually works very well in practice and enables the "one binary for Win95 to Win10" effect.
MSVCRT.DLL used to be default runtime library for code produced by Microsoft Visual Studio 6 compiler. This DLL is still shipped with Windows, albeit its sprintf() doesn't support %ll format specification, its time() function assumes that time_t is 32-bit, and in general it feels very 1998y. It required some redist on Win95 and NT4 IIRC.
Really cool to see that Windows 1.0 application run on Windows 10!
it was fun at the time, though information was limited and precious, you ended up being very dependent on books and magazines to find out about how to get things done.
I spent a sizeable amount of my scholarship money on Microsoft Systems Journals, Charles Petzolds "Programming Windows" and "Undocumented Windows". The latter would teach you tricks like saving the Window position in an INI file so you could make it reappear in the same place where it was closed...
Having right click system menu options that you could extend in what for me is a simple syntax is awesome :-)