Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save aont/f7fecc0a1adf97212b68bbc2f6730710 to your computer and use it in GitHub Desktop.

Select an option

Save aont/f7fecc0a1adf97212b68bbc2f6730710 to your computer and use it in GitHub Desktop.

A Tiny Windows Helper: Skipping argv[0] Without the CRT

If you’re writing ultra-small Windows utilities—like tiny launchers or wrappers—you may want to avoid both the C runtime (CRT) and the C++ runtime. That means no printf, no strlen, and no mainCRTStartup scaffolding. The snippet below shows a practical pattern: advance a command-line pointer past argv[0] and then print the progressively “shifted” command line, all using only Win32 APIs.

What the code does

  1. ShiftCmdline (no CRT / no C++ runtime): Walks through the process command line returned by GetCommandLineA() and returns a pointer to the first character of the next token after argv[0]. It accurately follows Windows quoting rules:

    • Tracks whether we’re inside a double-quoted section.
    • Handles backslash escaping before a quote (\"), including doubled backslashes.
    • Detects the end of argv[0] at the first unquoted space or tab.
    • Stops at '\0' if the command line ends.
  2. WriteLineA: A tiny helper to write ASCII text to stderr using WriteFile. It computes the string length manually and appends \r\n. No CRT involved.

  3. main:

    • Gets the full command line with GetCommandLineA().
    • Prints the original line.
    • Repeatedly calls ShiftCmdline and prints the shifted view until the string ends. This is a convenient way to visualize how Windows tokenization would move through arguments.

Why this matters

  • No CRT dependency: Reduces binary size and avoids pulling in ucrtbase.dll/msvcp*.dll. Useful for tiny tools, recovery environments, or specialized launchers.
  • Accurate Windows parsing: Windows command-line parsing is quirky (quotes, backslashes, spaces). This routine mirrors the core behavior closely enough for most launcher tasks.
  • Pointer math, not copies: It avoids allocations or token splitting—just returns a pointer into the existing command line buffer.

Key parsing rules captured

  • Inside quotes: A " toggles quote state unless it’s escaped by a preceding backslash. The code tracks a backslashPreceding flag to decide whether to treat " literally or as a delimiter.
  • Outside quotes: The first unquoted space/tab marks the end of argv[0]. Subsequent leading spaces/tabs are skipped to land on the next argument.
  • Termination: On '\0', the routine returns a pointer to the terminator—callers can detect end-of-line immediately.

How to use it

  • Compile with options that omit CRT startup if you want a truly minimal artifact (e.g., custom entry point and ExitProcess), or keep a normal main as shown.
  • Replace any printf/puts calls with WriteFile to standard handles obtained via GetStdHandle.

Practical applications

  • Wrapper executables: Strip argv[0] and forward the remainder to another process without paying CRT costs.
  • Argument peeking: Inspect or log raw user arguments as Windows sees them.
  • Tiny utilities: Ideal for bootstrappers, installers, or shims where dependency footprint matters.

This pattern keeps your executable lean while respecting Windows’ real-world command-line rules—no CRT required.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// ----------------------------------------------------
// ShiftCmdline (no CRT / no C++ runtime)
// ----------------------------------------------------
LPSTR ShiftCmdline(LPSTR cmdline) {
BOOL backslashPreceding = FALSE;
BOOL insideDoubleQuote = FALSE;
BOOL afterArgv0 = FALSE;
for (INT i = 0;; ++i) {
CHAR c = cmdline[i];
if (afterArgv0) {
if (c == ' ' || c == '\t') {
continue;
}
return cmdline + i;
} else if (insideDoubleQuote) {
if (c == '\\') {
backslashPreceding = !backslashPreceding;
} else if (c == '"') {
if (!backslashPreceding) {
insideDoubleQuote = FALSE;
} else {
backslashPreceding = FALSE;
}
} else if (c == '\0') {
return cmdline + i;
} else {
backslashPreceding = FALSE;
}
} else {
if (c == '\\') {
backslashPreceding = !backslashPreceding;
} else if (c == '"') {
if (!backslashPreceding) {
insideDoubleQuote = TRUE;
} else {
backslashPreceding = FALSE;
}
} else if (c == ' ' || c == '\t') {
afterArgv0 = TRUE;
} else if (c == '\0') {
return cmdline + i;
} else {
backslashPreceding = FALSE;
}
}
}
}
// ----------------------------------------------------
// Minimal helper: print string to stderr without CRT
// ----------------------------------------------------
static void WriteLineA(LPCSTR s) {
DWORD len = 0;
while (s[len] != '\0') len++;
DWORD written;
HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
WriteFile(h, s, len, &written, NULL);
WriteFile(h, "\r\n", 2, &written, NULL);
}
int main(void) {
LPSTR cmdline = GetCommandLineA();
WriteLineA("=== Original Command Line ===");
WriteLineA(cmdline);
while (TRUE) {
cmdline = ShiftCmdline(cmdline);
if (*cmdline == '\0') break;
WriteLineA("=== Shifted ===");
WriteLineA(cmdline);
}
ExitProcess(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment