0

I need to modify the system time inside a cpp Windows application. I also would like to reset the date later to the real current time, at best read from a reliable web service.

I have access to Microsoft Foundation Classes.

Is there an API which I can use?

Jan Hackenberg
  • 481
  • 3
  • 14
  • 2
    Do you need to actually set the time of the operating system itself? Why? What underlying problem is that supposed to solve? – Some programmer dude Jul 06 '23 at 09:21
  • 1
    https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime or is what you actually want https://stackoverflow.com/questions/2772061/windows-api-to-trigger-the-time-synchronization – Alan Birtles Jul 06 '23 at 09:21
  • @Someprogrammerdude I want to write a GTest where the code fragments relie on system date. And this changing time stamp will effect the outcome of the test I envisage. Rather than opening up a pandoras box I want to simply hack a fixed date inside GTest`s setup() method and reset the system date inside TearDown(). The tests might still break if they are run exactly over mid night, but apart from this it should be stable then. – Jan Hackenberg Jul 06 '23 at 09:35
  • 3
    To me that sounds more like you want to *mock* the API used by your application for getting the system time. – Some programmer dude Jul 06 '23 at 09:38
  • 4
    I can envisage that one day someone happens to run the test while editing some source files, and now those files have the wrong save date. Disaster to follow. Faking the date in the test code seems a lot better. – BoP Jul 06 '23 at 10:12
  • 1
    IIRC you'd need Administrator privileges, exactly because it is system-wide and has many unintended consequences including potential security issues. Mocking only affects the test environment, and is done within that test environment, so there are no security implications. – MSalters Jul 06 '23 at 14:55

1 Answers1

0

First of all, let me be clear that I agree with the comments pointing out that the way you talk about putting this to use strikes me as a poor idea. If you want a fake time for test purposes, it's much better to provide a mock time provider to do the things you need.

There are honestly useful purposes for setting the system time though, and although Windows does have a built in service to set the clock from a remote server, it doesn't provide full control over when it runs, so you choices are to let it run when it decides to, or run it manually.

Setting the system time isn't entirely trivial though. Doing so has serious enough ramifications that it isn't enough to just run the program as an administrator for it to work. Running as an administrator gives you the right to do so, but setting the system time is a privileged operation, which means you have to enable the privilege before you can do it.

Enabling privileges is kind of a pain. The code I use to do it is something Microsoft posted to their web site many years ago. To be honest, it doesn't make a lot of sense to me. Every time I look at it, I think I should rewrite it, but then I get busy with something else, so it's still around, clunky and excessively complex as it was when I downloaded it ~25 years ago. But all this time later, it still works...

Anyway, Windows has a SetSystemTime to set the system time. You need to enable the SE_SYSTEMTIME_NAME privilege to do that, so code to do it looks something like this:

bool set_system_time(SYSTEMTIME new_time) { 
    set_privilege(SE_SYSTEMTIME_NAME, TRUE);

    if ( !SetSystemTime(&new_time)) {
        std::cerr << "Error setting system time.\n";
        return false;
    }

    set_privilege(SE_SYSTEMTIME_NAME, FALSE);
    return true;
}

Here's the header for set_privilege:

// setpriv.h
#ifndef SET_PRIV_H_INCLUDED
#define SET_PRIV_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

BOOL set_privilege( LPCSTR privilege, BOOL enable );

#pragma comment(lib, "advapi32.lib")

#ifdef __cplusplus
}
#endif

#endif

...and here's the implementation:

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "setpriv.h"

BOOL set_privilege( LPCSTR privilege, BOOL enable ) {
//
// Set or clear a specified privilege for the current process.
//
// privilege:   Privilege to enable/disable
// enable:      tells whether to enable or disable privilege
//
    HANDLE token;
    TOKEN_PRIVILEGES old_privileges;
    TOKEN_PRIVILEGES new_privileges;
    LUID luid;
    DWORD size=sizeof(TOKEN_PRIVILEGES);
    BOOL success=FALSE;

    //
    // obtain the Luid of the privilege from the target computer
    //
    if(!LookupPrivilegeValue(NULL, privilege, &luid))
        return success;

    //
    // open our access token, which we read and modify later
    //
    if(!OpenProcessToken(
            GetCurrentProcess(),
            TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
            &token))
        return FALSE;

    //
    // first get current the privilege settings
    //
    old_privileges.PrivilegeCount           = 1;
    old_privileges.Privileges[0].Luid       = luid;
    old_privileges.Privileges[0].Attributes = 0;

    AdjustTokenPrivileges(
        token,
        FALSE,
        &old_privileges,
        sizeof(TOKEN_PRIVILEGES),
        &new_privileges,
        &size);

    if(GetLastError() == ERROR_SUCCESS) {
        //
        // Then if that worked, set the privilege based on previous
        //  setting
        //
        new_privileges.PrivilegeCount     = 1;
        new_privileges.Privileges[0].Luid = luid;

        if(enable)
            new_privileges.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
        else
            new_privileges.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
                new_privileges.Privileges[0].Attributes);

        AdjustTokenPrivileges(
            token,
            FALSE,
            &new_privileges,
            size,
            NULL,
            NULL);

        success = (GetLastError() == ERROR_SUCCESS);
    }

    CloseHandle(token);

    return success;
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111