• Great code

    From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++,comp.lang.c on Mon Jun 15 11:10:46 2026
    From Newsgroup: comp.lang.c

    I've written a tool called "bg" to combine Windows' runas with start.
    One parameter is the affinity-mask for the processors which the star-
    ted program is attached to. The below code is the parsing function.
    The code wouldn't apply if this function has to be performance since
    I work with regexes. But having this this way makes the code rather
    short for what is required - imagine you'd have to do that in C.

    bool params::state::checkAffinity( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--affinity" ) != 0 )
    return false;
    if( m_todo.size() < 2 )
    {
    m_todo = {};
    m_errs.emplace_back( L"please specify affinity" );
    cerr << "" << endl;
    return false;
    };
    DWORD_PTR dwpAff = 0;
    match_results<wstring_view::iterator> match;
    defer chop2( [&] { m_todo = m_todo.subspan( 2 ); } );
    wstring_view svPattern( m_todo[1] );
    constexpr const wchar_t
    *RxHex = L"^0x([0-9a-f]+)$",
    *RxBin = sizeof( DWORD_PTR ) == 8 ? L"^b((?:[01]+){1,64})$" : L"^b((?:[01]+){1,32})$",
    *RxRange = L"^([0-9]+)(?:-([0-9]+))?(,)?";
    if( wregex rxHex( RxHex, regex_constants::icase ); regex_match( svPattern.begin(), svPattern.end(), match, rxHex ) )
    wistringstream( wstring( svPattern ) ) >> hex >> dwpAff;
    else if( wregex rxBinary( RxBin, regex_constants::icase );
    regex_match( svPattern.begin(), svPattern.end(), match, rxBinary ) )
    for( wchar_t c : wstring_view( match[1].first, match[1].second ) )
    dwpAff = dwpAff << 1 | (c - '0');
    else
    {
    defer affZero( [&] { dwpAff = 0; } );
    auto it = svPattern.begin();
    wregex rx( RxRange );
    match_results<wstring_view::iterator> match;
    for( ; regex_search( it, svPattern.end(), match, rx ); it = match[0].second )
    {
    if( match[3].matched && match[3].second == svPattern.end() )
    break;
    unsigned from, to;
    wistringstream( wstring( match[1].first, match[1].second )
    ) >> from;
    if( match[2].matched )
    {
    wistringstream( wstring( match[2].first,
    match[2].second ) ) >> to;
    ++to;
    }
    else
    to = from + 1;
    constexpr unsigned BITS = sizeof(ULONG_PTR) * 8;
    if( from >= to || from >= BITS || to > BITS )
    break;
    constexpr DWORD_PTR ALL = -1;
    DWORD_PTR dwpBits = ALL << from;
    if( to < BITS )
    dwpBits &= ~(ALL << to);
    dwpAff |= dwpBits;
    }
    if( it == svPattern.end() )
    affZero.disable();
    }
    if( !dwpAff )
    {
    wostringstream woss;
    woss << "invalid affinity pattern: " << svPattern;
    m_errs.emplace_back( woss.str() );
    return true;
    }
    DWORD_PTR dwpProc, dwpSys;
    GetProcessAffinityMask( GetCurrentProcess(), &dwpProc, &dwpSys );
    if( (dwpAff &= dwpProc) )
    pms.dwpAffinity = dwpAff;
    return true;
    }
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Tue Jun 16 07:39:56 2026
    From Newsgroup: comp.lang.c

    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working on that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has). Perhaps
    you could contribute code ...
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Tue Jun 16 09:57:06 2026
    From Newsgroup: comp.lang.c

    Am 16.06.2026 um 09:39 schrieb Lawrence DrCOOliveiro:

    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working on that <https://github.com/Jamesits/SvcGuest>.

    No, my tool has nothing to do with making arbitrary executables
    runnable as services. It's a combination of Windows' runas, start
    and Unix' time.

    Here are the commandline options of my tool:

    static const char usage[] =
    "--console create new console\n"
    "--detach start detached / hidden\n"
    "--idle idle priority\n"
    "--below below normal priority\n"
    "--above above normal priority\n"
    "--high high priority\n"
    "--realtime realtime priority\n"
    "--affintiy f CPU-affinity as hex (0x*), binary (b*) or list (e.g. 1,5-9)\n"
    "--group g primary processor group\n"
    "--wait wait for program to terminate\n"
    "--logon u d p create process under 1. username, 2. domain and 3. password\n"
    "--no-profile don't load registry for --logon, just network-credentials\n"
    "--dir d set startup directory\n"
    "--times show process times (includes --wait)";

    Here's the main source:

    #include <Windows.h>
    #include <iostream>
    #include <iomanip>
    #include <vector>
    #include <string>
    #include <cwchar>
    #include <cstddef>
    #include <system_error>
    #include <cassert>
    #include <sstream>
    #include <functional>
    #include <array>
    #include <type_traits>
    #include <cstdio>
    #include <bit>
    #include <span>
    #include <regex>
    #include <expected>
    #include "defer.hpp"

    #pragma warning(disable: 4554) // operator prececence
    #pragma warning(disable: 26812) // prefer scoped enums

    using namespace std;

    struct params
    {
    wstring userName, domain, password;
    bool noProfile = false;
    wstring startupDir;
    DWORD
    dwPrioFlag = NORMAL_PRIORITY_CLASS,
    dwConsoleFlag = 0;
    DWORD_PTR dwpAffinity = -1;
    WORD wGroup = -1;
    bool wait = false;
    wstring cmdLine;
    bool times = false;
    bool parse( int argc, const wchar_t *const *argv );
    private:
    struct state
    {
    using check_fn = bool (state::*)( params & );
    static const check_fn g_checkFns[9];
    span<const wchar_t *const> m_todo;
    vector<wstring> m_errs;
    bool checkNoProfile( params &pms );
    bool checkLogon( params &pms );
    bool checkDir( params &pms );
    bool checkConsole( params &pms );
    bool checkPriority( params &pms );
    bool checkAffinity( params &pms );
    bool checkGroup( params &pms );
    bool checkWait( params &pms );
    bool checkTimes( params &pms );
    bool parse( params &pms, int argc, const wchar_t *const *argv );
    };
    };

    static expected<wstring, DWORD> inputPassword();
    static string formatTime( uint64_t t );
    static string formatDotted( uintmax_t value );

    int wmain( int argc, const wchar_t *const *argv )
    {
    try
    {
    params pms;
    if( !pms.parse( argc, argv ) )
    return EXIT_FAILURE;
    const wchar_t *curDir = pms.startupDir.length() ? pms.startupDir.c_str() : nullptr;
    if( pms.userName.length() && !pms.password.length() )
    {
    cout << "password: ";
    expected<wstring, DWORD> xptPass = inputPassword();
    if( !xptPass )
    throw exception( "error while password input" );
    pms.password = *xptPass;
    cout << endl;
    }
    BOOL cpRet;
    bool suspended = pms.dwpAffinity != -1 || pms.wGroup != (WORD)-1;
    DWORD dwCreationFlags = pms.dwPrioFlag | pms.dwConsoleFlag | (!suspended ? 0 : CREATE_SUSPENDED);
    STARTUPINFOW si { sizeof si };
    PROCESS_INFORMATION pi;
    if( !pms.userName.length() )
    cpRet = CreateProcessW(
    nullptr, (LPWSTR)pms.cmdLine.c_str(), nullptr, nullptr,
    FALSE, dwCreationFlags, nullptr, curDir, &si, &pi );
    else
    cpRet = CreateProcessWithLogonW(
    pms.userName.c_str(),
    pms.domain.c_str(),
    pms.password.c_str(),
    pms.noProfile ? LOGON_NETCREDENTIALS_ONLY : LOGON_WITH_PROFILE,
    nullptr, (LPWSTR)pms.cmdLine.c_str(),
    dwCreationFlags, nullptr, curDir, &si, &pi );
    if( !cpRet )
    throw exception( "creating process failed" );
    defer closeHandles( [&] { CloseHandle( pi.hProcess ); CloseHandle(
    pi.hThread ); } );
    defer terminate( [&] { TerminateProcess( pi.hProcess, EXIT_FAILURE ); } );
    if( suspended )
    {
    bool threadAssigned = false;
    if( pms.wGroup != (WORD)-1 )
    {
    GROUP_AFFINITY aff {};
    DWORD dwNCPUs = GetActiveProcessorCount( pms.wGroup );
    if( !dwNCPUs )
    throw exception( "can't determine the number of processors in the
    destination group" );
    if( dwNCPUs < sizeof(KAFFINITY) * 8 )
    aff.Mask = ((KAFFINITY)1 << dwNCPUs) - 1;
    else
    aff.Mask = -1;
    aff.Group = pms.wGroup;
    if( !SetThreadGroupAffinity( GetCurrentThread(), &aff, nullptr ) )
    throw exception( "can't set primary thread's group affinity" );
    threadAssigned = true;
    }
    if( pms.dwpAffinity != -1 )
    if( !SetProcessAffinityMask( pi.hProcess, pms.dwpAffinity ) )
    throw exception( "can't set process affinity mask" );
    else if( !threadAssigned && !SetThreadAffinityMask( pi.hThread,
    pms.dwpAffinity ) )
    throw exception( "can't set primary thread's affinity mask" );
    if( ResumeThread( pi.hThread ) == -1 )
    throw exception( "can't resume main thread of process" );
    }
    terminate.disable();
    if( !pms.wait )
    return EXIT_SUCCESS;
    if( WaitForSingleObject( pi.hProcess, INFINITE ) != WAIT_OBJECT_0 )
    throw exception( "can't wait for end of program" );
    DWORD dwExitCode;
    if( !GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
    throw exception( "can't get process exit code" );
    if( !pms.times )
    return dwExitCode;
    FILETIME creationTime, exitTime, userFTime, sysFTime;
    if( !GetProcessTimes( pi.hProcess, &creationTime, &exitTime, &sysFTime, &userFTime ) )
    throw exception( "can't get process times" );
    ULONG64 processCycles;
    if( !QueryProcessCycleTime( pi.hProcess, &processCycles ) )
    throw exception( "can't get process cycles" );
    auto ft2ts = []( const FILETIME &ft ) static -> uint64_t { return
    (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime; };
    uint64_t
    realTime = ft2ts( exitTime ) - ft2ts( creationTime ),
    userTime = ft2ts( userFTime ),
    sysTime = ft2ts( sysFTime );
    cerr
    << "real " << formatTime( realTime ) << endl
    << "user " << formatTime( userTime ) << endl
    << "sys " << formatTime( sysTime ) << endl
    << "cylces " << formatDotted( processCycles ) << endl;
    return dwExitCode;
    }
    catch( const exception &exc )
    {
    cerr << exc.what() << endl;
    }
    }

    static const char usage[] =
    "--console create new console\n"
    "--detach start detached / hidden\n"
    "--idle idle priority\n"
    "--below below normal priority\n"
    "--above above normal priority\n"
    "--high high priority\n"
    "--realtime realtime priority\n"
    "--affintiy f CPU-affinity as hex (0x*), binary (b*) or list (e.g. 1,5-9)\n"
    "--group g primary processor group\n"
    "--wait wait for program to terminate\n"
    "--logon u d p create process under 1. username, 2. domain and 3. password\n"
    "--no-profile don't load registry for --logon, just network-credentials\n"
    "--dir d set startup directory\n"
    "--times show process times (includes --wait)";

    const params::state::check_fn params::state::g_checkFns[9] =
    {
    &checkNoProfile,
    &checkLogon,
    &checkDir,
    &checkConsole,
    &checkPriority,
    &checkAffinity,
    &checkGroup,
    &checkWait,
    &checkTimes
    };

    bool params::parse( int argc, const wchar_t *const *argv )
    {
    state st;
    return st.parse( *this, argc, argv );
    }

    bool params::state::checkNoProfile( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--no-profile" ) != 0 )
    return false;
    pms.noProfile = true;
    m_todo = m_todo.subspan( 1 );
    return true;
    }

    bool params::state::checkLogon( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--logon" ) != 0 )
    return false;
    if( m_todo.size() < 4 )
    {
    m_todo = {};
    m_errs.emplace_back( L"please specify username, domain and password" );
    return true;
    };
    pms.userName = m_todo[1];
    pms.domain = m_todo[2];
    pms.password = m_todo[3];
    m_todo = m_todo.subspan( 4 );
    return true;
    }

    bool params::state::checkDir( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--dir" ) != 0 )
    return false;
    if( m_todo.size() < 2 )
    {
    m_todo = {};
    m_errs.emplace_back( L"please supply startup direcotry" );
    return true;
    };
    pms.startupDir = m_todo[1];
    m_todo = m_todo.subspan( 2 );
    return true;
    }

    bool params::state::checkConsole( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--console" ) == 0 )
    pms.dwConsoleFlag = CREATE_NEW_CONSOLE;
    else if( _wcsicmp( m_todo.front(), L"--detach" ) == 0 )
    pms.dwConsoleFlag = DETACHED_PROCESS;
    else
    return false;
    m_todo = m_todo.subspan( 1 );
    return true;
    }

    bool params::state::checkPriority( params &pms )
    {
    using opt_t = pair<const wchar_t *, DWORD> ;
    static const opt_t opts[] =
    {
    { L"--idle", IDLE_PRIORITY_CLASS },
    { L"--below", BELOW_NORMAL_PRIORITY_CLASS },
    { L"--above", ABOVE_NORMAL_PRIORITY_CLASS },
    { L"--high", HIGH_PRIORITY_CLASS },
    { L"--realtime", REALTIME_PRIORITY_CLASS }
    };
    DWORD dwPrio = 0;
    for( const opt_t &opt : opts )
    if( _wcsicmp( m_todo.front(), opt.first ) == 0 )
    {
    dwPrio = opt.second;
    break;
    }
    if( !dwPrio )
    return false;
    m_todo = m_todo.subspan( 1 );
    pms.dwPrioFlag = dwPrio;
    return true;
    }

    bool params::state::checkAffinity( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--affinity" ) != 0 )
    return false;
    if( m_todo.size() < 2 )
    {
    m_todo = {};
    m_errs.emplace_back( L"please specify affinity" );
    cerr << "" << endl;
    return false;
    };
    DWORD_PTR dwpAff = 0;
    match_results<wstring_view::iterator> match;
    defer chop2( [&] { m_todo = m_todo.subspan( 2 ); } );
    wstring_view svPattern( m_todo[1] );
    constexpr const wchar_t
    *RxHex = L"^0x([0-9a-f]+)$",
    *RxBin = sizeof( DWORD_PTR ) == 8 ? L"^b((?:[01]+){1,64})$" : L"^b((?:[01]+){1,32})$",
    *RxRange = L"^([0-9]+)(?:-([0-9]+))?(,)?";
    if( wregex rxHex( RxHex, regex_constants::icase ); regex_match( svPattern.begin(), svPattern.end(), match, rxHex ) )
    wistringstream( wstring( svPattern ) ) >> hex >> dwpAff;
    else if( wregex rxBinary( RxBin, regex_constants::icase ); regex_match(
    svPattern.begin(), svPattern.end(), match, rxBinary ) )
    for( wchar_t c : wstring_view( match[1].first, match[1].second ) )
    dwpAff = dwpAff << 1 | (c - '0');
    else
    {
    defer affZero( [&] { dwpAff = 0; } );
    auto it = svPattern.begin();
    wregex rx( RxRange );
    match_results<wstring_view::iterator> match;
    for( ; regex_search( it, svPattern.end(), match, rx ); it = match[0].second )
    {
    if( match[3].matched && match[3].second == svPattern.end() )
    break;
    unsigned from, to;
    wistringstream( wstring( match[1].first, match[1].second ) ) >> from;
    if( match[2].matched )
    {
    wistringstream( wstring( match[2].first, match[2].second ) ) >> to;
    ++to;
    }
    else
    to = from + 1;
    constexpr unsigned BITS = sizeof(ULONG_PTR) * 8;
    if( from >= to || from >= BITS || to > BITS )
    break;
    constexpr DWORD_PTR ALL = -1;
    DWORD_PTR dwpBits = ALL << from;
    if( to < BITS )
    dwpBits &= ~(ALL << to);
    dwpAff |= dwpBits;
    }
    if( it == svPattern.end() )
    affZero.disable();
    }
    if( !dwpAff )
    {
    wostringstream woss;
    woss << "invalid affinity pattern: " << svPattern;
    m_errs.emplace_back( woss.str() );
    return true;
    }
    DWORD_PTR dwpProc, dwpSys;
    GetProcessAffinityMask( GetCurrentProcess(), &dwpProc, &dwpSys );
    if( (dwpAff &= dwpProc) )
    pms.dwpAffinity = dwpAff;
    return true;
    }

    bool params::state::checkGroup( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--group" ) != 0 )
    return false;
    if( m_todo.size() < 2 )
    {
    m_todo = {};
    m_errs.emplace_back( L"please specify processor group" );
    return false;
    };
    defer finish( [&] { m_todo = m_todo.subspan( 2 ); } );
    WORD wGroups = GetActiveProcessorGroupCount();
    if( !wGroups )
    {
    m_errs.emplace_back( L"unable to determine the active processor groups" );
    return true;
    }
    defer invalid( [&] { m_errs.emplace_back( L"please specify valid processor group" ); } );
    wstring_view svGroup( m_todo[1] );
    match_results<wstring_view::iterator> match;
    wregex rxGroup( L"^[0-9]+$" );
    if( !regex_match( svGroup.begin(), svGroup.end(), match, rxGroup ) )
    return false;
    WORD group;
    wistringstream( wstring( m_todo[1] ) ) >> group;
    if( group >= wGroups )
    return false;
    pms.wGroup = group;
    invalid.disable();
    return true;
    }

    bool params::state::checkWait( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--wait" ) != 0 )
    return false;
    pms.wait = true;
    m_todo = m_todo.subspan( 1 );
    return true;
    }

    bool params::state::checkTimes( params &pms )
    {
    if( _wcsicmp( m_todo.front(), L"--times" ) != 0 )
    return false;
    pms.wait = true;
    pms.times = true;
    m_todo = m_todo.subspan( 1 );
    return true;
    }

    // might throw bad_alloc

    static optional<WORD> setConsoleCols( WORD wAttrs );
    static wstring quot( wstring_view str );

    bool params::state::parse( params &pms, int argc, const wchar_t *const
    *argv )
    {
    m_todo = span<const wchar_t *const>( argv + 1, argc - 1 );
    while( m_todo.size() )
    {
    bool checked = false;
    for( check_fn check : g_checkFns )
    if( !m_todo.size() || (checked = (this->*check)( pms )) )
    break;
    if( !checked )
    break;
    }
    if( m_errs.size() )
    {
    optional<WORD> wOldAtrs( setConsoleCols( FOREGROUND_RED ) );
    defer oldCols( [&] { if( wOldAtrs ) setConsoleCols( *wOldAtrs ); } );
    for( const wstring &err : m_errs )
    wcout << err << endl;
    return false;
    }
    if( !m_todo.size() )
    {
    cerr << endl << ::usage << endl;
    return false;
    }
    for( const wchar_t *const &part : m_todo )
    {
    if( &part != &m_todo.front() )
    pms.cmdLine += L" ";
    pms.cmdLine += L"\"";
    pms.cmdLine += part;
    pms.cmdLine += L"\"";
    }
    return true;
    }

    static optional<WORD> setConsoleCols( WORD wAttrs )
    {
    constexpr WORD FG_CLR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    HANDLE hStdErr = GetStdHandle( STD_ERROR_HANDLE );
    if( !hStdErr || hStdErr == INVALID_HANDLE_VALUE )
    return nullopt;
    if( GetFileType( hStdErr ) != FILE_TYPE_CHAR )
    return nullopt;
    CONSOLE_SCREEN_BUFFER_INFO csbi {};
    if( !GetConsoleScreenBufferInfo( hStdErr, &csbi ) )
    return nullopt;
    if( wAttrs == (WORD)-1 )
    return csbi.wAttributes;
    wAttrs = (csbi.wAttributes & ~FG_CLR_MASK) | (wAttrs & FG_CLR_MASK);
    if( !SetConsoleTextAttribute( hStdErr, wAttrs ) )
    return nullopt;
    return csbi.wAttributes;

    }

    static expected<wstring, DWORD> inputPassword()
    {
    cout.flush(), wcout.flush();
    constexpr auto *err = +[] static { return unexpected( GetLastError() ); };
    HANDLE hStdIn, hStdOut;
    if( !(hStdIn = GetStdHandle( STD_INPUT_HANDLE )) )
    return err();
    if( !(hStdOut = GetStdHandle( STD_OUTPUT_HANDLE )) )
    return err();
    DWORD dwOldConsoleMode;
    if( !GetConsoleMode( hStdIn, &dwOldConsoleMode ) )
    return err();
    if( !SetConsoleMode( hStdIn, dwOldConsoleMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT) ) )
    return err();
    defer revert( [&] { SetConsoleMode( hStdIn, dwOldConsoleMode ); } );
    vector<COORD> coords;
    wstring pass;
    for( ; ; )
    {
    wchar_t ch;
    if( DWORD dwRead; !ReadConsoleW( hStdIn, &ch, 1, &dwRead, nullptr ) ||
    !dwRead )
    return err();
    if( ch == L'\r' )
    return pass;
    auto writeConsole = [&]( wchar_t ch )
    {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if( !GetConsoleScreenBufferInfo( hStdOut, &csbi ) )
    return false;
    coords.emplace_back( csbi.dwCursorPosition );
    DWORD dwWritten;
    return WriteConsoleW( hStdOut, &ch, 1, &dwWritten, nullptr ) &&
    dwWritten == 1;
    };
    if( ch != L'\b' )
    {
    pass += ch;
    if( !writeConsole( L'*' ) )
    return err();
    continue;
    }
    if( !pass.length() )
    continue;
    pass.pop_back();
    auto back = [&]()
    {
    if( !SetConsoleCursorPosition( hStdOut, coords.back() ) )
    return false;
    coords.pop_back();
    return true;
    };
    if( !back() || !writeConsole( L' ' ) || !back() )
    return err();
    }
    return pass;
    }

    static string formatTime( uint64_t t )
    {
    constexpr auto MS = 10'000u;
    t = (t + MS / 2) / MS;
    uint16_t ms = t % 1'000; t /= 1'000;
    uint8_t secs = t % 60; t /= 60;
    uint8_t mins = t % 60; t /= 60;
    uint32_t hours = (uint32_t)t;
    ostringstream oss;
    if( hours )
    oss << hours << "h" << ":";
    if( hours || mins )
    oss << mins << "m" << ":";
    oss << (int)secs;
    if( ms )
    oss << "." << setw( 3 ) << setfill( '0' ) << ms;
    oss << "s";
    return oss.str();
    }

    static string formatDotted( uintmax_t value )
    {
    ostringstream oss;
    oss << value;
    string_view svDigits( oss.view() );
    auto pDigits = svDigits.begin();
    size_t
    digLen = svDigits.length(),
    dots = (digLen - 1) / 3,
    head = digLen - 3 * dots;
    string dotted( digLen + dots, 0 );
    auto pDotted = dotted.begin();
    pDotted = copy( pDigits, pDigits + head, pDotted );
    for( ; pDotted != dotted.end(); pDigits += 3 )
    {
    *pDotted++ = L'.';
    pDotted = copy( pDigits, pDigits + 3, pDotted );
    }
    return dotted;
    }
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Sat Jun 27 07:20:17 2026
    From Newsgroup: comp.lang.c

    On Tue, 6/16/2026 3:39 AM, Lawrence DrCOOliveiro wrote:
    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working on that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has). Perhaps
    you could contribute code ...


    Windows has had third-party launch tools for some time.
    One of the situations where this was encountered, was
    some games would not "tolerate" dual core computers, and
    the EXE cooked up at the time could fix that for you,
    without resorting to Task Manager to do it from there.

    You can see here, that generally speaking, there is a way to do it.

    https://superuser.com/questions/309617/how-to-limit-a-process-to-a-single-cpu-core

    And this is the utility I was thinking of, RunFirst. What this does, is it doesn't provide arbitrary single-core affinity, it's for running games on
    CPU0, and relying upon the scheduler to move other tasks to CPU1. The purpose of RunFirst, is to prevent games from crashing that do not handle the CPU
    core issue properly. It makes the game "think" it is on a single core CPU.
    If the game were to check, it reads CPU0, and the game is then satisfied
    that not only does it have one core, the one core is numbered CPU0.

    https://www.vogons.org/viewtopic.php?t=31541

    C:\Windows\System32\RunFirst.exe "C:\Program Files\GOG.com\Divine Divinity\Div.exe"

    Paul
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sat Jun 27 21:28:42 2026
    From Newsgroup: comp.lang.c

    On Sat, 27 Jun 2026 07:20:17 -0400
    Paul <nospam@needed.invalid> wrote:
    On Tue, 6/16/2026 3:39 AM, Lawrence DrCOOliveiro wrote:
    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working on
    that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has). Perhaps
    you could contribute code ...


    Windows has had third-party launch tools for some time.
    One of the situations where this was encountered, was
    some games would not "tolerate" dual core computers, and
    the EXE cooked up at the time could fix that for you,
    without resorting to Task Manager to do it from there.

    You can see here, that generally speaking, there is a way to do it.

    https://superuser.com/questions/309617/how-to-limit-a-process-to-a-single-cpu-core

    And this is the utility I was thinking of, RunFirst. What this does,
    is it doesn't provide arbitrary single-core affinity, it's for
    running games on CPU0, and relying upon the scheduler to move other
    tasks to CPU1. The purpose of RunFirst, is to prevent games from
    crashing that do not handle the CPU core issue properly. It makes the
    game "think" it is on a single core CPU. If the game were to check,
    it reads CPU0, and the game is then satisfied that not only does it
    have one core, the one core is numbered CPU0.

    https://www.vogons.org/viewtopic.php?t=31541

    C:\Windows\System32\RunFirst.exe "C:\Program
    Files\GOG.com\Divine Divinity\Div.exe"

    Paul
    How does it differ from "start /affinity 1 div.exe" ?
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Sat Jun 27 16:53:56 2026
    From Newsgroup: comp.lang.c

    On Sat, 6/27/2026 2:28 PM, Michael S wrote:
    On Sat, 27 Jun 2026 07:20:17 -0400
    Paul <nospam@needed.invalid> wrote:

    On Tue, 6/16/2026 3:39 AM, Lawrence DrCOOliveiro wrote:
    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working on
    that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has). Perhaps
    you could contribute code ...


    Windows has had third-party launch tools for some time.
    One of the situations where this was encountered, was
    some games would not "tolerate" dual core computers, and
    the EXE cooked up at the time could fix that for you,
    without resorting to Task Manager to do it from there.

    You can see here, that generally speaking, there is a way to do it.

    https://superuser.com/questions/309617/how-to-limit-a-process-to-a-single-cpu-core

    And this is the utility I was thinking of, RunFirst. What this does,
    is it doesn't provide arbitrary single-core affinity, it's for
    running games on CPU0, and relying upon the scheduler to move other
    tasks to CPU1. The purpose of RunFirst, is to prevent games from
    crashing that do not handle the CPU core issue properly. It makes the
    game "think" it is on a single core CPU. If the game were to check,
    it reads CPU0, and the game is then satisfied that not only does it
    have one core, the one core is numbered CPU0.

    https://www.vogons.org/viewtopic.php?t=31541

    C:\Windows\System32\RunFirst.exe "C:\Program
    Files\GOG.com\Divine Divinity\Div.exe"

    Paul

    How does it differ from "start /affinity 1 div.exe" ?

    Runfirst was written years ago, like maybe WinXP era.
    Some of the other (convenience) methods, did not exist at the time.

    Paul


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sun Jun 28 02:07:29 2026
    From Newsgroup: comp.lang.c

    On Sat, 27 Jun 2026 16:53:56 -0400
    Paul <nospam@needed.invalid> wrote:
    On Sat, 6/27/2026 2:28 PM, Michael S wrote:
    On Sat, 27 Jun 2026 07:20:17 -0400
    Paul <nospam@needed.invalid> wrote:

    On Tue, 6/16/2026 3:39 AM, Lawrence DrCOOliveiro wrote:
    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working
    on that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has).
    Perhaps you could contribute code ...


    Windows has had third-party launch tools for some time.
    One of the situations where this was encountered, was
    some games would not "tolerate" dual core computers, and
    the EXE cooked up at the time could fix that for you,
    without resorting to Task Manager to do it from there.

    You can see here, that generally speaking, there is a way to do it.

    https://superuser.com/questions/309617/how-to-limit-a-process-to-a-single-cpu-core

    And this is the utility I was thinking of, RunFirst. What this
    does, is it doesn't provide arbitrary single-core affinity, it's
    for running games on CPU0, and relying upon the scheduler to move
    other tasks to CPU1. The purpose of RunFirst, is to prevent games
    from crashing that do not handle the CPU core issue properly. It
    makes the game "think" it is on a single core CPU. If the game
    were to check, it reads CPU0, and the game is then satisfied that
    not only does it have one core, the one core is numbered CPU0.

    https://www.vogons.org/viewtopic.php?t=31541

    C:\Windows\System32\RunFirst.exe "C:\Program
    Files\GOG.com\Divine Divinity\Div.exe"

    Paul

    How does it differ from "start /affinity 1 div.exe" ?

    Runfirst was written years ago, like maybe WinXP era.
    Some of the other (convenience) methods, did not exist at the time.

    Paul


    I think that 'start /affinity' existed in WinXp and I would be surprised
    if it didn't exist much earlier.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sat Jun 27 22:02:26 2026
    From Newsgroup: comp.lang.c

    On 6/27/2026 4:07 PM, Michael S wrote:
    On Sat, 27 Jun 2026 16:53:56 -0400
    Paul <nospam@needed.invalid> wrote:

    On Sat, 6/27/2026 2:28 PM, Michael S wrote:
    On Sat, 27 Jun 2026 07:20:17 -0400
    Paul <nospam@needed.invalid> wrote:

    On Tue, 6/16/2026 3:39 AM, Lawrence DrCOOliveiro wrote:
    On Mon, 15 Jun 2026 11:10:46 +0200, Bonita Montero wrote:

    I've written a tool called "bg" to combine Windows' runas with
    start.

    Sort of a Windows answer to systemd? Somebody is already working
    on that <https://github.com/Jamesits/SvcGuest>.

    One parameter is the affinity-mask for the processors which the
    started program is attached to.

    They havenrCOt yet added that capability (which systemd has).
    Perhaps you could contribute code ...


    Windows has had third-party launch tools for some time.
    One of the situations where this was encountered, was
    some games would not "tolerate" dual core computers, and
    the EXE cooked up at the time could fix that for you,
    without resorting to Task Manager to do it from there.

    You can see here, that generally speaking, there is a way to do it.

    https://superuser.com/questions/309617/how-to-limit-a-process-to-a-single-cpu-core

    And this is the utility I was thinking of, RunFirst. What this
    does, is it doesn't provide arbitrary single-core affinity, it's
    for running games on CPU0, and relying upon the scheduler to move
    other tasks to CPU1. The purpose of RunFirst, is to prevent games
    from crashing that do not handle the CPU core issue properly. It
    makes the game "think" it is on a single core CPU. If the game
    were to check, it reads CPU0, and the game is then satisfied that
    not only does it have one core, the one core is numbered CPU0.

    https://www.vogons.org/viewtopic.php?t=31541

    C:\Windows\System32\RunFirst.exe "C:\Program
    Files\GOG.com\Divine Divinity\Div.exe"

    Paul

    How does it differ from "start /affinity 1 div.exe" ?

    Runfirst was written years ago, like maybe WinXP era.
    Some of the other (convenience) methods, did not exist at the time.

    Paul




    I think that 'start /affinity' existed in WinXp and I would be surprised
    if it didn't exist much earlier.






    Win XP had support for hyperthreads iirc?
    --- Synchronet 3.22a-Linux NewsLink 1.2