Getting a QWORD from the registry using C# and P/Invoke

From time to time you need to use really, really big numbers, like when describing, say, the time of day you need to download a certain set of data from remote servers.  Well, you probably don't need that much granularity in the time, but that's what MCE uses to schedule EPG downloads. <g>

So, we store this thing off in the registry at in a value named dlRegTime.  I can see why the dev did this: it's easy to convert a DateTime to an Int64 by way of DateTime.ToFileTime(), which makes for a clean, unambiguous value to write.  I'd almost bet that he was patting himself on the back... until he had to actually write the value to the registry.

The problem comes when you need that value from C# (or, I guess, VB.NET).  The 1.1 Framework designers apparently did not think that anyone could ever possibly need a 64-bit value from the registry, and if they did, they could P/Invoke.  That's exactly what you have to do.  Here's an example where I convert the FILETIME stored as a QWORD into a DateTime:

 [DllImport("Advapi32.dll")]
static extern uint RegOpenKeyEx(UIntPtr hKey,
    string lpSubKey,
    uint ulOptions,
    int samDesired,
    out IntPtr phkResult);

[DllImport("Advapi32.dll", EntryPoint="RegQueryValueEx")]
static extern uint RegQueryValueEx_QWORD(
    IntPtr hKey,
    string lpValueName,
    uint lpReserved,
    ref uint lpType,
    ref long lpData,
    ref int lpcbData
    );

[DllImport("Advapi32.dll")]
static extern uint RegCloseKey(IntPtr hKey);

const int KEY_QUERY_VALUE = 0x1;
const string EPG_REGKEY = 
    @"SOFTWARE\Microsoft\Windows\CurrentVersion\Media Center\Service\EPG";
const string DLTIME_REGVALUE = "dlRegTime";

DateTime GetNextDownloadTime()
{
    UIntPtr HKEY_LOCAL_MACHINE = (UIntPtr)0x80000002;
    IntPtr hkey = IntPtr.Zero;
    DateTime date = DateTime.MinValue;

    try
    {
        uint lResult = RegOpenKeyEx(
            HKEY_LOCAL_MACHINE, 
            EPG_REGKEY,
            0, KEY_QUERY_VALUE, out hkey);

        if (0 == lResult)
        {
            long qword = 0;
            uint uType = 0;
            int cbData = 8;
            
            lResult = RegQueryValueEx_QWORD(hkey, 
                                            "dlRegTime", 
                                            0, 
                                            ref uType, 
                                            ref qword, 
                                            ref cbData);

            date = DateTime.FromFileTime(qword);
        }
        else
        {
            throw new ApplicationException("The registry key " + 
                EPG_REGKEY + 
                " could not be opened for reading.  The error code was 0x" + 
                lResult.ToString("x"));
        }
    }
    finally
    {
        if (IntPtr.Zero != hkey)
        {
            RegCloseKey(hkey);
        }
    }

    return date;
}