Skip to content

Replace CString + unsafe sprintf with safe string formatting — std or Boost

Classification

Abandoned by operator decision on 2026-05-21. This historical record preserves the old Boost/POCO analysis only as provenance; do not promote it without a new active item.

Summary

CString is used for essentially all string operations, coupling core logic to MFC. sprintf/_stprintf with fixed-size TCHAR buf[N] buffers are present throughout and carry classic buffer-overflow risk when format output exceeds the buffer size.

Note: REF-023 covers the immediate safety fix (replace sprintf with _stprintf_s / CString::Format / StringCchPrintf). This issue covers the longer-term architectural question: moving away from CString toward standard string types. Two paths are documented.

Affected Files

File Current usage
srchybrid/AsyncSocketEx.h:133,179,186 const CString &sSocketAddress in API signatures
srchybrid/OtherFunctions.h:113-115 GetNextString(CString, LPCTSTR, int&) tokeniser
srchybrid/OtherFunctions.h:120-145 Multiple CastItoXBytes(uint16/32/64) overloads + SecToTimeLength
50+ additional files CString used for all string manipulation

Current Problematic Pattern

// Fixed-size buffer — overflows if output exceeds 64 chars
TCHAR buf[64];
_stprintf(buf, _T("%u KB/s upload, %u KB/s download"), upKB, downKB);

Option A — CString::Format + StringCchPrintf (no new dependencies)

Least disruptive. Stay within MFC ecosystem. No new dependencies.

Replace unsafe sprintf/_stprintf with:

// Option 1: CString::Format — no buffer, no overflow
CString msg;
msg.Format(_T("%u KB/s upload, %u KB/s download"), upKB, downKB);

// Option 2: StringCchPrintf — safe fixed-buffer version (shlwapi.h)
TCHAR buf[256];
StringCchPrintf(buf, _countof(buf), _T("%u KB/s upload, %u KB/s download"), upKB, downKB);
// Truncates to fit — never overflows, always null-terminates

// Replace CString tokeniser with MFC AfxExtractSubString or manual find:
CString token;
int pos = 0;
while (AfxExtractSubString(token, input, pos++, _T(','))) { ... }

Pros: Minimal code churn; stays with CString which is used everywhere; CString::Format is already exception-safe and unbounded.
Cons: Still MFC-coupled; doesn't help with non-UI code portability.


Option B — std::wstring / std::string + std::format (C++20)

No Boost required. MSVC supports std::format since VS 2022 17.1.

#include <format>
#include <string>

// Replace fixed-size sprintf — type-safe, no buffer
std::wstring msg = std::format(L"{} KB/s upload, {} KB/s download", upKB, downKB);

// Tokeniser — std::ranges::views::split (C++20)
auto tokens = input | std::views::split(L',');

// Replace CastItoXBytes overloads — one template
template<typename T>
std::wstring CastItoXBytes(T count, bool isK = false) {
    return isK ? std::format(L"{} KB", count / 1024)
               : std::format(L"{} B", count);
}

CString interop during migration:

// Convert std::wstring ↔ CString
std::wstring toStd(const CString& s) { return std::wstring((LPCTSTR)s); }
CString      toCStr(const std::wstring& s) { return CString(s.c_str()); }

Option C — boost::format + boost::algorithm (requires Boost)

Use only if Boost is already adopted for REF-008.

#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

// Replace fixed-size sprintf
std::string msg = boost::str(
    boost::format("%1% KB/s upload, %2% KB/s download") % upKB % downKB);

// Replace CString tokeniser
std::vector<std::string> tokens;
boost::algorithm::split(tokens, input, boost::is_any_of(",;"));

// Type-safe numeric conversion
int port = boost::lexical_cast<int>(portString);

Note: boost::format is significantly slower than std::format or CString::Format due to runtime type-erasure. Prefer Option A or B for hot paths.


Recommendation

  • Immediate safety (now): Apply REF-023 first — replace all sprintf/ _stprintf with _stprintf_s or CString::Format. This is a prerequisite.
  • Short-term (Option A): Use CString::Format for new code in MFC-coupled files; safe and zero-churn.
  • Long-term (Option B): Migrate utility functions (OtherFunctions.h overloads, tokenisers) to std::wstring + std::format as files are refactored for other reasons.
  • Avoid Option C unless Boost is already a dependency — boost::format is slower and std::format covers all the same ground.

Migration Strategy

  • Do not do a bulk CString→wstring replace; it will break too much at once.
  • Replace in refactored files first (those already touched by other issues).
  • Target OtherFunctions.h/cpp overloads early — high ROI for template collapse.
  • Add conversion helpers toStd() / toCStr() for interop during transition.

Files

  • srchybrid/OtherFunctions.h / OtherFunctions.cpp — primary target
  • srchybrid/AsyncSocketEx.h — API signatures (after REF-008)
  • 50+ consumer files — migrate gradually

Acceptance Criteria

  • [ ] REF-023 (unsafe sprintf) completed first
  • [ ] OtherFunctions.h CastItoXBytes overloads collapsed to a single template
  • [ ] GetNextString tokeniser uses safe string API
  • [ ] No new sprintf/_stprintf calls introduced in migrated files
  • [ ] CString::Format or std::format used for all new formatted string construction