AccessViolationException во время справки P / Invoke - PullRequest
0 голосов
/ 09 апреля 2009

Я получаю исключение AccessViolationException при вызове следующего из внешней DLL:

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-й аргумент выше - это указатель на структуру, которая также содержит указатель на другую структуру. Ниже описано, как настроить эти sctructs:

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

С ++, эквивалентные следующим:

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, установленным в нуль (это помещает значение в "размер"). Затем память выделяется для новой структуры с использованием этого параметра размера, и теперь функция вызывается снова, используя указатель на это выделенное пространство памяти для 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 ]

1 голос
/ 09 апреля 2009

'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 голос
/ 09 апреля 2009

Согласно 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')
    }
}
0 голосов
/ 09 апреля 2009

Боюсь, я не прошел через все это, но если у вас есть структуры с символами char и вы распределяете их как 'string', тогда вам следует быть осторожным, чтобы украшать вещи с соответствующими атрибутами CharSet = CharSet.Ansi.

Одной вещью, которую было бы полезно добавить в ваше сообщение, является прототип C ++ для функции (я бы не назвал вашу декларацию DLLImport «прототипом», но это может быть только я.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...