vmm.dll中Map_GetPool迁移至C# vmmsharp的实现问题求解
VMMDLL_Map_GetPoolEx C# 封装问题排查与修正
问题背景
正在将vmmdll.h中的Map_GetPool系列函数迁移封装到C#版vmmsharp中,适配DMA、PCILEECH开发场景,迁移过程中遇到两个报错:
PVMMDLL类型的VMMDLL*指针转换抛出错误,提示无法完成转换操作- 封装函数内明确定义的变量
e被编译器提示未定义
同时需要校验现有封装结构、实现逻辑是否正确。
现有实现代码
常量与Interop结构体定义(内部调用层)
参考函数定义编写的interop层代码如下:
internal static uint VMMDLL_POOLMAP_FLAG_ALL = 0; internal static uint VMMDLL_POOLMAP_FLAG_BIG = 1; [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] internal unsafe struct VMMDLL_MAP_POOLENTRYTAG { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] internal char[] szTag; //szTag 对应union结构: /*internal uint dwTag; internal uint _Filler; internal uint cEntry; internal uint iTag2Map;*/ } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] internal unsafe struct VMMDLL_MAP_POOLENTRY { internal ulong va; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] internal char[] szTag; //szTag 对应位域结构: /*internal uint dwTag; internal byte _ReservedZero; internal byte fAlloc; internal byte tpPool; internal byte tpSS;*/ internal uint cb; internal uint _Filler; } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] internal unsafe struct VMMDLL_MAP_POOL { internal uint dwVersion; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] internal uint[] _Reserved1; internal uint cbTotal; internal uint* piTag2Map; internal VMMDLL_MAP_POOLENTRYTAG* pTag; internal uint cTag; internal uint cMap; internal VMMDLL_MAP_POOLENTRY[] pMap; } [DllImport("vmm.dll", EntryPoint = "VMMDLL_Map_GetPoolEx")] internal static extern unsafe bool VMMDLL_Map_GetPoolEx( byte* pNetMap, ref uint pcbNetMap);
业务层结构体定义
C#业务层自定义的池映射相关结构体、枚举如下:
public unsafe struct MAP_POOL { public uint dwVersion; public uint[] _Reserved1; //public unsafe fixed uint _Reserved1[6]; public uint cbTotal; public uint[] piTag2Map; public MAP_POOLENTRYTAG* pTag; public uint cTag; public uint cMap; public MAP_POOLENTRY[] pMap; } // szTag[5] 后第4位tpPool对应池类型,第5位tpSS对应池子段类型 public enum MAP_POOL_TYPE { MAP_POOL_TYPE_Unknown = 0, MAP_POOL_TYPE_NonPagedPool = 1, MAP_POOL_TYPE_NonPagedPoolNx = 2, MAP_POOL_TYPE_PagedPool = 3 } public enum MAP_POOL_TYPE_SUBSEGMENT { VMM_MAP_POOL_TYPE_SUBSEGMENT_UNKNOWN = 0, VMM_MAP_POOL_TYPE_SUBSEGMENT_NA = 1, VMM_MAP_POOL_TYPE_SUBSEGMENT_BIG = 2, VMM_MAP_POOL_TYPE_SUBSEGMENT_LARGE = 3, VMM_MAP_POOL_TYPE_SUBSEGMENT_VS = 4, VMM_MAP_POOL_TYPE_SUBSEGMENT_LFH = 5 } public unsafe struct MAP_POOLENTRYTAG { //union结构 public char[] szTag; /* * struct { DWORD dwTag; DWORD _Filler; DWORD cEntry; DWORD iTag2Map; }; */ } public unsafe struct MAP_POOLENTRY { public ulong va; public char[] szTag; //public unsafe fixed char szTag[5]; public uint cb; public uint _Filler; }
封装函数实现
public static unsafe MAP_POOLENTRY[] Map_GetPoolEx() { bool result; uint cb = 0; int cbMAP = System.Runtime.InteropServices.Marshal.SizeOf(typeof(vmmi.VMMDLL_MAP_POOL)); int cbENTRY = System.Runtime.InteropServices.Marshal.SizeOf(typeof(vmmi.VMMDLL_MAP_POOLENTRY)); result = vmmi.VMMDLL_Map_GetPoolEx(null, ref cb); if (!result || (cb == 0)) { return new MAP_POOLENTRY[0]; } fixed (byte* pb = new byte[cb]) { result = vmmi.VMMDLL_Map_GetPoolEx(pb, ref cb); if (!result) { return new MAP_POOLENTRY[0]; } vmmi.VMMDLL_MAP_POOL pm = Marshal.PtrToStructure<vmmi.VMMDLL_MAP_POOL>((System.IntPtr)pb); if (pm.dwVersion != vmmi.VMMDLL_MAP_PHYSMEM_VERSION) { return new MAP_POOLENTRY[0]; } MAP_POOLENTRY[] m = new MAP_POOLENTRY[pm.cMap]; for (int i = 0; i < pm.cMap; i++) { vmmi.VMMDLL_MAP_POOLENTRY n = Marshal.PtrToStructure<vmmi.VMMDLL_MAP_POOLENTRY>((System.IntPtr)(pb + cbMAP + i * cbENTRY)); MAP_POOLENTRY e; e.va = n.va; e.cb = n.cb; e.szTag = n.szTag; //m[i] = e; } return m; } }
问题原因与修正方案
1. 变量e提示未定义的问题
这个报错是C#结构体初始化规则导致的:声明MAP_POOLENTRY e;后直接给字段赋值,但char[] szTag是引用类型,C#要求所有结构体字段在使用前必须完成全部赋值,不能只赋值部分字段。
修正方式:
- 用
default初始化结构体后再赋值 - 补全所有字段赋值逻辑,包括注释掉的
m[i] = e数组赋值
示例修正:
MAP_POOLENTRY e = default; // 先完成默认初始化 e.va = n.va; e.cb = n.cb; e.szTag = n.szTag; m[i] = e; // 取消注释完成数组赋值
2. VMMDLL*指针转换错误
当前VMMDLL_Map_GetPoolEx函数签名写错了,对照头文件原函数签名,第一个参数不是byte* pNetMap,而是VMM上下文句柄PVMMDLL pVmm,同时漏了flags参数,传参类型不匹配才会报指针转换错误。
正确的DllImport签名应该是:
[DllImport("vmm.dll", EntryPoint = "VMMDLL_Map_GetPoolEx")] internal static extern unsafe bool VMMDLL_Map_GetPoolEx( IntPtr pVmm, // 传入初始化VMM得到的实例句柄,不需要做指针强转 byte* pNetMap, ref uint pcbNetMap, uint flags = VMMDLL_POOLMAP_FLAG_ALL // 补全原函数的flags参数 );
3. 现有封装结构的其他错误
- 结构体内存布局错误:把
VMMDLL_MAP_POOLENTRY里的szTag定义成char数组,但C#默认char是Unicode 2字节类型,和C端的1字节ANSI char不匹配,会直接导致结构体对齐错乱。要么给结构体加CharSet = CharSet.Ansi特性,要么直接用fixed byte szTag[5]定义更稳妥。 - 内存偏移计算错误:用
pb + cbMAP + i * cbENTRY计算条目地址是错的。VMM返回的内存结构里,pMap是结构体里的指针字段,不是头结构体后面连续排布的数组,需要取pm.pMap指针作为数组基址遍历,不能直接从头指针后面算偏移。 - 版本校验错误:用
VMMDLL_MAP_PHYSMEM_VERSION做版本校验不对,PoolMap有自己的版本常量VMMDLL_MAP_POOL_VERSION,要和这个值比对。 - fixed块写法错误:
fixed (byte* pb = new byte[cb])写法不合法,fixed块不能直接固定new出来的托管数组,要先声明数组变量再固定:byte[] buf = new byte[cb]; fixed(byte* pb = buf) { // 调用逻辑 }
内容的提问来源于stack exchange,提问作者BloodDragon
相关产品推荐
相关产品推荐

