P/Invoke offreg.dll 函数 - 后续调用的访问冲突

P/Invoke offreg.dll functions - Access violation on subsequent calls

这是我的第一个问题,所以如果我违反了一些成文或不成文的规定,请多多包涵。

我正在尝试使用 C# 中 offreg.dll 的 P/Invoke 函数。为了了解它们的工作原理,我编写了这个非常基本的控制台应用程序:

using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp2
{

    class Program
    {
        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "ORGetVersion", CharSet = CharSet.Unicode)]
        public static extern void ORGetVersion(out uint MajorVersion, out uint MinorVersion);

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "OROpenHive", CharSet = CharSet.Unicode)]
        public static extern int OROpenHive(string HivePath, out IntPtr rootKeyHandle);

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "OROpenKey", CharSet = CharSet.Unicode)]
        public static extern int OROpenKey(IntPtr KeyHandle, string SubKeyName, out IntPtr SubKeyHandle);

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "ORCloseKey", CharSet = CharSet.Unicode)]
        public static extern int ORCloseKey(IntPtr KeyHandle);

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "OREnumKey", CharSet = CharSet.Unicode)]
        public static extern int OREnumKey( IntPtr KeyHandle, 
                                            uint Index, 
                                            [MarshalAs(UnmanagedType.LPWStr)]
                                            StringBuilder SubKeyName, 
                                            ref uint SubKeyLen, 
                                            [MarshalAs(UnmanagedType.LPWStr)]
                                            StringBuilder ClassName, 
                                            ref uint ClassLen, 
                                            out uint LastWriteTime
            );

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "ORQueryInfoKey", CharSet = CharSet.Unicode)]
        public static extern int ORQueryInfoKey(IntPtr KeyHandle, 
                                            [MarshalAs(UnmanagedType.LPWStr)]
                                            StringBuilder ClassName, 
                                            ref uint ClassLen, 
                                            out uint NumKeys, 
                                            out uint MaxKeyLen, 
                                            out uint NumVals, 
                                            out uint MaxValLen, 
                                            out IntPtr SecDesc, 
                                            out uint LastWrite
            );

        [DllImport("C:\TEMP\offreg-x64.dll", EntryPoint = "ORCloseHive", CharSet = CharSet.Unicode)]
        public static extern int ORCloseHive(IntPtr rootKeyHandle);

        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            int res = 0;    // result of every OR function call
            IntPtr h;       // hive (or root key) handle


            // Get DLL version
            uint ver1 = 0;
            uint ver2 = 0;
            Program.ORGetVersion(out ver1, out ver2);
            Console.WriteLine("Library Version: " + ver1.ToString() + "." + ver2.ToString());
            // end get version

            // Open hive
            res = Program.OROpenHive(@"C:\TEMP\NTUSER.DAT", out h);
            Console.WriteLine("Open Result: " + res.ToString());
            Console.WriteLine("Open Handle: " + h.ToString());
            // end oppen hive

            // prepare variables for key operations
            StringBuilder ClassName = new StringBuilder(256);
            uint ClassLen = 256;
            StringBuilder SubKeyName = new StringBuilder(256);
            uint SubKeyLen = 256;
            uint NKeys = 0;
            uint maxKeyLen = 0;
            uint NVals = 0;
            uint maxValLen = 0;
            IntPtr SecDesc = IntPtr.Zero;
            uint LastWrite = 0;
            uint SubKeyIndex = 0;   // we are asking for the first subkey
            // end prepare variables

            // Query root key information
            res = Program.ORQueryInfoKey(h, ClassName, ref ClassLen, out NKeys, out maxKeyLen, out NVals, out maxValLen, out SecDesc, out LastWrite);
            Console.WriteLine("Query Result: " + res.ToString());
            Console.WriteLine("Query Class Name: " + ClassName.ToString());
            Console.WriteLine("Query Class Length: " + ClassLen.ToString());
            Console.WriteLine("Query Num Subkeys: " + NKeys.ToString());
            Console.WriteLine("Query Max Subkey Len: " + maxKeyLen.ToString());
            Console.WriteLine("Query Num Values: " + NVals.ToString());
            Console.WriteLine("Query Max Value Len: " + maxValLen.ToString());
            Console.WriteLine("Query Last Write: " + LastWrite.ToString());
            // end query root key information

            // enum first subkey
            res = Program.OREnumKey(h, SubKeyIndex, SubKeyName, ref SubKeyLen, ClassName, ref ClassLen, out LastWrite);
            Console.WriteLine("Enum Result: " + res.ToString());
            Console.WriteLine("Enum Builder: " + SubKeyName.ToString());
            Console.WriteLine("Enum Length: " + SubKeyLen.ToString());
            Console.WriteLine("Enum Capacity: " + SubKeyName.Capacity.ToString());
            // end enum first subkey

            // close hive
            res = Program.ORCloseHive(h);
            Console.WriteLine("Close Result: " + res.ToString());
            // end close hive

            // sayonara
            Console.WriteLine("Press [ENTER] to exit");
            Console.ReadLine();
        }
    }
}

当我 运行 如上所示的代码时,它将 运行 ORQueryInfoKey 块,但在尝试调用 OREnumKey.

