简体   繁体   中英

Setting windows system time in C++

I am having a GPS unit connected to com port. I wanted to make my system as a time server. How to do this?

I tried using settime() , SetLocalTime() , SetSystemTime() , etc. None are working for me. By using system("cmd") I tried, but again I am not getting administrator privileges. When using system("runas cmd") the command prompt is opening and closing as blink. Unable to understand what is happening.

How I can make this work?

Privilege management on Windows is always a tricky part. MSoft assumes that normal mortals should not care with that and documentation is often incomplete.

Here you problem is likely that normal processes (not running as administrator) have not the privilege to change the system time.

But what is possible is:

  • check whether the SeSystemtimePrivilege is granted to the running process
    • if it is just set the time
    • if not restart the process with same parameter as administrator via ShellExectute , verb= "runas"

Here is an example of code (the part to parse the time string and actually set the time is not implemented):

#include <iostream>
#include <windows.h>
#include <tchar.h>

#ifdef UNICODE
#define tcout std::wcout
#else
#define tcout std::cout
#endif

// utility used to display error messages
void errmsg() {
    DWORD err = ::GetLastError();
    LPTSTR msg;
    ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
        err, LANG_NEUTRAL, (LPTSTR) &msg, 0, NULL);
    tcout << _T("Error") << std::endl;
}

int _tmain(int argc, TCHAR *argv[]) {
    // syntax check
    if (argc != 2) {
        tcout << _T("Usage ") << argv[0] << " _time_" << std::endl;
        return 1;
    }
    LPCTSTR time = argv[1];
    // get access to the current process token
    HANDLE process = ::GetCurrentProcess();
    HANDLE token;
    if (! ::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token)) {
        errmsg();
        return 1;
    }
    // get priviledges from current token
    DWORD sz = 0, sz2;
    TOKEN_PRIVILEGES *privs;
    ::GetTokenInformation(token, TokenPrivileges, NULL, 0, &sz); // first only size
    if (sz == 0) {
        errmsg();
        return 1;
    }
    privs = (TOKEN_PRIVILEGES *) malloc(sz);
    if (! ::GetTokenInformation(token, TokenPrivileges, privs, sz, &sz2)) {
        errmsg();
        return 1;
    }

    // display found priviledges and look if SE_SYSTEMTIME_NAME is present
    BOOL ok = FALSE;
    for (size_t i=0; i<privs->PrivilegeCount; i++) {
        sz = 0;
        ::LookupPrivilegeName(NULL, &(privs->Privileges[i].Luid), NULL, &sz);
        LPTSTR name = (LPTSTR) malloc(sz * sizeof(TCHAR));
        if (! ::LookupPrivilegeName(NULL, &(privs->Privileges[i].Luid), name, &sz)) {
            errmsg();
            return 1;
        }
        if (0 == ::lstrcmp(name, SE_SYSTEMTIME_NAME)) {
            ok = TRUE;
        }
        // tcout << name << std::endl;
        free(name);
    }
    free(privs); // done with it
    tcout << (ok ? _T("Can") : _T("Cannot")) << _T(" change time") << std::endl;

    if (ok) {
        tcout << _T("Setting time with ") << time << std::endl;
        // actually parse the time string and call SetSystemTime
        //...
    }
    else {
        tcout << _T("Restart self as admin...") << std::endl;
        // start as cmd /K "full path" to have a chance to see eventual errors
        LPTSTR params = (LPTSTR) malloc(MAX_PATH + 7 + lstrlen(time));
        lstrcpy(params, _T(" /K \""));
        sz = ::GetModuleFileName(NULL, params + 5, MAX_PATH);
        // tcout << params + 5 << std::endl;
        ::lstrcat(params,_T("\" "));  // do not forget the trailing "
        lstrcat(params, time);
        // Let's go, the UAC control should pop to allow the privilege elevation
        if (((int) ShellExecute(NULL, _T("runas"), _T("cmd.exe"), params,
                _T("."), SW_SHOW)) < 32) {
            tcout << _T("Could not start self with elevated privileges") << std::endl;
            return 1;
        }
        free(params);
    }
    // time to clean...
    ::CloseHandle(token);
    ::CloseHandle(process);
    return 0;
}

Things to still work on:

  • when the command is restarted in admin mode, is uses a console with cmd /K to allow user to see what as happened. If you prefere to be more silent, you should use cmd /C instead and use SW_HIDE is ShellExecute .
  • this program assumes that the new time will be given as a simple string which may or not be relevant for your use case
  • the verbosity should be controlled with optional arguments
  • a system should prevent to try runas admin more than once (probably a second or optional parameter)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM