Skip to content

Releases: UberGuidoZ/Simple_Launcher

Simple Launcher v2.18

22 Mar 05:59
04f166f

Choose a tag to compare

Bug Fixes

  • Fixed DrawDropLine fallback Y coordinate being wrong for any dropIdx > 1. The previous fallback always stopped at the first visible button, drawing the drop indicator at the wrong position. The fallback now walks backward from the last visible button to find the correct slot boundary.
  • Fixed GetBasePath off-by-one in the .ini extension boundary check. The guard (dot - g_basePath) + 5 < MAX_PATH was one position too strict and silently dropped the .ini extension when the path filled the buffer exactly. Corrected to <= MAX_PATH.
  • Fixed SaveAll leaving a .tmp file behind when MoveFileEx fails. The return value was previously ignored; on failure the orphaned temp file is now removed with DeleteFile(tmpPath) so it does not accumulate on disk.

Security

  • Fixed LoadIconForSlot applying no UNC/device path guard to the iconPath field. A malicious or corrupted INI could specify \evil.server\share\icon.ico and trigger an outbound SMB connection or object-manager side-effect. iconPath now rejects any path beginning with \ or //. The path field (target executable) is intentionally not restricted, as users may legitimately launch programs from network shares.

Optimizations

  • Fixed ExpandEnvVars DWORD underflow: when dstSize == 0, the fallback strncpy(dst, src, dstSize - 1) wraps to 0xFFFFFFFF, causing a potential multi-gigabyte overrun. A zero-size guard is now added at entry.
  • Fixed tooltip builder launchMode == 2 branch missing pos +=. The launchMode == 1 branch correctly accumulated the snprintf return value; the == 2 branch discarded it, leaving pos stale - inconsistent and fragile if the block is extended.

Simple Launcher v2.17

21 Mar 07:05
19bcdc4

Choose a tag to compare

New Feature

  • Export Buttons saves the current button list (Name, Path, Args, WorkDir, IconPath, Admin, Separator, IsCategory, ShowIcon, LaunchMode) to a user-chosen .ini file using the same [Button1]..[ButtonN] section format as a profile INI, but without the [Settings] block so it's safe to share across profiles.
  • Import Buttons reads that file and appends the buttons after any already in the active profile, skipping entries that would exceed MAX_BUTTONS and reporting the count on completion.
  • Both options appear nested under the Profiles popup menu

Removed Open INI option as it only worked on the default INI.

Simple Launcher v2.16

21 Mar 06:36
8c8eead

Choose a tag to compare

Bug fixes

  • Icons on buttons below a duplicated entry were silently dropped; the shift loop in IDM_DUPLICATE_BTN now carries g_icons[i-1] forward correctly.
  • The separator and category branches in IDC_OK now explicitly zero launchMode, preventing a stale value from leaking if the slot previously held a normal button.
  • SwitchProfile now cancels any in-flight drag and releases mouse capture before tearing down the old layout, preventing a phantom BN_CLICKED from firing on the new profile's buttons.
  • The freed tail slot after a delete is now memset to zero, so no stale isSeparator, isCategory, or other fields linger beyond g_count.

