P / Invoke帮助期间的AccessViolationException

时间:2009-04-08 23:43:39

标签: c# marshalling sizeof access-violation

从外部DLL调用以下内容时,我一直收到AccessViolationException:

FILES_GetMemoryMapping(MapFile, out size, MapName, out PacketSize, pMapping, out PagePerSector);

我有这样的原型设置:

    [DllImport("Files.DLL", SetLastError = true)]
    public static extern uint FILES_GetMemoryMapping(
        [MarshalAs(UnmanagedType.LPStr)]
        string pPathFile,
        out ushort Size,
        [MarshalAs(UnmanagedType.LPStr)]
        string MapName,
        out ushort PacketSize,
        IntPtr pMapping,
        out byte PagesPerSector);

现在,造成这种情况的论点很可能是第五个(IntPtr pMapping)。我已将此代码从C ++应用程序移植到C#中。上面的第五个参数是一个指向结构的指针,该结构还包含指向另一个结构的指针。以下是我如何设置这些结构:

    [StructLayout(LayoutKind.Sequential)]
    public struct MappingSector
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string Name;
        public uint dwStartAddress;
        public uint dwAliasedAddress;
        public uint dwSectorIndex;
        public uint dwSectorSize;
        public byte bSectorType;
        public bool UseForOperation;
        public bool UseForErase;
        public bool UseForUpload;
        public bool UseForWriteProtect;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct Mapping
    {
        public byte nAlternate;
        [MarshalAs(UnmanagedType.LPStr, SizeConst=260)]
        public string Name;
        public uint NbSectors;
        public IntPtr pSectors;
    }

这些C ++的等价物如下:

typedef struct {
    char*       Name;
    DWORD       dwStartAddress;
    DWORD       dwAliasedAddress;
    DWORD       dwSectorIndex;
    DWORD       dwSectorSize;
    BYTE        bSectorType;
    BOOL        UseForOperation;
    BOOL        UseForErase;
    BOOL        UseForUpload;
    BOOL        UseForWriteProtect;
} MAPPINGSECTOR, *PMAPPINGSECTOR;

typedef struct {
    BYTE            nAlternate;
    char            Name[MAX_PATH]; // MAX_PATH = 260
    DWORD           NbSectors;
    PMAPPINGSECTOR  pSectors;   
} MAPPING, *PMAPPING;

我感觉我在移植这些结构或移植函数原型时做错了。一些Marshaling问题。

这篇文章顶部的函数在我的代码中被调用两次。将pMapping设置为null(这会将值设置为“size”)。然后使用此size参数为新结构分配内存,现在使用指向pMapping的已分配内存空间的指针再次调用该函数。 (pMapping还有另一个结构的指针,在这段时间内也会分配一些空间。)

以下是完成此操作的旧c ++代码:

FILES_GetMemoryMapping((LPSTR)(LPCTSTR)MapFile, &Size, (LPSTR)MapName, &PacketSize, pMapping, &PagePerSector);
// Allocate the mapping structure memory
pMapping = (PMAPPING)malloc(sizeof(MAPPING));
pMapping->NbSectors = 0;
pMapping->pSectors = (PMAPPINGSECTOR) malloc((Size) * sizeof(MAPPINGSECTOR));
printf("mapsectorsize: <%d>\n", football);
printf("pMappingsize: <%d>\n", f2);  
// Get the mapping info
FILES_GetMemoryMapping((LPSTR)(LPCTSTR)MapFile, &Size, (LPSTR)(LPCTSTR)MapName, &PacketSize, pMapping, &PagePerSector);

我最初认为我没有分配正确的空间,所以我尝试了上面的旧C ++代码并发现:

sizeof(MAPPING) = 272
and
sizeof(PMAPPINGSECTOR) = 40

我在C#代码中做了同样的检查,发现了以下内容:

Marshal.SizeOf(new Mapping()) = 16
and
Marshal.SizeOF(new MappingSector()) = 40

我们在这里遇到了问题。 Mapping结构应该是272的大小,但它只有16个。我想我可以快速修复,我在这里手动分配了272而不是16,但它仍然存在AccessViolationException错误。

有关如何解决此问题的任何想法?或者可能还会出现什么问题?

3 个答案:

答案 0 :(得分:1)

'prototype'不是正确的单词,我更喜欢“DLLImport声明”。

我刚刚开始工作。

所以在C ++中:

typedef struct {
    BYTE                        nAlternate;
    char                        Name[MAX_PATH]; // MAX_PATH = 260
    DWORD                       NbSectors;
    PMAPPINGSECTOR      pSectors;       
} 

到C#:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct Mapping
{
    public byte nAlternate;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=260)]
    public char[] Name;
    public uint NbSectors;
    public IntPtr pSectors;
}

字符数组不是字符串,应该被视为一个字符数组....谁会猜到:P

答案 1 :(得分:1)

According to MSDN,你应该为一个固定长度的缓冲区传递一个StringBuilder。尝试以下或某些变体(未经测试):

[StructLayout(LayoutKind.Sequential)]
public struct Mapping
{
    public byte nAlternate;
    [MarshalAs(UnmanagedType.LPStr, SizeConst=260)]
    public StringBuilder Name;
    public uint NbSectors;
    public IntPtr pSectors;

    public Mapping()
    {
        Name = new StringBuilder(259); 
        //This will be a buffer of size 260 (259 chars + '\0')
    }
}

答案 2 :(得分:0)

我还没有完成所有这些工作,我很害怕,但是如果你有'char *'的结构并且你将它们编组为'string',那么你应该小心装饰具有适当CharSet = CharSet.Ansi属性的东西。

添加到您的帖子中有用的一件事是函数的C ++原型(我不会将您的DLLImport声明称为'原型',但这可能只是我。)