WinUtils: shell-powered CLI tools for Windows 95
WinUtils: shell-powered CLI tools for Windows 95
github.com/codenaked/winutils<br>WinUtils started in 1996-1997 as a way to build my programming chops. I was poking<br>around the Windows 95 shell APIs, found the file operation functions, and thought<br>it would be cool to have CLI tools that called them instead of doing raw file I/O.<br>The payoff was practical: because the operations went through the shell, the same<br>confirmation prompts, progress dialogs, and Recycle Bin behavior you got from<br>Windows Explorer came along for free.
WinCopy
WinDel
WinMove
WinRen
The shell APIs behind it<br>The interesting part of the project, at least to me at the time, was that almost<br>none of the heavy lifting was mine. The shell already knew how to copy, move,<br>delete, and rename files the way users expected. WinUtils was mostly a thin CLI<br>front end for those calls:
SHFileOperation: the workhorse for copy, move, delete, and rename, including Recycle Bin support via FOF_ALLOWUNDO
SHFILEOPSTRUCT: the struct that configures the operation, source/destination paths, and flags
FO_COPY / FO_DELETE / FO_MOVE / FO_RENAME: the operation codes used by each utility
SHGetFileInfo: handy for grabbing icons and display names that matched what Explorer was showing
Routing through SHFileOperation is what gave the tools their best property: undo.<br>Files removed by windel landed in the Recycle Bin, and a misfired winmove could<br>be reversed from Explorer like any other shell action.<br>Usage and switches<br>Each utility took its source/target arguments first and then any number of single-letter<br>switches prefixed with /. The switches mapped almost directly to SHFileOperation<br>flags, plus a couple of conveniences for help and an About dialog.<br>wincopy [/a /d /h /n /r /s /u]<br>winmove [/a /d /h /n /r /s /u]<br>windel [/a /d /h /n /s /u]<br>winren [/a /h /r /u]<br>Common switches:
/a: show the About dialog
/h: open winutils.hlp
/d: include directories (recurse) instead of the default files-only behavior (FOF_FILESONLY)
/n: answer yes to all prompts (FOF_NOCONFIRMATION)
/r: rename on collision (FOF_RENAMEONCOLLISION)
/s: silent, no progress dialog (FOF_SILENT)
/u: allow undo via the Recycle Bin (FOF_ALLOWUNDO)
windel skips /r since there is no target to collide with, and winren keeps<br>only the switches that make sense for a rename: /a, /h, /r, and /u.<br>About those icons<br>At the time, even getting a custom icon onto an executable felt like a real<br>accomplishment. In reality I mostly cribbed pieces of existing Windows icons in<br>an icon editor and chopped them together until each utility had something that<br>looked at least passably its own. Not original art, but it gave each tool an<br>identity, and putting that final .ico on the EXE was the moment the program<br>felt finished.
And the help file<br>The /h switch opened winutils.hlp, and getting that file built was almost a<br>side project on its own. Windows Help in that era was not just a text file you<br>shipped. You wrote topics in RTF, defined a project in an .HPJ file, set up a<br>contents file (.CNT), and then ran the Help Compiler (HCW / HCRTF) to turn<br>all of it into a single .HLP binary. Cross-references, popup definitions, and<br>jumps were tagged with footnote codes inside the RTF, which meant most of the<br>work happened in Word with a very specific set of conventions.<br>None of it was forgiving. A wrong footnote or a mismatched context string would<br>silently open the wrong topic, and for a set of utilities this small, the help<br>project was one of the fiddliest parts of the whole release.<br>Getting it out there<br>There was no app store, no GitHub Releases, no npm publish. Distribution meant<br>zipping up the EXEs and the help file, writing a short description, and uploading<br>it to whatever shareware sites would take it: Tucows, Download.com, WinSite,<br>ZDNet, Simtel, and a long tail of smaller archives. Each one had its own<br>submission form and its own pace for actually listing your upload, sometimes days<br>or weeks later.<br>The feedback loop was checking back. I would dial in, click through to each<br>listing, and look at the download counter. Just a number that ticked up over time.<br>Every tick was a little microdose of joy: someone, somewhere, was actually running<br>my code.<br>Why now<br>I saw Coreutils for Windows<br>on Hacker News and it reminded me of this old project. I went digging to see<br>if I still had the original files. Turns out I did, so I pushed them up to<br>GitHub and wrote this down.<br>Worth the pain<br>An LLM could probably reproduce all of WinUtils in a few minutes today. Source,<br>switches, help file, icons, the whole thing. And that is fine. It lowers the floor<br>for getting something working.<br>But the pain was the point for me back then. Reading shell API docs on paper,<br>guessing at flags, fighting the help compiler, hand-tweaking icons one pixel at a<br>time, and waiting for a full rebuild to see if anything changed. None of it was<br>efficient, but every bit of it taught me something I still rely on.<br>~codenaked