源生成原生封送中ref参数是否需要特殊处理?
源生成原生封送中ref参数的AccessViolationException问题
我在试用源生成原生封送特性时,选择dbghelp.dll的SymGetModuleInfo64作为示例。编写自定义封送器后,生成的代码能运行,但在将ConvertToManaged的结果赋值给ref参数时抛出AccessViolationException。想请教ref参数是否需要特殊处理?目前我仅实例化IMAGEHELP_MODULE64并设置其SizeOfStruct后传入SymGetModuleInfo64。
生成的P/Invoke代码:
public unsafe partial class Test { [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.10.26716")] [System.Runtime.CompilerServices.SkipLocalsInitAttribute] private static partial bool SymGetModuleInfo64(nint hProcess, long ModuleBase64, ref global::Test.Test.IMAGEHELP_MODULE64 imgHelpModule) { int __lastError; global::Test.Test.IMAGEHELP_MODULE64_Marshaller.Unmanaged __imgHelpModule_native; bool __retVal; int __retVal_native; // Marshal - Convert managed data to native data. __imgHelpModule_native = global::Test.Test.IMAGEHELP_MODULE64_Marshaller.ConvertToUnmanaged(imgHelpModule); { System.Runtime.InteropServices.Marshal.SetLastSystemError(0); __retVal_native = __PInvoke(hProcess, ModuleBase64, &__imgHelpModule_native); __lastError = System.Runtime.InteropServices.Marshal.GetLastSystemError(); } // Unmarshal - Convert native data to managed data. __retVal = __retVal_native != 0; // 此处抛出AccessViolationException imgHelpModule = global::Test.Test.IMAGEHELP_MODULE64_Marshaller.ConvertToManaged(__imgHelpModule_native); System.Runtime.InteropServices.Marshal.SetLastPInvokeError(__lastError); return __retVal; // Local P/Invoke [System.Runtime.InteropServices.DllImportAttribute("dbghelp.dll", EntryPoint = "SymGetModuleInfo64", ExactSpelling = true)] static extern unsafe int __PInvoke(nint hProcess, long ModuleBase64, global::Test.Test.IMAGEHELP_MODULE64_Marshaller.Unmanaged* imgHelpModule); } }
我尝试为参数分配内存确保所有权,但无论如何操作仍会抛出异常。
自定义封送器及结构体定义:
public unsafe partial class Test { [StructLayout(LayoutKind.Sequential)] [NativeMarshalling(typeof(IMAGEHELP_MODULE64_Marshaller))] private struct IMAGEHELP_MODULE64 { public int SizeOfStruct; public long BaseOfImage; public int ImageSize; public int TimeDateStamp; public int CheckSum; public int NumSyms; public SymType SymType; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string ModuleName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string ImageName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string LoadedImageName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string LoadedPdbName; public int CVSig; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 780)] public string CVData; public int PdbSig; public GUID PdbSig70; public int PdbAge; public bool PdbUnmatched; public bool DbgUnmatched; public bool LineNumbers; public bool GlobalSymbols; public bool TypeInfo; public bool SourceIndexed; public bool Publics; public int MachineType; public int Reserved; } [StructLayout(LayoutKind.Sequential)] private struct GUID { public int Data1; public ushort Data2; public ushort Data3; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] data4; } [Flags] private enum SymType : uint { SymNone, SymCoff, SymCv, SymPdb, SymExport, SymDeferred, SymSym, SymDia, SymVirtual, } [CustomMarshaller(typeof(IMAGEHELP_MODULE64), MarshalMode.Default, typeof(IMAGEHELP_MODULE64_Marshaller))] private static unsafe class IMAGEHELP_MODULE64_Marshaller { [StructLayout(LayoutKind.Explicit)] public ref struct Unmanaged { [FieldOffset(0)] public int SizeOfStruct; [FieldOffset(580)] public fixed byte LoadedPdbName[256]; } public static Unmanaged ConvertToUnmanaged(IMAGEHELP_MODULE64 managed) { return new Unmanaged { SizeOfStruct = managed.SizeOfStruct }; } public static IMAGEHELP_MODULE64 ConvertToManaged(Unmanaged unmanaged) { return new IMAGEHELP_MODULE64 { LoadedPdbName = AnsiStringMarshaller.ConvertToManaged(unmanaged.LoadedPdbName) }; } } [LibraryImport("dbghelp.dll", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool SymGetModuleInfo64(nint hProcess, long ModuleBase64, ref IMAGEHELP_MODULE64 imgHelpModule); }
调用代码:
var moduleInfo = new IMAGEHELP_MODULE64(); moduleInfo.SizeOfStruct = Marshal.SizeOf(moduleInfo); if (!SymGetModuleInfo64(_processPtr, _baseOfDll, ref moduleInfo)) throw new Win32Exception(Marshal.GetLastWin32Error());
问题分析与解决
核心问题
- 内存越界写入:你的
Unmanagedref struct仅定义了SizeOfStruct和LoadedPdbName,但SymGetModuleInfo64会向传入的结构体指针写入完整的原生IMAGEHLP_MODULE64数据,导致写入超出Unmanaged结构体内存范围的数据,触发内存损坏,最终在赋值时抛出异常。 - 结构体大小不匹配:
Marshal.SizeOf(moduleInfo)计算的是托管结构体的大小,包含字符串等托管类型的布局开销,和原生结构体实际大小不符,导致SizeOfStruct设置错误,原生函数写入逻辑混乱。 - 字符串处理风险:直接使用
AnsiStringMarshaller.ConvertToManaged处理fixed byte数组,若未正确处理字符串终止符,可能读取超出数组范围的内存。
修复步骤
匹配原生结构体内存布局
必须让Unmanaged结构体的大小和布局与Windows SDK中的IMAGEHLP_MODULE64完全一致,即使你只关心个别字段。可以直接定义完整的原生结构体,或通过Size属性强制匹配大小:[StructLayout(LayoutKind.Sequential, Size = 1048)] // 对应原生IMAGEHLP_MODULE64的标准大小 public ref struct Unmanaged { public int SizeOfStruct; public long BaseOfImage; public int ImageSize; public int TimeDateStamp; public int CheckSum; public int NumSyms; public uint SymType; public fixed byte ModuleName[32]; public fixed byte ImageName[256]; public fixed byte LoadedImageName[256]; public fixed byte LoadedPdbName[256]; // 其余成员可省略,但Size属性必须保证总大小正确 }改用in-place更新逻辑
对于ref参数,自定义封送器应直接更新传入的托管实例,而非返回新对象,避免内存赋值时的异常:public static void ConvertToManaged(Unmanaged unmanaged, ref IMAGEHELP_MODULE64 managed) { // 仅更新关心的字段,保留原有字段值 fixed (byte* p = unmanaged.LoadedPdbName) { managed.LoadedPdbName = Marshal.PtrToStringAnsi((nint)p) ?? string.Empty; } }同时调整封送器的
MarshalMode为ManagedToUnmanagedIn和UnmanagedToManagedOut,让源生成器自动生成in-place更新的代码。修正SizeOfStruct赋值
直接使用原生结构体的标准大小赋值,避免托管结构体大小的干扰:moduleInfo.SizeOfStruct = 1048; // 替换为Windows SDK中定义的sizeof(IMAGEHLP_MODULE64)值
简化替代方案
如果仅关心LoadedPdbName,可以直接使用标准P/Invoke,无需自定义封送器,避免手动处理内存布局:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 1048)] private struct IMAGEHLP_MODULE64 { public int SizeOfStruct; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string LoadedPdbName; } [DllImport("dbghelp.dll", CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SymGetModuleInfo64(nint hProcess, long ModuleBase64, ref IMAGEHLP_MODULE64 imgHelpModule);
内容的提问来源于stack exchange,提问作者Alex Malcoci
相关产品推荐
相关产品推荐

