0

从外部 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);

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

    [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;

我觉得我在移植这些结构或移植函数原型时做错了。某种编组问题。

这篇文章顶部的函数在我的代码中被调用了两次。一旦 pMapping 设置为 null(这会将一个值放入“大小”中)。然后使用此大小参数为新结构分配内存,并且现在使用指向此分配内存空间的指针再次调用该函数以进行 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。

关于如何解决这个问题的任何想法?或者还有什么问题?

4

3 回答 3

1

“原型”不是正确的词,我更喜欢“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

于 2009-04-08T23:57:01.267 回答
1

根据 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')
    }
}
于 2009-04-09T00:11:08.547 回答
0

恐怕我还没有完成这一切,但是如果你有带有'char *'的结构并且你将它们编组为'string',那么你应该小心用适当的 CharSet = CharSet.Ansi 属性。

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

于 2009-04-08T23:48:54.390 回答