代码之家  ›  专栏  ›  技术社区  ›  Alex Malcoci

在源代码生成的本机封送处理中,ref参数是否需要特殊处理?

  •  1
  • Alex Malcoci  · 技术社区  · 1 年前

    我一直在尝试这个新功能,并决定使用 dbghelp.dll s SymGetModuleInfo64 作为一个例子。我编写了自定义封送器,我可以运行它(下面是生成的代码),但当结果为 ConvertToManaged 被重新分配给ref。ref参数需要特殊处理吗?目前,我只是在实例化一个 IMAGEHELP_MODULE64 并设置其 SizeOfStruct 在将其传递给 SymGetModuleInfo64 .

        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;
    ----->      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);
            }
        }
    

    我尝试为参数分配内存,以确保我拥有它,但无论我做什么,我仍然会得到 AccessViolationException .

    这是什么 Test 生成上述代码的类看起来像(注意:我只对 LoadedPdbName 所以我没有为整个结构体实现编组,但这应该没关系,PInvoke调用成功了):

    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());
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Charlieface    1 年前

    我以为 Unmanaged 不需要完全充实,因为它承载了结构体的整个长度 SizeOfStruct 进去的时候。

    你不能忽略结构中的其余数据。您告诉本机函数可以写入1680(?)字节,但实际上要少得多。因此,C#没有分配足够的空间,这意味着你的缓冲区溢出,清除了堆栈的其余部分,可能还有你的返回指针和参数。然后,当你尝试分配 ref 价值 裁判 位置已被垃圾或零覆盖,因此您会得到一个AVE。

    请注意,你的结构体的大小实际上应该是912个字节,因为 CVData 实际上是一个指针。

    至少,在末尾添加一个保留字段,或显式设置结构体大小:

    [StructLayout(LayoutKind.Explicit, Size = 912)]
    public ref struct Unmanaged
    {
        [FieldOffset(0)] public int SizeOfStruct;
        [FieldOffset(580)] public fixed byte LoadedPdbName[256];
    }
    

    以及:

    [MarshalAs(UnmanagedType.LPStr)]
    public string CVData;
    

    最好的办法是用所有正确的缓冲区重新定义整个结构,并读取和复制所需的值。

    [StructLayout(LayoutKind.Sequential)]
    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;
    
        public fixed byte ModuleName[32];
    
        public fixed byte ImageName[256];
    
        public fixed byte LoadedImageName[256];
    
        public fixed byte LoadedPdbName[256];
    
        public int CVSig;
    
        public nint 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;
    }