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

问题描述 投票:0回答:1

我正在尝试这个新功能,并决定使用

dbghelp.dll
SymGetModuleInfo64
作为示例。我编写了自定义编组器并且能够运行它(下面是生成的代码),但是当
ConvertToManaged
的结果分配回引用时,它会抛出 AccessViolationException。 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());
c# interop pinvoke marshalling sourcegenerators
1个回答
0
投票

您不能忽略结构中的其余数据。您告诉本机函数有 1680(?) 个字节可供写入,但实际上要少得多。因此,C# 没有分配足够的空间,这意味着缓冲区溢出,清除了堆栈的其余部分,可能还清除了返回指针和参数。

注意结构体的大小实际上应该是 912 字节,因为

CVData
实际上是一个指针。

至少在末尾添加一个保留字段:

[StructLayout(LayoutKind.Explicit)]
public ref struct Unmanaged
{
    [FieldOffset(0)] public int SizeOfStruct;
    [FieldOffset(580)] public fixed byte LoadedPdbName[256];
    [FieldOffSet(836)] private fixed byte Reserved[76];
}

并且:

[MarshalAs(UnmanagedType.LPStr, SizeConst = 780)]
public string CVData;
© www.soinside.com 2019 - 2024. All rights reserved.