如果我注释掉对 ORQueryInfoKey 的调用,它 运行 没问题并且 returns 正确了第一个子项的信息(但我显然没有t 获取有关子项和值的数量或其长度的信息)。

如果我注释掉对 OREnumKey 的调用,访问冲突仍然发生在 ORCloseHive 上。

如果我交换 ORQueryInfoKey 块和 OREnumKey 块,它不会抛出任何异常,但查询调用 returns 234 (MORE_DATA_AVAILABLE) 这意味着ClassLen太短了。

如果我交换 ORQueryInfoKey 块和 OREnumKey 并在第二次调用前将 ClassLen 设置为 1,它 运行 没问题,returns 正确数据但在 ORCloseHive.

上引发访问冲突

总结一下,在成功调用 ORQueryInfoKey 之后,下一次使用句柄 h 的调用似乎将产生一个 Access违规。

我错过了什么?

非常感谢任何见解!

ORQueryInfoKey was missing a lot of parameters, FILETIME相当于一个.NET long(64位,DateTime有内置函数转换),必须两次调用API字符串:首先你传递一个空指针, API 会给你长度,然后你分配长度,再次调用 API (并将分配的指针转换回字符串,释放缓冲区),一些东西像这样(我添加了一个循环,所以你可以看到它是如何在循环中工作的):

[DllImport(@"C:\Program Files (x86)\Windows Kits\Redist\offreg\x64\offreg.dll", CharSet = CharSet.Unicode)]
public static extern int OREnumKey(IntPtr Handle,
                                    int dwIndex,
                                    IntPtr lpName,
                                    ref int lpcName,
                                    IntPtr lpClass,
                                    ref int lpcClass,
                                    out long lpftLastWriteTime);

[DllImport(@"C:\Program Files (x86)\Windows Kits\Redist\offreg\x64\offreg.dll", CharSet = CharSet.Unicode)]
public static extern int ORQueryInfoKey(
                            IntPtr Handle,
                            IntPtr lpClass,
                            ref int lpcClass,
                            out int lpcSubKeys,
                            out int lpcMaxSubKeyLen,
                            out int lpcMaxClassLen,
                            out int lpcValues,
                            out int lpcMaxValueNameLen,
                            out int lpcMaxValueLen,
                            out int lpcbSecurityDescriptor,
                            out long lpftLastWriteTime);

static void Main()
{
    const int ERROR_MORE_DATA = 234;
    const int ERROR_NO_MORE_ITEMS = 259;

    var res = OROpenHive(@"c:\TEMP\NTUSER.DAT", out var h);
    if (res != 0)
        throw new Win32Exception(res);

    try
    {
        var clsLen = 0;
        var clsPtr = IntPtr.Zero;
        res = ORQueryInfoKey(h, IntPtr.Zero, ref clsLen, out var subKeys, out var maxSubKeyLen, out var maxClassLen, out var values, out var maxValueNameLen, out var maxValueLen, out var securityDescriptor, out var lastWriteTime);
        if (res == ERROR_MORE_DATA)
        {
            clsPtr = Marshal.AllocHGlobal(clsLen);
            try
            {
                res = ORQueryInfoKey(h, clsPtr, ref clsLen, out subKeys, out maxSubKeyLen, out maxClassLen, out values, out maxValueNameLen, out maxValueLen, out securityDescriptor, out lastWriteTime);
                if (res == 0)
                {
                    var cls = Marshal.PtrToStringUni(clsPtr);
                    Console.WriteLine("Class: " + cls);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(clsPtr);
            }
        }

        if (res != 0)
            throw new Win32Exception(res);

        var nameLen = 0;
        var i = 0;
        var namePtr = IntPtr.Zero;

        do
        {
            clsLen = 0;
            nameLen = 0;
            res = OREnumKey(h, i, IntPtr.Zero, ref nameLen, IntPtr.Zero, ref clsLen, out lastWriteTime);
            if (res == ERROR_NO_MORE_ITEMS)
                break;

            if (res == ERROR_MORE_DATA)
            {
                if (nameLen > 0)
                {
                    namePtr = Marshal.AllocHGlobal(nameLen);
                }

                if (clsLen > 0)
                {
                    clsPtr = Marshal.AllocHGlobal(clsLen);
                }

                try
                {
                    res = OREnumKey(h, i, namePtr, ref nameLen, clsPtr, ref clsLen, out lastWriteTime);
                    if (res == 0)
                    {
                        Console.WriteLine("LastWriteTime: " + DateTime.FromFileTime(lastWriteTime));

                        if (namePtr != IntPtr.Zero)
                        {
                            var name = Marshal.PtrToStringUni(namePtr);
                            Console.WriteLine("Name: " + name);
                        }

                        if (clsPtr != IntPtr.Zero)
                        {
                            var cls = Marshal.PtrToStringUni(clsPtr);
                            Console.WriteLine("Class: " + cls);
                        }
                    }
                }
                finally
                {
                    if (namePtr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(namePtr);
                        namePtr = IntPtr.Zero;
                    }

                    if (clsPtr != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(clsPtr);
                        clsPtr = IntPtr.Zero;
                    }
                }
            }

            if (res != 0)
                throw new Win32Exception(res);

            i++;
        }
        while (true);
    }
    finally
    {
        ORCloseHive(h);
    }
}