我试图运行从互联网上找到的 .NET C# 控制台应用程序 ( 文章, 代码 )。
它使用了一些 pInvoke 调用,并在某个时候触发了 Marshal.PtrToStringUni(IntPtr ptr, int len)
。
非常奇怪的是,我无法修复这个问题:
- 如果将项目编译为 .NET Framework 4.0,则可以正常工作。
- 在 .NET Framework 4.5 或更高版本下,会抛出
AccessViolationException
(臭名昭著的“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”)。
堆栈跟踪告诉我:
在 System.Buffer.Memmove(Byte* dest, Byte* src, UInt64 len) 中 at System.String.CtorCharPtrStartLength(Char* ptr, Int32 startIndex, Int32 length) 在 System.Runtime.InteropServices.Marshal.PtrToStringUni(IntPtr ptr, Int32 len)
我在网上搜索了几个小时,看到了类似的问题,但没有什么能帮助我的。我绝对需要在我的项目中实现这个功能,并且感觉将其编译为 .Net Framework 4.0 而其他部分是更高版本可能会在未来引起一些麻烦。无论如何,总有一种方法可以让它工作。
有办法让这段代码工作吗?
编辑:我刚刚更新了“code”链接。作者提交了一个新版本。
编辑2:我明白没有直接在这里提供代码不会让人想要帮助。我认为将重点部分链接到文章中会更有效率。
因此,这里是一些背景和代码:
该代码旨在列出由进程锁定的所有文件。首先获取 SYSTEM_HANDLE_INFORMATION 的列表。以下是结构体:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public int ProcessID;
public byte ObjectTypeNumber;
public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
public ushort Handle;
public int Object_Pointer;
public UInt32 GrantedAccess;
}
它遍历集合,并将每个项目发送到GetFileDetails方法中。
以下是该方法,我只复制到try/catch语句,.NET 4.5中失败,在.NET 4.0中成功:
private static FileDetails GetFileDetails(Win32API.SYSTEM_HANDLE_INFORMATION sYSTEM_HANDLE_INFORMATION)
{
FileDetails fd = new FileDetails();
fd.Name = "";
IntPtr ipHandle = IntPtr.Zero;
Win32API.OBJECT_BASIC_INFORMATION objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
Win32API.OBJECT_TYPE_INFORMATION objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
IntPtr ipObjectType = IntPtr.Zero;
Win32API.OBJECT_NAME_INFORMATION objObjectName = new Win32API.OBJECT_NAME_INFORMATION();
IntPtr ipObjectName = IntPtr.Zero;
string strObjectTypeName = "";
string strObjectName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, sYSTEM_HANDLE_INFORMATION.Handle,Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS)) return fd;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int) Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectType.Name.Buffer;
}
try
{
strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
}
catch (AccessViolationException)
{
return null;
}
如果您有兴趣,完整的代码可在上面的链接中找到。 感谢您的帮助。
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
创建了随机指针。为什么要坚持转换Buffer
的值?它也被声明为IntPtr
,所以只需将其传递给Marshal.PtrToStringUni
方法即可。 - Anateus