Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #400064 > unrolled thread

Great code

Started byBonita Montero <Bonita.Montero@gmail.com>
First post2026-06-15 11:10 +0200
Last post2026-06-27 22:02 -0700
Articles 8 — 5 participants

Back to article view | Back to comp.lang.c


Contents

  Great code Bonita Montero <Bonita.Montero@gmail.com> - 2026-06-15 11:10 +0200
    Re: Great code Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-06-16 07:39 +0000
      Re: Great code Bonita Montero <Bonita.Montero@gmail.com> - 2026-06-16 09:57 +0200
      Re: Great code Paul <nospam@needed.invalid> - 2026-06-27 07:20 -0400
        Re: Great code Michael S <already5chosen@yahoo.com> - 2026-06-27 21:28 +0300
          Re: Great code Paul <nospam@needed.invalid> - 2026-06-27 16:53 -0400
            Re: Great code Michael S <already5chosen@yahoo.com> - 2026-06-28 02:07 +0300
              Re: Great code "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-06-27 22:02 -0700

#400064 — Great code

FromBonita Montero <Bonita.Montero@gmail.com>
Date2026-06-15 11:10 +0200
SubjectGreat code
Message-ID<110ofim$820a$1@raubtier-asyl.eternal-september.org>
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;
}

[toc] | [next] | [standalone]


#400078

FromLawrence D’Oliveiro <ldo@nz.invalid>
Date2026-06-16 07:39 +0000
Message-ID<110qukc$uotb$1@dont-email.me>
In reply to#400064
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 haven’t yet added that capability (which systemd has). Perhaps
you could contribute code ...

[toc] | [prev] | [next] | [standalone]


#400081

FromBonita Montero <Bonita.Montero@gmail.com>
Date2026-06-16 09:57 +0200
Message-ID<110qvkh$v3nt$1@raubtier-asyl.eternal-september.org>
In reply to#400078
Am 16.06.2026 um 09:39 schrieb Lawrence D’Oliveiro:

> 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;	
}

[toc] | [prev] | [next] | [standalone]


#400258

FromPaul <nospam@needed.invalid>
Date2026-06-27 07:20 -0400
Message-ID<111oblh$2r3ac$1@paganini.bofh.team>
In reply to#400078
On Tue, 6/16/2026 3:39 AM, Lawrence D’Oliveiro 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 haven’t 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

[toc] | [prev] | [next] | [standalone]


#400261

FromMichael S <already5chosen@yahoo.com>
Date2026-06-27 21:28 +0300
Message-ID<20260627212842.00003a0a@yahoo.com>
In reply to#400258
On Sat, 27 Jun 2026 07:20:17 -0400
Paul <nospam@needed.invalid> wrote:

> On Tue, 6/16/2026 3:39 AM, Lawrence D’Oliveiro 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 haven’t 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" ?


[toc] | [prev] | [next] | [standalone]


#400263

FromPaul <nospam@needed.invalid>
Date2026-06-27 16:53 -0400
Message-ID<111pd93$2tjn0$1@paganini.bofh.team>
In reply to#400261
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 D’Oliveiro 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 haven’t 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

[toc] | [prev] | [next] | [standalone]


#400265

FromMichael S <already5chosen@yahoo.com>
Date2026-06-28 02:07 +0300
Message-ID<20260628020729.00005e20@yahoo.com>
In reply to#400263
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 D’Oliveiro 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 haven’t 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.




[toc] | [prev] | [next] | [standalone]


#400269

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2026-06-27 22:02 -0700
Message-ID<111q9t2$3dfo9$1@dont-email.me>
In reply to#400265
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 D’Oliveiro 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 haven’t 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?

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web