1

Rather than using WMI to obtain the last boot time, I wanted to calculate it using ::GetSystemTime() and ::GetTickCount64. But once I get to milliseconds, I don't know how to get back to a FILETIME object.

I tried this:

static ULONGLONG FileTimeToMillis(const FILETIME &ft)
{
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime; // could use memcpy here!
    uli.HighPart = ft.dwHighDateTime;

    return uli.QuadPart/10000;
}

static void MillisToSystemTime(ULONGLONG millis, SYSTEMTIME *st)
{
   UINT64 t = static_cast<UINT64>(-10000) * static_cast<UINT64>(millis);

   FILETIME ft;
   ft.dwLowDateTime = static_cast<DWORD(t & 0xFFFFFFFF);
   ft.dwHighDateTime = static_cast<DWORD>(t >> 32);

   ::FileTimeToSystemTime(&ft, st);
}

void main()
{
    SYSTEMTIME st, lt, st2, lt2;

    ::GetSystemTime(&st);
    ::GetLocalTime(&lt);

    cout << "The system time is: " << st.wHour << ":" << st.wMinute << ":" << st.wSecond << endl;
    cout << "The local time is: "  << lt.wHour << ":" << lt.wMinute << ":" << lt.wSecond << endl;

    FILETIME sft, lft;
    ::SystemTimeToFileTime(&st, &sft);
    ::SystemTimeToFileTime(&lt, &lft);  

    cout << "The system time in millis is: " << FileTimeToMillis(sft) << endl;
    cout << "The local time in millis is: "  << FileTimeToMillis(lft) << endl;

    MillisToSystemTime(FileTimeToMillis(sft), &st2);
    MillisToSystemTime(FileTimeToMillis(sft), &lt2);

    cout << "The system time (post conversion) is: " << st2.wHour << ":" << st2.wMinute << ":" << st2.wSecond << endl;
    cout << "The local time (post conversion) is: "  << lt2.wHour << ":" << lt2.wMinute << ":" << lt2.wSecond << endl;
}

But I certainly don't get what's expected. Instead, I get:

The system time is: 15:5:2
The local time is: 10:5:2
The system time in millis is: 12984678302935
The local time in millis is: 12984660302935
The system time (post conversion) is: 52428:52428:52428
The local time (post conversion) is: 52428:52428:52428

Any ideas? Please don't tell me to use Boost or because it's not available to me.

Mike Caron
  • 5,674
  • 4
  • 48
  • 71

2 Answers2

4

The system time (post conversion) is: 52428:52428:52428

When you get weirdo values, always convert to hex first. 52428 == 0xcccc. That's the value that the debug build generated code uses to initialize variables. So you are looking at uninitialized memory.

Next defense strategy is to never ignore the return value of Windows api function. Use a VERIFY() macro, like the one available in MFC. You'll then easily see that FileTimeToSystemTime() fails.

The bug is -10000.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • D'Oh! Sticking a negative number into an unsigned is a rookie error!! I was using code I found via Google and they were using __int64, I didn't change the -10000 to 10000. Good catch!! – Mike Caron Jun 20 '12 at 15:52
3

Turns out I was doing the arithmetic incorrectly. Once I read up on ULARGE_INTEGER and FILETIME, I figured out that I could directly work with the QuadPart of the ULARGE_INTEGER and then pull out the high and low pieces correctly, while avoiding bit shifting and casting confusion.

static UINT64 FileTimeToMillis(const FILETIME &ft)
{
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime; // could use memcpy here!
    uli.HighPart = ft.dwHighDateTime;

    return static_cast<UINT64>(uli.QuadPart/10000);
}

static void MillisToSystemTime(UINT64 millis, SYSTEMTIME *st)
{
    UINT64 multiplier = 10000;
    UINT64 t = multiplier * millis;

    ULARGE_INTEGER li;
    li.QuadPart = t;
    // NOTE, DON'T have to do this any longer because we're putting
    // in the 64bit UINT directly
    //li.LowPart = static_cast<DWORD>(t & 0xFFFFFFFF);
    //li.HighPart = static_cast<DWORD>(t >> 32);

    FILETIME ft;
    ft.dwLowDateTime = li.LowPart;
    ft.dwHighDateTime = li.HighPart;

    ::FileTimeToSystemTime(&ft, st);
}

Key was reading the following from MSDN:

It is not recommended that you add and subtract values from the FILETIME structure to obtain relative times. Instead, you should copy the low- and high-order parts of the file time to a ULARGE_INTEGER structure, perform 64-bit arithmetic on the QuadPart member, and copy the LowPart and HighPart members into the FILETIME structure.

Mike Caron
  • 5,674
  • 4
  • 48
  • 71