C# winApi - 获取注册表上次写入日期

C# winApi - getting registry last write date

我的目标是获取某个寄存器的最后写入时间。 要获取寄存器 im 的值,请使用:

RegistryKey mKey;
//...
mKey.GetValue("SomeRegName");

这很好,因为 mKey 的一切都已正确设置。 我发现我需要用它来获取上次写作时间:

[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
    extern private static int RegQueryInfoKey(
        UIntPtr hkey,
        out StringBuilder lpClass,
        ref uint lpcbClass,
        IntPtr lpReserved,
        out uint lpcSubKeys,
        out uint lpcbMaxSubKeyLen,
        out uint lpcbMaxClassLen,
        out uint lpcValues,
        out uint lpcbMaxValueNameLen,
        out uint lpcbMaxValueLen,
        out uint lpcbSecurityDescriptor,
        IntPtr lpftLastWriteTime);

所以我需要我的钥匙的句柄,但我不能那样做:

UIntPtr hkey = new UIntPtr(mKey.Handle);
UIntPtr hkey = (UIntPtr)mKey.Handle;

因为SafeRegistryHandle无法转换为UIntPtr。我的问题是我应该如何调用这个 winapi 函数来获得我需要的东西? 第二个问题是我如何将 IntPtr lpftLastWriteTime 转换为 DateTime?

RegQueryInfoKey的声明中替换

UIntPtr hkey

SafeRegistryHandle handle

并通过 mKey.Handle.

至于文件时间,请不要将其作为IntPtr传递。而是使用 FILETIME 结构。

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME {
    public uint DateTimeLow;
    public uint DateTimeHigh;
}

并像这样声明该参数:

out FILETIME lpftLastWriteTime

或者如 Hans 的有益评论,使用 long

out long lpftLastWriteTime

然后可以直接传递给

DateTime.FromFileTimeUtc()

其他一些评论:

  • 注册表函数不调用 SetLastError
  • 设置EntryPoint有点没意义。
  • 第二个参数应该是StringBuilder lpClass

我猜你是从 pinvoke.net 的声明开始的。可悲的是,这些通常质量很差。

这是一个完整的实现:

MSDN 有帮助 Links :

基础 MSDN Link - https://msdn.microsoft.com/en-us/library/ms724284(v=vs.85).aspx

更改基地中的 msXXXXX 编号 link。

  1. ms724284 - FILETIME 结构
  2. ms724897 - RegOpenKeyEx
  3. ms724837 - RegCloseKey
  4. ms724902 - RegQueryInfoKey
  5. ms724205 - "Other way to convert FILETIME - Changing a File Time to the Current Time"

代码:

代码包含 WinAPI 方法签名(来自 PInvoke.net)

using System;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Text;
using Microsoft.Win32;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace RegistryUtils
{
    public static class RegistryHelper
    {

        [DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        private static extern int RegQueryInfoKey(
            UIntPtr hkey,
            out StringBuilder lpClass,
            ref uint lpcbClass,
            IntPtr lpReserved,
            out uint lpcSubKeys,
            out uint lpcbMaxSubKeyLen,
            out uint lpcbMaxClassLen,
            out uint lpcValues,
            out uint lpcbMaxValueNameLen,
            out uint lpcbMaxValueLen,
            out uint lpcbSecurityDescriptor,
            ref FILETIME lpftLastWriteTime);

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern int RegCloseKey(UIntPtr hKey);


        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        private static extern int RegOpenKeyEx(
          UIntPtr hKey,
          string subKey,
          int ulOptions,
          int samDesired,
          out UIntPtr hkResult);

        private static DateTime ToDateTime(FILETIME ft)
        {
            IntPtr buf = IntPtr.Zero;
            try
            {
                long[] longArray = new long[1];
                int cb = Marshal.SizeOf(ft);
                buf = Marshal.AllocHGlobal(cb);
                Marshal.StructureToPtr(ft, buf, false);
                Marshal.Copy(buf, longArray, 0, 1);
                return DateTime.FromFileTime(longArray[0]);
            }
            finally
            {
                if (buf != IntPtr.Zero) Marshal.FreeHGlobal(buf);
            }
        }

        public static DateTime? GetDateModified(RegistryHive registryHive, string path)
        {
            var lastModified = new FILETIME();
            var lpcbClass = new uint();
            var lpReserved = new IntPtr();
            UIntPtr key = UIntPtr.Zero;

            try
            {
                try
                {
                    var hive = new UIntPtr(unchecked((uint)registryHive));
                    if (RegOpenKeyEx(hive, path, 0, (int)RegistryRights.ReadKey, out key) != 0)
                    {
                        return null;
                    }

                    uint lpcbSubKeys;
                    uint lpcbMaxKeyLen;
                    uint lpcbMaxClassLen;
                    uint lpcValues;
                    uint maxValueName;
                    uint maxValueLen;
                    uint securityDescriptor;
                    StringBuilder sb;
                    if (RegQueryInfoKey(
                                 key,
                                 out sb,
                                 ref lpcbClass,
                                 lpReserved,
                                 out lpcbSubKeys,
                                 out lpcbMaxKeyLen,
                                 out lpcbMaxClassLen,
                                 out lpcValues,
                                 out maxValueName,
                                 out maxValueLen,
                                 out securityDescriptor,
                                 ref lastModified) != 0)
                    {
                        return null;
                    }

                    var result = ToDateTime(lastModified);
                    return result;
                }
                finally
                {
                    if (key != UIntPtr.Zero)
                        RegCloseKey(key);

                }
            }
            catch (Exception ex)
            {
                return null;
            }
        }

    }
}

用法 - 控制台应用程序

using System;
using Microsoft.Win32;
using RegistryUtils;

namespace ConsoleApplicationRegistry
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var dateModified = RegistryHelper.GetDateModified(RegistryHive.LocalMachine, @"SYSTEM\Software\Microsoft");
            if(dateModified.HasValue)
                Console.WriteLine("Key Last Modified Date: {0}", dateModified.Value);
            else
                Console.WriteLine("Error... Try again.");
        }
    }
}