[HN Gopher] I built a native Windows Todo app in pure C (278 KB,...
       ___________________________________________________________________
        
       I built a native Windows Todo app in pure C (278 KB, no frameworks)
        
       Author : toxi360
       Score  : 234 points
       Date   : 2025-05-11 15:57 UTC (7 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | vardump wrote:
       | I think this Win32 app could be done in C under 20 kB.
        
         | iforgotpassword wrote:
         | Iirc mingw links its libc statically, that's probably why. Fwiw
         | I still remember that an empty VB6 project with one form
         | compiles to 20kb (16 if you select "optimize for size"). It
         | still needs its VM, which is a dll that was considerably
         | larger. I think about 1mb, but it's been a while.
        
           | sixtram wrote:
           | We shipped our VB6 software on a 1.44 MB floppy disk.
           | Actually, we had to add some random stuff to the installer to
           | make our software fit on two floppies instead of one, so that
           | customers would think our application was more complex. Our
           | CEO wanted to make sure that the end user had to eject disk 1
           | and insert disk 2 during the installation. Sometimes you want
           | to go big. So size matters.
        
             | exe34 wrote:
             | That's amazing! It reminds me of when a client once asked
             | to make the red more red. I asked him to look at it again
             | the next day and he said it was much better. I didn't have
             | to do anything.
        
             | SoftTalker wrote:
             | Similar to programs like TurboTax that add delays and
             | "working" animations to make the user think something
             | really complicated is being achieved.
        
         | chuckadams wrote:
         | I remember TheGun, a Win32 notepad app in 6K, written in MASM.
         | Anyone know where to find it?
        
       | formerly_proven wrote:
       | If you add a manifest you'll get post-Windows-2000 GUI styling.
        
         | hard_times wrote:
         | can you elaborate?
        
           | torstenvl wrote:
           | In my experience, the wxWidgets documentation and forums are
           | pretty good resources for Windows manifest files. YMMV.
           | 
           | An example from one of my projects:
           | https://pastebin.com/Jvjn5C6S
           | 
           | You need to reference it from your resource source like so:
           | https://pastebin.com/8FUi4tMz
           | 
           | And then compile that into an object file with windres:
           | x86_64-w64-mingw32-windres rsrc/metadata/windows.rc -o
           | winbuild/windowsrc.o
           | 
           | And link it with your project like you would any other object
           | file.
        
             | trinix912 wrote:
             | The entire structure is also documented on MSDN with all
             | possible values - https://learn.microsoft.com/en-
             | us/windows/win32/sbscs/applic....
             | 
             | While MSDN is a bit impractical to browse (there's simply
             | so much stuff in there) it's usually the best place to go
             | to for documentation when doing Windows dev.
        
               | torstenvl wrote:
               | Strongly disagree. There's way too much mystery and magic
               | number usage in the official documentation.
        
               | crimsontech wrote:
               | Can you recommend any good alternatives for someone
               | looking to learn programming using windows APIs?
        
           | fredoralive wrote:
           | As well as information about side by side assemblies (ie:
           | library versions), a Windows manifest file has various
           | settings and a declaration of compatible versions of Windows
           | that affect Windows' handling of the app, such as whether it
           | can handle paths over MAX_PATH, if it is hi-dpi aware, or if
           | it knows about themed controls.
           | 
           | https://learn.microsoft.com/en-
           | us/windows/win32/sbscs/applic...
           | 
           | If an EXE doesn't have a manifest file, Windows assumes that
           | it's ancient, so it falls back to conservative defaults like
           | ye olde USER controls to try and avoid breaking it.
        
         | MortyWaves wrote:
         | I'd be interested in seeing a PR for that. I've always found
         | the manifests a bit confusing.
        
         | layer8 wrote:
         | But why would you possibly want that? ;)
        
         | Dwedit wrote:
         | You can do it without a manifest too, it involves calling
         | "CreateActCtxA" and "ActivateActCtx" with the appropriate
         | Activation Context object. Manifest file is much easier.
        
       | vparikh wrote:
       | Looks like you are linking to static libraries. You should link
       | to DLL not to static libraries - this is will cut down on the
       | application size dramatically.
        
         | TonyTrapp wrote:
         | That seems backwards. If you need to ship the DLLs with the
         | program anyway (they are not part of the operating system,
         | after all), they will contain their full functionality, each
         | one potentially with its own C runtime, etc. If you statically
         | compile everything into a single EXE, there will be only a
         | single C runtime, all unused functions can be trivially
         | removed, etc.
         | 
         | DLLs only reduce size if their code is meant to be shared
         | between different programs.
        
           | throwanem wrote:
           | > they are not part of the operating system
           | 
           | Yes they are. Exercising the native Windows API is the entire
           | point of this project, and the only artifact it builds is an
           | executable.
           | 
           |  _edit:_ See the thread; I had the wrong end here. I haven 't
           | worked with Win32 or C in so long I'd forgotten what balls of
           | fishhooks and hair they both tend to be.
        
             | TonyTrapp wrote:
             | The CRT is not part of the operating system, unless you
             | count the UCRT on Windows 10 onwards (yes there is also a
             | MSVCRT copy in the Windows folder that Microsoft strongly
             | discourages you from using, so let's ignore that for now).
             | So unless you link against the system-provided UCRT, you
             | will have to either ship a dynamic or a static copy of the
             | C runtime, and linking it statically _will_ be smaller
             | because that will only contain the few string and time
             | functions used by the program, instead of the whole CRT.
        
               | throwanem wrote:
               | The idea that something which ships with the OS, as a
               | compatibility shim for legacy but still intentionally
               | supported applications, can be called "not part of the
               | OS," is religious. Strongly discouraged or otherwise, I
               | believe it to be in use here.
               | 
               | Not sure. I haven't really done anything serious with
               | MinGW, or Cygwin or even Windows really, in at least a
               | decade now. But there's not really a lot going on in the
               | build here, so I would imagine with your much more recent
               | experience you'd be better able to interpret what's
               | there.
               | 
               |  _edit:_ Wait, are you even discussing the old (okay,
               | ancient) Win32 API? I 'm confused, but as I said, it's
               | been a very long time since I attended the space.
        
               | TonyTrapp wrote:
               | The important thing in the context of the original
               | question is that mingw uses its own C runtime (MSVCRT is
               | old and incomplete - you would not be able to use it as a
               | drop-in replacement for a modern CRT). You can link the
               | mingw CRT statically or dynamically, and OP suggested
               | that linking it dynamically would reduce the size. But
               | that is only true for the main executable. You would
               | still have to ship the mingw CRT alongside, which in
               | return would increase the distribution size of the
               | program again.
               | 
               | I hope that clears my point up.
        
               | userbinator wrote:
               | MSVCRT works perfectly fine for something with this
               | feature set.
        
               | TonyTrapp wrote:
               | I really didn't want to get into the specifics, because
               | the situation is already complex enough without MSVCRT,
               | but: Yes, it is. If you wanted to do a size-optimzied
               | version of this particular program, it would be a good
               | enough choice.
        
               | TonyTrapp wrote:
               | Regarding your edit: the CRT is responsible for headers
               | such as time.h and string.h, which you can find being
               | used in todo.h. Their implementations are not provided by
               | the operating system but by the C runtime typically
               | supplied by the compiler (mingw in this particular case).
               | Other headers such as windows.h belong to the WinAPI, and
               | result in functions being imported from OS libraries such
               | as user32.dll. They are obviously not linked statically
               | into the program.
        
               | throwanem wrote:
               | Ugh, thanks. That clears it up.
               | 
               | Say what you like about the modern JS ecosystem, the
               | practical requirement of a single exhaustive declaration
               | of all external dependencies and their sources is not to
               | be sneered at.
        
               | tacker2000 wrote:
               | i get you, but this also introduces a whole other set of
               | issues, like "dependency hell", where if you update one
               | package you need to re-jig everything else. Things were
               | just moving much slower back "in the days"
        
               | throwanem wrote:
               | Dep version clashes were a problem until iirc some time
               | about mid-late last decade, say ~2017.
               | 
               | These days it's perfectly solved at some cost in storage
               | and memory, which in entire fairness are lately about as
               | cheap as any other resource. There is also lately a
               | movement in the space significantly away from "taking
               | dependencies," a phrase I quote to highlight its
               | pejorative connotation: presumptively undesirable,
               | however necessary.
               | 
               | If a large language model is satisfactorily efficient for
               | 2025, then to scruple over the disk space and RAM used in
               | the current model of exhaustive dependency resolution
               | employed by modern JS/TS package managers, seems
               | impossible to regard as other than ideologically
               | motivated.
               | 
               | In any case, I took some care earlier to avoid talking
               | beyond the package.json dependency _declaration_ model,
               | in hopes of forestalling precisely this bikeshed. If you
               | want to talk further about things that don 't interest me
               | in this context, please find someone else with whom to do
               | so.
               | 
               | (I don't wish to seem rude in this. Only that I have had
               | that discussion too many more times than I can count, and
               | I have long since stopped expecting it to vary noticeably
               | in a given iteration.)
        
               | jug wrote:
               | Why would you not link against the UCRT if you're
               | intentionally designing for Win32 though? It's a decade
               | old library by now. Time flies...
        
               | TonyTrapp wrote:
               | Some people value being able to support older OSes
               | without shipping the entire UCRT as an optional
               | component. Granted, this will become less and less
               | relevant in the future...
        
             | 90s_dev wrote:
             | You must not have worked with Win32 or C recently, they
             | both tend to be giant balls of fishhooks and hair.
             | 
             |  _edit:_ Saw that you just now edited your comment, glad we
             | 're on the same page now.
        
               | throwanem wrote:
               | Quite literally, it would seem! What an entirely
               | remarkable coincidence.
        
               | 90s_dev wrote:
               | Call it a coincidence all you want, we both know you just
               | copy/pasted my comment into your own edit.
        
               | throwanem wrote:
               | Bold, I'll give you that. Did we both get that from the
               | UNIX-HATERS Handbook? I believe it originally described
               | Perl, and not at all unjustly.
        
               | 90s_dev wrote:
               | Don't even have to go that far, it's word for word in
               | Perl's own documentation. They've become self aware at
               | some point it seems.
        
               | throwanem wrote:
               | And who could say it had been too long coming?
        
           | pjmlp wrote:
           | Not really, because this is Windows we are talking about.
           | 
           | A traditonal Windows application would be using the Windows
           | APIs, and not the C standard library, e.g. FillMemory()
           | instead of memset(), thus there is no DLLs to ship with the
           | application.
           | 
           | As can be seen on Petzold books examples.
        
             | TonyTrapp wrote:
             | This code is not from the Petzold books. It literally
             | includes and uses string.h, stdlib.h and time.h. It is
             | _not_ using WinAPI equivalents of C functions.
        
         | Dwedit wrote:
         | No, linking to a static version of the CRT is a good thing, it
         | cuts out the unused code. If you dynamic link to
         | MSVCRxx/VCRUNTIME, you force the user to download that exact
         | DLL from Microsoft. Dynamic linking to MSVCRT doesn't have that
         | problem, but it's very hard to do in Visual Studio.
         | 
         | The only time you really can't static link to the CRT is LGPL
         | compliance?
        
           | TonyTrapp wrote:
           | Note that the project is using mingw, so it's not using any
           | Microsoft DLLs for the CRT anyway. mingw brings its own CRT
           | along.
        
             | Dwedit wrote:
             | I thought Mingw defaulted to MSVCRT.DLL as the CRT?
        
               | TonyTrapp wrote:
               | It looks looks mingw actually has several options to base
               | its own CRT on (including MSVCRT, UCRT and various VS
               | runtimes). Still, in the context of OP's original post,
               | we are talking about the mingw DLLs that may or may not
               | redirect most of their duties to Microsoft DLLs,
               | depending on how mingw is configured.
        
       | AaronAPU wrote:
       | The 6502 programmer in me is dying inside that 278kb now passes
       | as lightweight.
        
         | nottorp wrote:
         | Maybe we could petition the demo scene competitions to have a
         | '64kb TODO app' category.
        
         | Borg3 wrote:
         | Hehe :) Okey.. I have sth easier to write.. but smaller:
         | 
         | 15kB quickrun.exe :) C, pure Win32 API.. No hacks to shrink
         | binary, Mingw32 compiler.
         | 
         | Its GUI app to quickly launch any application via alias.
        
         | jcelerier wrote:
         | A lot of it is due to the platform and executable format.
         | Things can be much more lightweight when there's no information
         | for stack traces, no dynamic linking infrastructure, no
         | exception handling tables (necessary even in C in case
         | exceptions traverse a c function,) etc.
        
           | userbinator wrote:
           | _no dynamic linking infrastructure_
           | 
           | You get that for free on Windows.
           | 
           |  _no exception handling tables (necessary even in C in case
           | exceptions traverse a c function,_
           | 
           | Not necessary if you're using pure C. SEH is rarely necessary
           | either.
        
         | kgabis wrote:
         | 6502? Luxury! In my times you were lucky to have a processor.
        
           | pineaux wrote:
           | A processor? Luuuxury! In my time we worked twenty-six hours
           | a day, did all the calculations with pen and paper and would
           | be thrilled to use an abacus!
        
             | psychoslave wrote:
             | Ah, back in my early existence, we didn't have time and all
             | these superficial dimensions. Ontological creation out of
             | nothing was all one needed, but it looks like it's all lost
             | art now.
        
             | p0w3n3d wrote:
             | you had paper? We had to use sand and sticks!
        
             | p0w3n3d wrote:
             | Btw. I love how you people refer to this sketch so
             | seamlessly!
        
         | mhd wrote:
         | This reminds me of the days when all of a sudden win32
         | programming in assembly became hip enough, probably as a
         | response to the increasing size of shareware downloads ('twas
         | the dark time of MFC).
         | 
         | Combined with early Palm Pilot 68k programming, those were the
         | last hurrahs of non-retrocomputing asm I can remember.
        
         | tonyarkles wrote:
         | That barely fits on a 5 1/4" floppy!
        
         | stevekemp wrote:
         | I'm spending some time this evening debugging a failure I have
         | with an emulator I've written - it emulates a system running a
         | Z80 processor with 64k of RAM.
         | 
         | Sometimes I too take a step back and look at the way things
         | have changed. But then again we've made a lot of progress for
         | the size-changes I guess.
        
         | jackjeff wrote:
         | I'm surprised it's that big to be honest. I was expecting it to
         | be smaller or half the size to be taken by some app icon. I
         | remember writing this kind of stuff back in the days and it was
         | smaller.
         | 
         | Is it due to MinGw maybe?
        
         | p0w3n3d wrote:
         | it's about ABI, also 8bit << ... << 64bit architecture. Every
         | pointer is 8 times longer. Don't complain, just admire. It's an
         | art.
        
         | abbeyj wrote:
         | I tried to reproduce this binary to see what the 278 KB was
         | being taken up by. The first obstacle that I ran into was that
         | the build.bat file doesn't work if you have git configured to
         | use core.autocrlf=false. Changing that to core.autocrlf=true
         | and recloning was sufficient to get me building.
         | 
         | I'm using x86_64-15.1.0-release-win32-seh-msvcrt-rt_v12-rev0.7z
         | from https://github.com/niXman/mingw-builds-
         | binaries/releases/tag... as the toolchain. This produces a 102
         | KB .exe file. Right off the bat we are doing much better than
         | the claimed 278 KB. Maybe the author is using a different
         | toolchain or different settings? Exact steps to reproduce would
         | be welcome.
         | 
         | We can improve this by passing some switches to GCC.
         | gcc -Os => 100 KB         gcc -Oz => 99 KB         gcc -flto =>
         | 101 KB         gcc -s => 51 KB         gcc -s -Oz -flto => 47
         | KB
         | 
         | If all you are interested in is a small .exe size, there is
         | plenty of room for improvement here.
        
           | jedimastert wrote:
           | > Maybe the author is using a different toolchain or
           | different settings?
           | 
           | I wonder if they are compiling with debugging symbols? I
           | don't know how much this would change things in vanilla C but
           | that would be my first guess
        
           | azhenley wrote:
           | If you had a blog or YouTube channel where you just went
           | around to open source projects optimizing them down, I'd be
           | very interested.
        
       | dvdkon wrote:
       | If you're going for a small EXE, I'd recommend telling GCC to
       | optimise for size with "-Os".
       | 
       | Link-Time Optimisation with "-flto" might also help, depending on
       | how the libraries were built.
        
         | RavSS wrote:
         | I'd suggest `-Oz` instead, as it optimises for size above all
         | else at the cost of performance, unlike `-Os` which is less
         | aggressive (but likely produces similar code anyway). `-Oz` is
         | somewhat new if I remember correctly, so it depends on the GCC
         | version.
        
       | slicktux wrote:
       | Simple and elegant!
        
       | Disposal8433 wrote:
       | > A modern, native Windows Todo application
       | 
       | What's modern about it? Also you could have used C++ instead to
       | remove some potential issues, and those global variables...
       | 
       | Use std::string and std::array or std::list, some anonymous
       | namespaces, remove all the malloc, etc. Your code would be half
       | the size and still compile to the same assembly language without
       | the bugs.
        
         | SoftTalker wrote:
         | It's not modern but there's some value to programming close to
         | the metal so you understand what's really going on. The problem
         | domain is simple and easy to keep in your head. Good learning
         | exercise. I wonder if there's an assembly language version of
         | something like this.
        
         | trinix912 wrote:
         | > Use std::string and std::array or std::list /.../
         | 
         | While nothing is modern about this approach, if we're going the
         | WINAPI route, there's very little to be gained by using
         | std::string instead of the LPWSTR that WINAPI offers (and plays
         | nicely with). I would definitely avoid plain C strings (char[])
         | but rather use the wide version (which is what LPWSTR is under
         | the hood). But for std::array or std::list, I don't see how the
         | codebase would vastly benefit from them.
        
           | pjmlp wrote:
           | In that case, make yourself a favour and use WIL, aka Windows
           | Implementation Library.
           | 
           | https://github.com/microsoft/wil
        
         | musjleman wrote:
         | > those global variables...
         | 
         | What about them? In a 500 loc app there is no practical
         | difference and there's only ~20 of them with clear purpose.
         | 
         | > Use std::string <...> or std::list <...> remove all the
         | malloc, etc
         | 
         | > still compile to the same assembly language without the bugs.
         | 
         | I see you have no clue what those things actually compile down
         | to.
        
         | pjmlp wrote:
         | To note that even the latest editions of the famous Petzold
         | book, before the C# edition, switched to using the common C
         | subset from C++ and compiling with Visual C++ in C++ mode,
         | instead of plain old C.
         | 
         | Already by Windows 95 timeframe, only C diehards would be
         | writing Windows applications still in C instead of VB, Delphi
         | and C++, as the more mainstream languages, there were obviously
         | other ones to chose from as well.
        
       | tippytippytango wrote:
       | The pedantry in the comments of a todo app is exquisite. HN never
       | disappoints.
       | 
       | Very nostalgic OP, warms my heart 10/10
        
       | broken_broken_ wrote:
       | I have done something similar for Linux under 2 KiB in assembly
       | some time ago: https://gaultier.github.io/blog/x11_x64.html
       | 
       | As others have said, doing so in pure C and linking dynamically,
       | you can easily remain under 20 KiB, at least on Linux, but
       | Windows should be even simpler since it ships with much more out
       | of the box as part of the OS.
       | 
       | In any event, I salute the effort! You can try the linking
       | options I mentioned at the end of my article, it should help
       | getting the size down.
        
       | toxi360 wrote:
       | Hello friends, I made this app just to try it out and have some
       | fun, haha, but the comments are right, something like this could
       | have been done more sensibly with C++ or other languages, ahaha.
        
         | toxi360 wrote:
         | https://github.com/Efeckc17/YoutubeGO By the way, you can also
         | review or examine this application, I would be very happy :D
        
         | drooopy wrote:
         | Unironically, I would rather use your to-do app over the
         | default Windows 11 one.
        
           | toxi360 wrote:
           | AHhhahah thanks
        
         | tomtomtom777 wrote:
         | This is exactly how I've learned to create my first Windows
         | programs about 30 years ago, except that I'd use a C++
         | compiler.
         | 
         | I am not sure why but I believe writing C style code with a C++
         | compiler was how the windows API was documented to be used. I
         | think Microsoft just went with the idea that C++ was an
         | improved superset of C so should be used even for C-style code.
        
         | kmangutov wrote:
         | This is exactly the sort of project (clean, native UI) that
         | motivated me to learn programming, kudos!
        
         | Johanx64 wrote:
         | It's just the way it should be.
         | 
         | Other language doesn't fundamentally change anything if you
         | want to use win32 API, if anything it would make things more
         | confusing.
         | 
         | People often fall prey to C++isms, and they would have made the
         | whole thing an even more confusing mess (to people not familiar
         | with win32 API).
         | 
         | This is a very cute thing to do and some familiarity with win32
         | APIs is a nice basic competency thing, regardless of what other
         | people think.
        
       | pcunite wrote:
       | Back in the day I used to use UPX to compress my executables to
       | achieve impressively small sizes.
        
         | jackjeff wrote:
         | I remember doing that for some custom installer I wrote. It
         | felt like a good idea for 5mins until it got flagged by a bunch
         | of anti virus software... had to sign the installer in the end
         | and spent a lot of time reporting false positives.
        
       | dorianmariecom wrote:
       | ready for enterprise todo
        
       | eviks wrote:
       | > no frameworks
       | 
       | Checks out: blurry fonts in scaled dpi, no Tab support, can't
       | Ctrl-A select text in text fields and do all the other stuff that
       | pre-modern frameworks offered you, errors on adding a row, ...
       | 
       | > modern
       | 
       | In what way?
        
         | userbinator wrote:
         | It's "modern" in that it's much bigger than necessary, while
         | missing a lot of functionality.
         | 
         | (A lot of what you mention is missing is trivial to add,
         | especially tabbing between controls.)
        
           | card_zero wrote:
           | I think font scaling is fixed (i.e. turned on) with
           | SetThreadDpiAwarenessContext(-4). Or whatever the constant
           | that equates to -4 is called.
        
         | Dwedit wrote:
         | Example of setting DPI awareness:
         | https://github.com/Dwedit/GameStretcher/blob/master/Stretche...
         | 
         | This code dynamically checks for and calls one of the
         | following: user32:SetProcessDpiAwarenessContext,
         | shcore:SetProcessDpiAwareness, then user32:SetProcessDPIAware.
         | If the Windows version is extremely old and doesn't implement
         | any of those (Windows XP or earlier), it won't call anything.
        
           | sargstuff wrote:
           | Ah isn't the user32:<windows api functions> a framework not
           | related to 'pure' C?
        
             | jchw wrote:
             | Arguably, Windows itself is an object oriented UI
             | framework.
        
             | ghewgill wrote:
             | The colons there don't represent C++. That's just a way of
             | referring to a windows API function that exists in a
             | specific DLL (in this case "user32"). Because the functions
             | used here do not exist in older versions of Windows, the
             | linked code dynamically loads user32.dll and tries to get
             | the address of those functions so they can be called.
             | That's why you need to know which Windows DLL they exist
             | in.
        
           | scq wrote:
           | You can also set it in the application manifest, which is
           | recommended over setting it programmatically:
           | https://learn.microsoft.com/en-
           | us/windows/win32/hidpi/settin...
        
       | thehias wrote:
       | 278kb? you are doing something very wrong, this should be
       | possible in 10kb!
        
         | musjleman wrote:
         | The actual code in the repo definitely compiles to less than
         | 10k. The rest is bloat from linking CRT statically.
        
       | ghewgill wrote:
       | I think the hard limit of 100 todos is the best feature of this.
       | Why don't other todo apps have this feature?
        
         | jonny_eh wrote:
         | Some of us have a lot to do!
        
         | userbinator wrote:
         | I think 127 would make more sense.
        
       | nu11ptr wrote:
       | This is a blast from the past and it is neat to see some bare
       | bones coding projects, but as others have said this is hardly
       | "modern".
        
       | userbinator wrote:
       | That's more than 10x bigger than I expected, given that all it
       | seems to do is manipulate a list view. Something like this should
       | be doable in under 10KB.
        
       | electroly wrote:
       | Instead of laboriously calling CreateWindow() for every control,
       | traditionally we would lay out a dialog resource in a .rc file
       | (Visual Studio still has the dialog editor to do it visually) and
       | then use CreateDialog() instead of CreateWindow(). This will
       | create all the controls for you. Add an application manifest and
       | you can get modern UI styling and high-DPI support.
        
         | urbandw311er wrote:
         | Great answer, helpful and not judgemental.
        
         | belter wrote:
         | Look it up in Petzold they used to say...
        
         | userbinator wrote:
         | You also get automatic tabbing between controls, and a few
         | other keyboard shortcuts this way. Note that resizing them
         | still needs to be done manually if you want that, but that's
         | usually easy and not more than a few hundred bytes of code.
        
       | p0w3n3d wrote:
       | Great respect! I've tried many times, without final result. I'll
       | try to use this for learning purposes!
       | 
       | Btw. I like how Inno Setup used some very old Delphi 2 compiler
       | to create exe so small it would fit without breaking the zip
       | compliancy. I read it somewhere 10+ years ago, so not sure if
       | this is still the case, but still. And the initial dialog was
       | done in pure winapi.h (of course it was winapi.pas which made
       | everything more difficult for me to learn from)
        
       | hudo wrote:
       | Less LOC than React/Redux app... Makes you think, what were we
       | doing last 30 years :/
        
         | Kwpolska wrote:
         | I'm pretty sure an app as mediocre as this would take up less
         | code in React, or even plain JavaScript. The UI is a single
         | table and a few inputs and buttons, and its main way of
         | communicating with the outside world is message boxes - trivial
         | to do in a web browser.
        
       | keepamovin wrote:
       | Interesting! I was just looking at raylib for this kind of thing:
       | super light weight, cross platform, reliable, ideally C-based
       | method to get GUI.
       | 
       | Raylib and raygui is truly incredible from my point of view. I
       | succeeded in getting the macOS and Windows builds going on a
       | bunch of cute little novel (not stock standard in the repo)
       | examples in a matter of hours with AI help. I'm inspired by all I
       | can do with this.
       | 
       | For ages I felt "cut off" from the world of Desktop GUI because
       | it was so verbose, and had high friction - need a bunch of
       | tooling, set up, and so on. And then everything was fragile. I
       | like to work quickly, with feedback, and PoCs and results. I
       | think in raylib I have found a method that really achieves this.
       | For instance, check out this tiny little "text_input.c"
       | #define RAYGUI_IMPLEMENTATION       #include <raylib.h>
       | #include "deps/raygui.h"            #define WINDOW_WIDTH 800
       | #define WINDOW_HEIGHT 600       #define MAX_INPUT_CHARS 32
       | int main(void) {           InitWindow(WINDOW_WIDTH,
       | WINDOW_HEIGHT, "Text Input Demo");           SetTargetFPS(60);
       | // Load a larger font for better text appearance           Font
       | font = LoadFontEx("resources/fonts/arial.ttf", 32, 0, 0);
       | if (font.texture.id == 0) {               font =
       | GetFontDefault(); // Fallback to default font if loading fails
       | }                // Set the font for raygui controls
       | GuiSetFont(font);                // Customize raygui styles
       | (using BGR order for hex values)           GuiSetStyle(BUTTON,
       | TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);           GuiSetStyle(BUTTON,
       | BASE_COLOR_NORMAL, 0x50AF4CFF);  // Green (B=80, G=175, R=76,
       | A=255) ,Ui R=76, G=175, B=80           GuiSetStyle(BUTTON,
       | TEXT_COLOR_NORMAL, 0xFFFFFFFF);  // White text
       | GuiSetStyle(BUTTON, BASE_COLOR_PRESSED, 0x6ABB66FF); // Lighter
       | green           GuiSetStyle(BUTTON, TEXT_COLOR_PRESSED,
       | 0xFFFFFFFF);           GuiSetStyle(BUTTON, BASE_COLOR_FOCUSED,
       | 0x84C781FF); // Hover color           GuiSetStyle(BUTTON,
       | TEXT_COLOR_FOCUSED, 0xFFFFFFFF);           GuiSetStyle(BUTTON,
       | BORDER_WIDTH, 2);           GuiSetStyle(BUTTON,
       | BORDER_COLOR_NORMAL, 0x327D2EFF); // Dark green border
       | // Adjust font size for raygui controls (optional, since font is
       | already 32pt)           GuiSetStyle(DEFAULT, TEXT_SIZE, 20); //
       | Slightly smaller for button and text box to fit better
       | GuiSetStyle(DEFAULT, TEXT_SPACING, 1);                char
       | inputText[MAX_INPUT_CHARS + 1] = "\0"; // Buffer for text input
       | bool textBoxEditMode = false; // Tracks if the text box is being
       | edited           bool messageSubmitted = false; // Tracks if a
       | message has been submitted           float effectTimer = 0.0f; //
       | Timer for the flash effect           const float effectDuration =
       | 0.5f; // Flash duration in seconds                while
       | (!WindowShouldClose()) {               // Update effect timer
       | if (effectTimer > 0) {                   effectTimer -=
       | GetFrameTime();               }
       | BeginDrawing();               // Set background color based on
       | effect               if (effectTimer > 0) {
       | ClearBackground((Color){ 255, 255, 150, 255 }); // Yellow flash
       | (RGB order)               } else {
       | ClearBackground(RAYWHITE);               }                    //
       | Center the text box and button               int textBoxWidth =
       | 200;               int textBoxHeight = 40;               int
       | buttonWidth = 120;               int buttonHeight = 40;
       | int textBoxX = (WINDOW_WIDTH - textBoxWidth) / 2;
       | int textBoxY = (WINDOW_HEIGHT - textBoxHeight) / 2 - 40;
       | int buttonX = (WINDOW_WIDTH - buttonWidth) / 2;               int
       | buttonY = textBoxY + textBoxHeight + 10;                    //
       | Draw the text box               if (GuiTextBox((Rectangle){
       | (float)textBoxX, (float)textBoxY, textBoxWidth, textBoxHeight },
       | inputText, MAX_INPUT_CHARS, textBoxEditMode)) {
       | textBoxEditMode = !textBoxEditMode; // Toggle edit mode on click
       | }                    // Draw the button               if
       | (GuiButton((Rectangle){ (float)buttonX, (float)buttonY,
       | buttonWidth, buttonHeight }, "Submit")) {
       | messageSubmitted = true;                   effectTimer =
       | effectDuration; // Start the flash effect
       | TraceLog(LOG_INFO, "Message submitted: %s", inputText);
       | }                    // Display the submitted message
       | if (messageSubmitted && inputText[0] != '\0') {
       | const char *label = "Message: ";                   char
       | displayText[256];                   snprintf(displayText,
       | sizeof(displayText), "%s%s", label, inputText);
       | int textWidth = MeasureTextEx(font, displayText, 32, 1).x;
       | int textX = (WINDOW_WIDTH - textWidth) / 2;                   int
       | textY = buttonY + buttonHeight + 20;
       | DrawTextEx(font, displayText, (Vector2){ (float)textX,
       | (float)textY }, 32, 1, (Color){ 33, 150, 243, 255 }); // Bright
       | blue (RGB order)               }                    EndDrawing();
       | }                UnloadFont(font);           CloseWindow();
       | return 0;       }
       | 
       | I love it! I feel unleashed again to program in graphics and
       | games and real GUI! The first real paid programming job I had was
       | using a lot of ps5 in Java and JavaScript (Open Processing) and I
       | dug it! :)
       | 
       | And the file sizes are sweet (to me):
       | 
       | - macOS: text_input - 123736
       | 
       | - Windows: text_input.exe - 538909
       | 
       | Two dependencies to distribute with on Windows: glfw3.dll and
       | libraylib.dll (322K and 2.1MB respectively)
       | 
       | Raylib was built to make game programming fun. And maybe I will
       | use it for that! :) But right now I want to use it for GUI. The
       | issue with Qt and others, is while I like the idea of standard-
       | Andy controls, I don't want to pay a commercial license - when I
       | figure "it can't be _that_ hard to get what I want " - as I plan
       | to use this stuff for commercial/proprietary control-panes and
       | layers on my existing products: BrowserBox, DiskerNet, and more.
       | 
       | At the same time I really respect what Qt have done growing their
       | business and might be inspired or even emulate some of their
       | model myself in my business.
        
       | bitwize wrote:
       | Hell to the yes!                   int PASCAL WinMain(HINSTANCE
       | hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int
       | nCmdShow);
       | 
       | If you know, you know.
       | 
       | I'm to understand that entire divisions of Microsoft itself no
       | longer know how to code anywhere near this level, which is why
       | many of their flagship applications (looking at you, Microsoft
       | Teams (work or school)) are Electron monstrosities you can watch
       | draw themselves like Windows 1.0 apps -- on modern multicore
       | hardware.
       | 
       | EDIT: more correct function sig
        
         | saurik wrote:
         | I'm apparently so ancient I am just looking at that line of
         | code thinking "isn't that the standard entrypoint for a Windows
         | app?", lol; is there something else to know? ;P Oh! I guess,
         | staring at it, isn't that wrong?... if you use LPCTSTR, one
         | would expect that you are trying to be agnostic to UNICODE, but
         | then you also have to use _tWinMain, no? (And, after
         | considering that, I went and double-checked, and the C is also
         | incorrect in this context.)
        
       | mtlynch wrote:
       | Thanks for sharing, OP!
       | 
       | It's probably too late, but this qualifies for "Show HN" if you
       | update the title to have the prefix "Show HN: ".[0]
       | 
       | I think the size of the source is actually more impressive than
       | the size of the binary. I'm impressed that you can implement the
       | whole thing in what looks like about 1 KLOC in just four .c
       | files.
       | 
       | [0] https://news.ycombinator.com/showhn.html
        
       | _bin_ wrote:
       | Fun project! I think the smallest I ever shrunk a win32
       | application was on the order of 2-4kb by writing in ASM. It was a
       | great illustration of why 10x binary size is actually a great
       | trade-off in terms of productivity.
        
       | pshirshov wrote:
       | If only I can do the same across 3 desktop and two phone
       | platforms...
        
       | fizlebit wrote:
       | Me no like inconsistent use of spaces.
       | x += labelW+20;                 hDescEdit =
       | createModernEdit(hwnd, x, y, editW, btnH, ID_DESC_EDIT);
       | x += editW + gap;
       | 
       | What no clang-format or equiv in 1990?
        
       | rfl890 wrote:
       | There's no application manifest for the common controls so it
       | will look outdated
        
       | nsxwolf wrote:
       | The Readme emojis tell me this was vibe coded.
        
       | dochtman wrote:
       | Curious what this would look like written in Rust, using the
       | windows-rs bindings.
        
       | masternight wrote:
       | There is something I like about win32 gui programming. It's a
       | little idiosyncratic, but if you read Raymond Chen's blog you'll
       | see why.
       | 
       | The win32 API has its origins on the 8088 processor and doing
       | things a certain way results in saving 40 bytes of code or uses
       | one less register or something.
       | 
       | I wrote a lot of toy gui apps using mingw and Petzold's book back
       | in the day. Writing custom controls, drawing graphics and text,
       | handling scrolling, hit testing etc was all a lot of fun.
       | 
       | I see in your app you're using strcpy, sprintf. Any kind of
       | serious programming you should be using the length-checked
       | variants. I'm surprised the compiler didn't spew.
       | 
       | You'll also find that the Win32 API has a lot of replacements for
       | what's in the C standard library. If you really want to try and
       | get the executable size down, see if you can write your app using
       | only <Windows.h> and no cstdlib. Instead of memset() you've got
       | ZeroMemory(), instead of memcpy() you've got CopyMemory().
       | 
       | At some point writing raw C code becomes painful. Still, I think
       | doing your first few attempts in raw C is the best way to learn.
       | Managing all the minutiae gives you a great sense of what's going
       | on while you're learning.
       | 
       | If you want to play more with win32 gui programming, I'd have a
       | look at the WTL (Windows Template Library). It's a C++ wrapper
       | around the win32 API and makes it much easier to reason about
       | what's going on.
        
         | userbinator wrote:
         | _Instead of memset() you 've got ZeroMemory(), instead of
         | memcpy() you've got CopyMemory()._
         | 
         | I believe MSVC intrinsics will use the rep stos/movs
         | instructions, which are even smaller than calling functions
         | (which includes the size of their import table entries too.)
        
         | rcarmo wrote:
         | I spent a lot of time doing that and to be honest, I miss the
         | ability to develop for native UIs with native code.
        
         | scripturial wrote:
         | At minimum, these days, if you dont use strncpy instead of
         | strcpy, you'll have to suffer through every man and his dog
         | forever telling you to do otherwise. (For me this is one of the
         | main arguments of using zig, a lot of these common pitfalls are
         | minimized by using zig, but c is fine as well)
        
           | masternight wrote:
           | Heh, and if you use strncpy() you'll have to suffer through
           | me lecturing you on why strncpy() is the wrong function to
           | use as well.
        
       | burnt-resistor wrote:
       | Contains numerous memory leaks, doesn't permit arbitrarily long
       | lists, and saves and restores uninitialized data. Really sloppy.
        
       | transcriptase wrote:
       | Seeing a lot of chirps in here from people who work on software
       | or websites that load megabytes of JS or C# or in order to send
       | 278kb of telemetry every time the user moves their mouse.
        
       | scripturial wrote:
       | The allure of the perfect notes and todo app. Having gone through
       | phases of various modern todo and note apps over the years, I've
       | finally let it go and decided to embrace just using text files.
       | (Neovim for me, not that it matters which text editor one uses)
       | 
       | It's not that there aren't cool apps for this stuff, it's more
       | that I have a trail of data across various todo and notes apps
       | from years of different tools.
       | 
       | One solution to the problem of making things "feel native" is to
       | go all in on letting go of native. Target a different style, be
       | it minimalism, Commodore 64, pixel art, etc... it can be fun that
       | way, especially if it's mostly just a tool for you.
        
       ___________________________________________________________________
       (page generated 2025-05-11 23:00 UTC)