Security

  • WriteEscaped now also strips ] in addition to \r, \n, and [, fully closing the section-header injection vector for INI readers that treat an unmatched bracket as a section boundary.
  • A comment was added at the ShellExecute call site noting that the "open" verb accepts script types (.bat, .ps1, .vbs, etc.) and that the INI should be stored in a user-controlled location only.

Optimizations

  • CreatePen/DeleteObject inside DrawDropLine (called twice per WM_MOUSEMOVE during drag) is replaced with a cached g_hpenDragLine managed alongside the other GDI objects in EnsureDarkGDI/FreeDarkGDI.
  • Three speculative EnsureDarkGDI calls inside DrawButton were removed; ApplyDarkBackground now calls EnsureDarkGDI unconditionally so light-mode pens and category brushes are always ready before any paint occurs.
  • Two cursor handles (g_curArrow, g_curSizeNS) are loaded once at startup and reused in WM_MOUSEMOVE, WM_LBUTTONUP, and WM_CAPTURECHANGED instead of calling LoadCursor on every message.
  • InitCommonControls was replaced with InitCommonControlsEx using ICC_STANDARD_CLASSES | ICC_BAR_CLASSES, which is the correct forward-compatible call and avoids linker warnings on modern SDKs.

Simple Launcher v2.15

21 Mar 06:04
421a385

Choose a tag to compare

New Feature

  • Drag-and-drop button reordering in the normal list view

Simple Launcher v2.14

20 Mar 07:05
f815294

Choose a tag to compare

Bug fixes

  • The main GetMessage loop condition was changed from != 0 to > 0, so a -1 fatal error return now exits cleanly rather than spinning indefinitely.
  • A WriteINIBody(FILE *f) helper was extracted from SaveAll, replacing the two identical write bodies that were introduced in v2.13. Both the direct-write and the atomic-temp-file paths now call this single function, so any future field change only needs to be made in one place.

Optimization

  • Three cached brushes (g_hbrDkCatBg, g_hbrLtCatBg, g_hbrLtCatPre) were added to EnsureDarkGDI / FreeDarkGDI. DrawButton now reads from these cached brushes for all four category header states instead of calling CreateSolidBrush / DeleteObject on every paint. EnsureDarkGDI is also now called unconditionally at startup so the light-mode category brushes are available even when dark mode is off.

Simple Launcher v2.13

20 Mar 06:54
5f54941

Choose a tag to compare

Bug fixes

  • LoadButtonIcons() is now called explicitly in SwitchProfile after LoadButtons(), fixing a regression where switching profiles left all icons blank because RefreshMainWindow never loaded them as the comment claimed.
  • g_filterTextLower is now zeroed alongside g_filterText in SwitchProfile, keeping the two lowercase filter caches in sync after every profile switch.
  • SaveAll now checks that strlen(g_iniPath) + 5 < MAX_PATH before building the .tmp path; when the path is too long for the suffix, it falls back to a direct overwrite rather than silently truncating the temp filename and potentially clobbering an unrelated file.

Security patch

  • CoInitializeEx(COINIT_APARTMENTTHREADED) is now called at startup and CoUninitialize is called on exit, satisfying the COM apartment requirement that SHBrowseForFolder with BIF_NEWDIALOGSTYLE depends on.

Optimization

  • Added g_hbrDkMenuHot to the EnsureDarkGDI / FreeDarkGDI cache. The WM_DRAWITEM menu hover handler now reads from this cached brush instead of calling CreateSolidBrush / DeleteObject on every mouse movement over a menu item.

Simple Launcher v2.12

20 Mar 06:41
639e79a

Choose a tag to compare

Bug fixes

  • Removed the redundant SaveAll() from IDM_TRAY_EXIT -- the tray exit path had the same double-save problem as the normal close path, with WM_DESTROY already calling SaveAll().
  • Added a g_count < 0 clamp in LoadButtons so a hand-edited Count=-1 in the INI cannot make button indices wrap negative and corrupt all move/delete/duplicate operations.
  • Added a null check on g_hwndMain immediately after CreateWindow in WinMain, returning early instead of crashing when the window cannot be created due to resource exhaustion.
  • Zero-initialized MSG msg in the main loop so msg.wParam is not garbage if GetMessage returns -1 on a fatal error.

Optimizations

  • Added a g_filterTextLower[256] cache updated once per keystroke in the EN_CHANGE handler. ButtonMatchesFilter now reads from it directly, eliminating the repeated per-button lowercasing of the same filter string on every keystroke.
  • The separator background fill in DrawButton now reuses the cached g_hbrDkBg brush for dark mode and the stock COLOR_BTNFACE brush for light mode, removing the CreateSolidBrush / DeleteObject pair that fired on every separator repaint.
  • Added g_hbrDkMenuBg to the EnsureDarkGDI / FreeDarkGDI cache. RepaintMenuBar now uses this cached brush instead of allocating and freeing one on every WM_NCPAINT and WM_NCACTIVATE call.

Simple Launcher v2.11

20 Mar 06:27
6a678e1

Choose a tag to compare

Bug fixes

  • Removed the redundant SaveAll() from WM_CLOSE; WM_DESTROY already calls it, so the file was being written twice on every normal exit.
  • SaveAll now writes to .tmp and atomically renames it with MoveFileEx, so a crash mid-write can no longer corrupt or empty the INI.
  • MSG pmsg is now zero-initialized so the WM_QUIT re-post check after the prompt loop cannot read an uninitialized value.
  • The boundary check in GetBasePath was changed from <= MAX_PATH to < MAX_PATH, closing a one-byte overrun when the path is exactly MAX_PATH - 5 chars.
  • The admin border pen is now cached in g_hpenAdminBorder and only recreated when g_adminColor changes, stopping the per-paint GDI alloc/free churn and associated flicker.

Security patches

  • ID_OPEN_INI now expands %SystemRoot%\system32\notepad.exe before calling ShellExecute, preventing a rogue notepad.exe on %PATH% from being picked up instead.
  • The profile name sanitizer now rejects all bytes outside printable ASCII (32-126), closing a gap where high-byte multi-byte sequences could survive truncation and be misinterpreted by Windows ANSI file APIs.
  • WriteEscaped now also replaces [ with a space, preventing a button name or path containing [SectionName] from malforming the INI structure.

Optimizations

  • Already in place via the existing ApplyFilter() path -- filter keystrokes reposition existing HWNDs rather than destroying and recreating them, so no additional change was needed.
  • Duplicated icon-loading logic was consolidated into a single LoadIconForSlot(int i) helper called by both LoadSingleButtonIcon and LoadButtonIcons.
  • A g_buttonNamesLower[MAX_BUTTONS][256] cache is rebuilt once per layout pass in RebuildFilterCache(); ButtonMatchesFilter now reads directly from it instead of re-lowercasing every button name on every keystroke.
  • The GetSystemMetrics calls in LoadSettings were already inside the if (g_winX != -1 && g_winY != -1) guard in the original code, so no change was required.

Simple Launcher v2.10

17 Mar 04:21
d86eeb7

Choose a tag to compare

Bug Fixes

  1. Category headers now correctly remember whether they were expanded or collapsed after you delete, move, or duplicate a button near them. Previously they could silently swap states.
  2. If a dialog window somehow fails to open (e.g. the system is low on resources), the app no longer crashes. It now checks before trying to use the window handle.
  3. If you close the app while the "New Profile" name prompt is open, the app now actually exits instead of becoming a ghost process with no visible window.
  4. A minor edge case where building the settings file path could theoretically write one byte past a buffer boundary has been closed off.

Security Fixes

  1. Button names, paths, and other text fields are now sanitized before being written to the INI file. Previously, a crafty value containing a newline could inject fake sections into the config file and silently corrupt settings.
  2. Profile names are now more strictly validated. Control characters and dots are rejected, so you can no longer accidentally create a profile whose file path resolves to something unexpected.

Performance

  1. Typing in the search bar no longer causes the whole button list to flicker. Previously every keypress destroyed and rebuilt all button windows from scratch; now it just shows and hides them in place.
  2. The colored brushes and pens used to draw buttons in dark mode are now created once and reused, instead of being created and thrown away hundreds of times per repaint.
  3. Adding, editing, or duplicating a button now only reloads the icon for that one button, instead of reloading all 64 icons every time.

Code Quality

  1. Two Windows API magic numbers (20, 19, and 0x0443) have been replaced with properly named constants and explanatory comments so the intent is clear to anyone reading the code later.
  2. All remaining sprintf calls have been replaced with the safer snprintf, consistent with the rest of the file.

Simple Launcher v2.00

15 Mar 05:52
3ad3bb3

Choose a tag to compare

Additions / Changelog

Profiles: a full multi-INI profile system, identical in design to my Simple Typer's. Default always maps to launcher.ini; named profiles are launcher_.ini files in the same directory. A [Meta] ActiveProfile key in the base INI persists the active profile across restarts. The "Profiles" menu item appears in the menu bar and in the tray right-click. New/Delete/Switch all work with the same duplicate-name check and safe-character filtering proven in simple_typer.

Environment variables: ExpandEnvVars() wraps ExpandEnvironmentStrings() with a safe fallback. It's called at launch time on path, args, and working directory — so %USERPROFILE%, %APPDATA%, %PROGRAMFILES% etc. all work. The raw (unexpanded) value is still what's stored in the INI and shown in the Edit dialog.

Launch mode: a new "Launch mode" dropdown in the Add/Edit dialog with three options: Normal (SW_SHOW), Minimized (SW_SHOWMINIMIZED), and Hidden (SW_HIDE). Useful for background scripts, batch files, or anything that should start silently. Saved as LaunchMode= in the INI, shown in the hover tooltip.