Skip to content

kernel32 + user32: implement FormatMessageA, WriteConsoleA, SendMessageA#120

Draft
HarryR wants to merge 2 commits intodecompals:mainfrom
HarryR:HarryR-format-message
Draft

kernel32 + user32: implement FormatMessageA, WriteConsoleA, SendMessageA#120
HarryR wants to merge 2 commits intodecompals:mainfrom
HarryR:HarryR-format-message

Conversation

@HarryR
Copy link
Copy Markdown

@HarryR HarryR commented Apr 17, 2026

APIs needed by NT 3.5-era build tools (RC.EXE, LINK.EXE, MC.EXE) for error diagnostics and string handling:

FormatMessageA (kernel32):

  • Full implementation of FROM_HMODULE, FROM_STRING, FROM_SYSTEM modes.
  • Walks RT_MESSAGETABLE resources embedded in PE .rsrc sections (the
    MESSAGE_RESOURCE_DATA/BLOCK/ENTRY structures produced by MC.EXE).
  • Handles %1..%99 argument substitution with !format! specifiers,
    FORMAT_MESSAGE_ALLOCATE_BUFFER, and FORMAT_MESSAGE_IGNORE_INSERTS.
  • Fix 32-bit va_list handling: dereference va_list* for non-ARGUMENT_ARRAY
    callers, use uint32_t indexing (not host DWORD_PTR) for guest stack args.
    The guest is 32-bit so va_list entries are 4 bytes, not 8.
  • Support !ws!/!ls! wide-string format specifiers in message inserts.

WriteConsoleA (kernel32):

  • Mirrors the existing WriteConsoleW implementation. NT tools (RC, LINK)
    use WriteConsoleA to emit error messages after FormatMessageA formats them.

user32 stubs:

  • SendMessageA/W: no-op returning 0 / ERROR_INVALID_WINDOW_HANDLE. RC.EXE
    calls SendMessage as a vestigial IDE progress hook (NULL HWND in CLI runs).
  • CharUpperBuffW: uppercase wide string buffer via towupper.
  • CharNextA: single-byte advance (non-DBCS).

Also adds ERROR_INVALID_WINDOW_HANDLE (1400) to errors.h.

Three APIs needed by NT 3.5-era build tools (RC.EXE, LINK.EXE, MC.EXE)
for error diagnostics:

FormatMessageA:
  Full implementation of FROM_HMODULE, FROM_STRING, FROM_SYSTEM modes.
  Walks RT_MESSAGETABLE resources embedded in PE .rsrc sections (the
  MESSAGE_RESOURCE_DATA/BLOCK/ENTRY structures produced by MC.EXE).
  Handles %1..%99 argument substitution with !format! specifiers,
  FORMAT_MESSAGE_ALLOCATE_BUFFER, and FORMAT_MESSAGE_IGNORE_INSERTS.
  Previously commented out due to va_list trampoline issues; signature
  changed to LPVOID to avoid leaking GCC's __va_list_tag.

WriteConsoleA:
  Mirrors the existing WriteConsoleW implementation. NT tools (RC, LINK)
  use WriteConsoleA to emit error messages after FormatMessageA formats
  them. Writes through to the FileObject's underlying fd for stdout/
  stderr handles.

SendMessageA:
  No-op stub returning 0 / ERROR_INVALID_WINDOW_HANDLE. NT's RC.EXE
  calls SendMessage(NULL, WM_USER+0x19, ...) as a vestigial IDE
  progress hook that's NULL in standalone CLI runs. Without the stub
  wibo aborts on the missing import.

Also adds ERROR_INVALID_WINDOW_HANDLE (1400) to errors.h.
@HarryR HarryR marked this pull request as draft April 18, 2026 11:23
FormatMessageA:
- Fix 32-bit va_list handling: dereference va_list* for non-ARGUMENT_ARRAY
  callers, use uint32_t indexing (not host DWORD_PTR) for guest stack args
- Support !ws!/!ls! wide-string format specifiers in message inserts

user32:
- SendMessageW no-op stub (NULL HWND path for CLI tools)
- CharUpperBuffW via towupper
- CharNextA single-byte advance (non-DBCS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant