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



这是我在这里的第一个问题,所以如果我违反了一些成文或不成文的规则,请耐心等待。

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

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:TEMPNTUSER.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的调用,它会运行正常并在第一个子项上返回正确的信息(但我显然没有得到有关子项和值的数量或其长度的信息(。

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

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

如果我交换ORQueryInfoKey块和OREnumKey,并在第二次调用之前将ClassLen设置为1,它会运行正常,返回正确的数据,但在ORCloseHive上抛出访问冲突。

因此,总结一下,在成功调用ORQueryInfoKey之后,似乎下一次使用句柄h的调用将产生访问冲突。

我错过了什么?

任何见解都非常感谢!

ORQueryInfoKey 缺少很多参数,FILETIME 相当于一个 .NET long (64 位,DateTime 有一个内置函数来转换它(,你必须用字符串调用 API 两次:首先你传递一个空指针,API 会给你长度,然后你分配长度,再次调用 API(并将分配的指针转换回字符串, 释放缓冲区(,像这样(我添加了一个循环,以便您可以看到它在循环中的工作方式(:

[DllImport(@"C:Program Files (x86)Windows Kits10Redistoffregx64offreg.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 Kits10Redistoffregx64offreg.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:TEMPNTUSER.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);
}
}

最新更新