The Infamous Windows “Hello World” Program
charlespetzold.com
charlespetzold.com
So, believe it or not, you might still find it out there in the wild.
[1] eg: https://github.com/ruby/ruby/blob/v1_8_7_374/compar.c
Under Windows it is easy to create a console application, which is similar to the usual C helloworld. But the application here creates a windows to show hello world - so it's fair to compare it with a similar program on GNU/Linux/Xlib.
EDIT: Yes, it is much easier to use something as Qt on GNU/Linux, but there are also abstractions around the WinAPI available, so using Qt on GNU/Linux for a helloworld example yields no fair comparison.
It was the mid 90s and I didn't have unix/linux at home. In those days you had to pay for a compiler on windows and I didn't know anyone running linux. Just a couple of years later it was so much easier!
The thing that changed for me was getting a chance to help a young man with his programming who had come up through Windows rather than something easier, and he was full of all the wonder and excitement I had had at his age for programming, and had no idea that he was running with a large handicap to his productivity. And partly because many of the "tools" you had to have like Visual Studio worked their butt off to make that gap small, from self writing code to documentation at your fingertips. I realized that if I didn't get the 'wonder' back of writing code I was going to become one of those guys who sits around grumbling about how in my day things were so much better and FORTRAN just worked dammit :-).
What I tried to learn from it was that systems with great generality suffered from usability efficiency. The reason UNIX hello.c was so simple is that UNIX programs, by and large, did simple things, algorithmic computation driven by interaction over a character based interface. One way to drive effecive use of a system was to either eliminate or otherwise hide the modalities that were "possible" but not probable in future execution of the program.
That has helped me see when I'm building something that is letting to much generality leak out to the surface.
I'm entering "curmudgeon" age myself, at least for our industry, and what I'm getting curmudgeonly about are my fellow curmudgeons, and that's part of why. Oh, programming was so much better in the 8-bit era? So go back there then. It's still available to you if you want it! Oh, you don't want to take actions to match your words? Why not? Because, in a nutshell, no it wasn't easier. It was easier to do the things it could do, sure, but it can't do much. So stop telling me about how wonderful the 8-bit era was.
That's just one example. There's several of these recurring rants that come up here on HN periodically.
There is a downside though when you get a curmudgeon with a fondness for the "old" days and takes action by re-implementing your build system to look like something you would read about in an IBM JCL handbook :-) I'm sure 8 character error codes were easy to remember but even better is a short sentence on what the error was.
But because Petzold used "his" hello-world program to introduce you to a bunch of critical, foundational concepts -- to show you everything you needed in order to get a functioning top-level application window open on the desktop -- I myself found it a great place to start. By forcing you to confront the message loop from square one, it really reinforced the idea that you aren't in Kansas anymore (and you can't do things like the Kansans do).
As Petzold hints in his article, the original version of the book (for Windows 2.x) didn't even start with that program, but worked its way up to hello-world through a sequence of 5-10 iterations that got you to that point over the course of an entire chapter. (Thus for example one iteration actually opened a top-level window but lacked a message loop, so the program exited and the window immediately disappeared after it was created.) It makes sense that he had to edit out such a lackadaisical introductory chapter as the subject of Windows programming got bigger and bigger, but I for one always liked that chapter.
Does anyone remember why the original samples allocated structures on the heap, did stuff with them, then immediately freed them, rather than just using them on the stack directly? Was that a thing? Were early Windows systems stack constrained or something?
#include <windows.h>
#include <winuser.h>
int main() {
MessageBoxA(0, "Hello", "World", MB_OK);
}
Depending on if you specify /subsystem:windows or not in the linker settings, you can even get a console window (and thus printf() to it, etc.) and use it for logging etc. alongside the GUI, something that isn't often mentioned.Another thing that probably drives people away is the insistence on starting with a "whole window" app, one that uses CreateWindow/RegisterClass, handles WM_PAINT, etc. when for a lot of purposes, a "modal dialog-based" app using a template and DialogBoxParam would be sufficient, and writing one of those is certainly easier. I've made a lot of trivial GUIs of the "window with a set of buttons to do various things" type with Win32, and it definitely doesn't take all that much work. Nothing more than two functions, one main() and one wndproc(), switch on the message/control in the wndproc() depending on which button was clicked, and do something accordingly.
I think it's a bit of a shame that the majority of the documentation on it is both overly complex and nonstandard in style, because the true "essence" of Win32 is really a small and simple C API. It's easy to create really small programs with it, and it doesn't take much code to do that either. When I learned WinSock I wrote a simple netcat-like GUI terminal emulator and it was <10KB.
Were early Windows systems stack constrained or something?
Win16, certainly. All apps ran in a segmented address space and the stack was usually in the 4-8KB range.
No GUI stuff, but all the differences between Win32 and Linux for the APIs I used were abstracted away with a few handfuls of typedef's and #define's.
Are there any tutorials/articles on how to do it?
http://www.pld.ttu.ee/prog/win32/source.html
Look at "Minimal dialog-based WIN32 application" - it has some code to do some other stuff but the basic skeleton is the same. Here is another example of the minimal code:
http://forums.codeguru.com/showthread.php?349927-Simple-Win3...
This may be a bit of an odd recommendation, but also look for tutorials on how to create keygens - the majority of those are dialog-based since the interaction fits the model perfectly - when a button is clicked, read one or more text fields, do some computation, write the result to another text field.
As I recall, there was a different entry point to distinguish DOS from Windows programs. A Windows program started in DOS would have a stub main that just printed out the "this is a Windows program" warning.
It's actually quite a reasonable segregation to do when you consider the different kinds of setup each type of program requires, not that there aren't other ways around it, of course.
- Win16/win32 programs in PE object files which could be: -- Console subsystem, where they'd have a stdin and stdout and all that, take a char* argv, their entry point was main(), etc. and if run directly would open a console window, and if run from a console would block the console with their own execution. -- Windows subsystem, where they would have the more elaborate entry point (WinMain), could get unicode argv, etc. If run directly they would not spawn a console, and if run from a command line would appear to return immediately. - DOS EXE files, which could obviously only be 'console' and only had one entry point.
PE executables (of either sort) had a stub DOS exe at their header that would print that message.
As another post pointed out, nothing was actually stopping you from using the gui windows apis from a console app, incidentally. It wasn't done very often though.
I don't use it out of choice, but it isn't so bad. Though, this was using a slightly HN slightly differently. complete variable types in the name i have different feelings for!
No offense, but what you describe seems more or less like the reviled version (stuffing ~redundant~ information in the name, where you end up with strName and iCounter).
The more-or-less accepted version described in the GP is trying to fix a shortcoming of the language's type system. If you cannot tell your compiler that this is a (default example) pixel value and this is a color, because both are plain ints to it, you're trying to make accidents less likely by requiring the programmer to double-check the prefixes (which encode a subtype, a special type. Not int, but int-representing-color or int-as-a-bool-here).
for iObject = 1:nObject
style from matlab coding firmly rooted in my head - when hungarian isn't even creating universally and easily understood/parsed names I just can't see the value.These days in C++ I'd only ever consider using s or g, and never the type specifiers. I also largely consider signalling member variable vs. local variable to be pointless in all its forms (including _ suffix or prefix) now.
"used in my example above where we decided that us meant “unsafe string” and s meant “safe string.” They’re both of type string. The compiler won’t help you if you assign one to the other"
Assuming you have the luxury of a language with a good type system (either because it's designed for the task in hand or it's extensible), the compiler can help you, and you would be much better off having unsafe and safe strings as separate types. Then the encode function simply becomes a function of type unsafe -> safe. I believe Michael Snoymann touches on this in his presentation, "Designing Type-Safe Haskell APIs"[1].
I'm not arguing that Joel's method isn't a good idea. However, if you can it's better to leave hints for the compiler, not just the programmers.
[1] https://docs.google.com/presentation/d/1K7smIeqmca-fY8qgQUKr...
int m_some_property;
Type in the name is definitely an overkill.Additionally, you can have the pairing of pbFoo with pcbFoo, where pbFoo is a byte array, and pcbFoo is a pointer to the size of pbFoo.
Void pointers are a major mess which should be avoided altogether unless there is really no other choice. And putting anything in the name of the void pointer doesn't really prevent it from pointing to something else entirely.
For instance, I'm not a native English speaker and I don't see the use of Hungarian notation (specially when used heavily).
There's a difference between a couple of handy conventions and things that actually make your code hard to read and less flexible (change the type, change the name too).
Or to consider it another way, we've constructed the idea of variable naming being essential under the premise that we want code to relate to native language at each step. But when code is put into an interlingual context this breaks down relatively quickly - at the extreme end, the non-native speaker has to reverse-engineer meanings anyway. In the terse/documented style, I more explicitly acknowledge this separation of actions and definitions.
A lot of the thinking around naming conventions feeds into the expected workflow - when considering the pre-Intellisense, namespace-free era of C coding that Windows Hungarian arose in, it makes sense to bulk up the names a little so that every point of the code conveys more meaning and doesn't collide by accident. But if the environment already gives you ample guidance towards meaning and categorization, the bottleneck revolves more around how much code fits onscreen.
Normally, you'd save the stack space by putting the structure definition in a separate function, and you'd regain the space on return from the function. But for these Hello World apps, you're over-simplifying to death, trying to reduce the number of functions. I suspect the LocalAlloc here is vestigal, from an earlier hello app where the HelloInit function was inline.
Some interesting notes on what happened in a few years .. when I first saw .net Compact Framework, it blew my mind how easy it was to write .net code/WinForms stuff. Even though I was relatively poor, I bought an MSDN subscription on the spot and started to write mobile apps. This was early 2000s. If MSFT had paid proper attention to mobile devices, they could have rocked it.
I faced weird challenges. Companies would ask me for 20K to certify my free apps on their phones. It was disgusting. I sold a few apps on the hangango app store, and then got out of mobile dev (missed the whole iPhone revolution). Talk about bad timing.
Thank Linus we still have an alternative to Microsoft, here and now! It is absolutely a huge gain for us that Unix was not eradicated by the feral, vermin new thing.
BTW, Gnu's "Hello" is 709k.
Because BSD isn't a "thing?"
Linux or not, it would have happened. The difference is that if the lawsuits didn't encumber the community and create uncertainty, it could have been the dominant Unix-like today.
I have no idea what the hell you mean by "Linux making the idea popular to culture".
Anyway, this is all "what if" line of thought .. just my opinion. But I've been a Linux user since the days of the minix-list, and a user of Unix-based systems since 1980.
Even though I favour a nix environment, there is more sanity in Windows programming circles methinks. I have been thinking about getting into some .NET development these days and moving away from doing nix stuff, altho I really love the BSDs.
Borland had a better solution if I remember, in their C++ Builder thing.
Another "adventure" I had in Win32, trying to make a dynamic dialog box (basically, selecting something in a list opened up a list of properties - like the Preferences dialog box in most browsers). It was some time before I figured out how to do it (there was a coordinate conversion needed)
Strangely it is still maintained by volunteers: http://sourceforge.net/projects/owlnext/
But it was always behind Microsoft's MFC in terms of features. And you had to drop out of it for advanced stuff and then you had to spend a bunch of time figuring out how it actually worked.
This was really no less true of MFC, and imo the OWL code was a lot cleaner and easier to understand. In the end, though, they were both just wrappers around the windows api.
But the GP seems to be referring to VCL, which is what came with Delphi/C++Builder. One of the reasons OWL was usually behind MFC was that Borland basically abandoned it and their mainline compiler products for the Delphi line, and C++Builder was always a second-rate product behind Delphi as well.
Microsoft concentrated on bringing MVC-enabling classes for application development, and otherwise just basically wrapped the raw Windows API, whereas Borland actually created an honest-to-gosh object library to represent the GUI (and to an extent, provide a migration path from TurboVision, their DOS GUI library).
VCL was even thicker still, having come from Delphi's rather successful attempt to duplicate the Visual Basic VBX components in a highly-extensible and more native way.
Like you hint at, it was kind of a weird fit for C++Builder, since Object Pascal had somewhat different capabilities that they had to lay into CB making it a little bit of a bastardized spinoff of C/C++.
.NET/C# was ultimately what came out of that heritage, after MS poached Delphi's architect. I think it benefited from not claiming to be C++.
The Afx prefix comes from this library.
As per Wikipedia "Much of the .NET design, especially WinForms, is modeled after the VCL."
That sounds overly complex - all you need to do is to catch the listbox selection message, hide all the controls that shouldn't be shown with the current list selection, and show all the ones that should be. I don't see how a coordinate conversion would be needed for that.
I built the dialog using their tools, one for each "tab"
Then I attached that dialog on the space for it based on the selection, and the coordinate transformation (was a function in MFC)
Something like this http://stackoverflow.com/questions/2653130/how-to-get-size-a...
Hiding/showing might have been a good idea, but for more complex dialogs it wouldn't work
Motif made it better, but still.
My exercise for fellow developers joking about the Windows API was to ask them how they would do similarly in Xlib/Xt/Motif.
Windows programming was irrelevant in the 80s -- it wasn't until Windows 3 and 3.1 that it really caught on. In the DOS world there were lots of "character-mode windowing libraries" (I mean lots : I think we once reviewed 15 in a single issue of Computer Language) and they all generally had fairly straightforward programming models. People were much more focused on, for instance, memory management.
When NeXTSTEP came out, word traveled by the magazines and BBSes and Compuserve fora. You couldn't really communicate the NeXTSTEP programming model in pictures and so the only people who I think really got NeXTSTEP were people who'd already experienced Smalltalk! (Which, at the time, was at it's peak, with Smalltalk/V available on DOS machines and ParcPlace available on UNIX systems.)
I swear I muttered "OnOK" in my nightmares for years.
A simple program has a lot of advantages, it shows you can actually compile and run a program. When you have never done so before, there are 100s of hurdles that regular developers would not even consider when starting out.
http://man7.org/linux/man-pages/man2/signal.2.html
"Signals have been around since the 1970s Bell Labs Unix and have been more recently specified in the POSIX standard." --wikipedia
Basically, they're a software interrupt handler. Not too different from assembly. I don't see why that would be innovative at all?
In fact, early C++ just compiled to C. You can write structs with function pointers and vtables all in C if you wanted. People have been doing that since the 80s.
1.>'qsort' function takes a function pointer to decide how to compare your data in order to sort it, presumably using quick sort.
2.>'signal' function has a Posix origin, but it did manage to make it into the C standard. It takes a function pointer to decide which function to call on getting a particular signal.
Fun times.