Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Brings back horrid memories of learning Windows programming back in the mid nineties. From the very first line of code, Microsoft had you doing it their (nonstandard) way, making you use WinMain() instead of main(). That style, too... Yuck! Even today, I can somewhat reliably identify folks who were raised on the Windows SDK by looking for lpszHungarianNotation and UNREADABLEALLCAPSTYPEDEFS.

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?



I found it a lot easier to learn Win32 when I discovered that much of it was just MS' own styling and not actually necessary at all. A lot of the typedefs are artifacts from the Win16 days (e.g. you can just use a void* instead of LPVOID). This is a perfectly valid "Hello World" program, in a more traditional C style:

    #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.


My one development job involving Windows code was a cross-platform Windows / some-embedded-os-that-used-Win-32 / Linux app that ran an FTP server and SNMP server.

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.


That's very interesting.

Are there any tutorials/articles on how to do it?


I can't think of any on that specific subject off the top of my head, but here's some examples:

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.



After I spent $59.99 on Petzold's "Programming Windows 95", I ended up just using it as a reference and instead went through TheForger's (from #winprog) tutorial: http://www.winprog.org/tutorial/


> making you use WinMain() instead of main()

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.


That's a different thing, actually. There are two distinctions here:

- 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 used to have much disgust for hungarian notation. When I worked on my first big C++ codebase though, hungarian notation was standard, and I have to say, it made the code more readable. using 's' for static, 'm' for member, etc brought some extra clarity as to what was happening. It's not that we couldn't determine these thing for ourselves (VS has great code-crawling facilities), but its less time hovering, symbol searching, and jumping-to-declaration.

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!


I think you're mixing two absolutely different types of Hungarian Notation.

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).


iCounter always makes me wonder why someone would be iterating through counters! I've still got the:

    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.


There's a limited degree to which it's useful in resolving scope and type ambiguities in C or C++, but excessive use could definitely render things much less readable. It doesn't help that some of those letters survived long past their shelf life (ie. 'lp' for long pointer, where in non-segmented architectures there is no far (aka long) and near pointer distinction).

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.


Joel tells a pretty compelling story about two types of Hungarian notation: http://www.joelonsoftware.com/articles/Wrong.html


From that article:

"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...


's' for static, 'm' for member is not a Hungarian notation. It's just an established prefix patterns. Some style guides use '_' as a suffix for class member variables, and g_ prefix for globals. It's just a consistency. Hungarian notation is a nightmarish replacement of the intellisense such as lpszFoo.


Out of that I use m_ for members. But that's about it. It is very helpful when you read lengthy methods code. For actual names I use snake notation. I.e. something like

    int m_some_property;
Type in the name is definitely an overkill.


Type in the name is overkill for int or char, but it's useful when you've got multiple levels of pointers. ppszName is a pointer to a pointer to a null-terminated string, whereas pbFoo is a pointer to a byte-array. Both of these are void, but what* they point to is important.

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.


All of that can be checked in definition. So there is no need to do that but it can be convenient sometimes. It's a question of balancing how much redundant information you want to put in the name to save looking up the definition. If you deal with some pointer however you better look at definition anyway unless you want to risk getting in trouble, so there is no need to put any of that in the name.

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.


I have no strong opinion about hungarian notation, but I have noticed that hungarian notation haters were often english native speakers.


Please, stop mod me down ! In my work, I have to work with developers from many countries. Understanding cultural differences is very important when many nationalities are involved. There was nothing pejorative in this one.


IMHO, You're not contributing just offering anecdotal evidence that could easily be construed as an unsupported attack.

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).


I'll contribute something I noticed, as a native English speaker, when I started defaulting to too-terse in my own style: Instead of spending time worrying about how to capture enough meaning in a concise name, I can write "km", write a comment beside the declaration like "keyboard mapping", and I'm done. The symbol-meaning is mnemonic, the documentation is verbose.

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.


Your comment is an unsupported attack. Mine was just an observation based on many years of work in multicultural context.


Have you worked in a multi cultural environment to observe the difference of behaviour ?


Your stack, globals, and local heap were all allocated out of the same 64KB segment. Any locals in WinMain are effectively allocated for the entire lifetime of your app, and took up some of that limited space.

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.


I first got into programming when I was around 13. I wanted to make games (little did I know how big an order that was!). So eventually I stumbled upon code that looked a lot like this, and I started hacking it out. I would tweak things until I understood why it was there, and then move on. I guess since I didn't know that something better existed it was the only world I knew. I had a lot of fun playing around. What a different world we live in now though. Stuff that would have took me weeks to figure out can be done in 5 minutes today.


Stack was pretty small. Allocating structure on the heap instead of on the stack is pretty standard. We just need to understand the historic context before making judgement.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: