Question

I keep getting an AccessViolationException when calling the following from an external DLL:

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

Which has a prototype that I've setup as such:

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

Now, the argument that is causing this is most likely the 5th one (IntPtr pMapping). I've ported this code over from a C++ app into C#. The 5th argument above is a pointer to a struct which also contains a pointer to another struct. Below is how I have these sctructs setup:

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

The C++ equivalent of these are as follows:

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;

I have a feeling I did something wrong with either porting over these structs, or porting over the function prototype. A Marshaling issue of somesort.

The function all the way at the top of this post gets called twice in my code. Once with pMapping set to null (this puts a value in "size"). Memory is then allocated for a new struct using this size parameter and the function is called again now using a pointer to this allocated memory space for pMapping. (pMapping also has a pointer for the other struct which also gets some space allocated during this time).

Here is the old c++ code that accomplished this:

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

I initially thought I wasn't allocating the correct amount of space so I tried the old C++ code above and found out that:

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

I did the same check in my C# code and found the following:

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

We got a problem here. The Mapping struct should be of size 272, but its only 16. Thinking I could just do a quick fix, I manually allocated 272 instead of 16 here, but it still errored out with an AccessViolationException.

Any idea on how to fix this? Or what might still be going wrong?

Was it helpful?

Solution

'prototype' was not the correct word, I like "DLLImport declaration" better.

And I've just got it working.

so in C++:

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

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

A character Array is NOT a string, and should be treated as an array of characters.... Who would have guessed :P

OTHER TIPS

According to MSDN, you should pass a StringBuilder for a fixed-length buffer. Try the following, or some variant (untested):

[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')
    }
}

I haven't worked through all this, I'm afraid, but if you've got structs with 'char*'s in and you're marshalling them as 'string', then you should be careful to be decorating stuff with the appropriate CharSet = CharSet.Ansi attributes.

One thing which would be useful to add to your posting is the C++ prototype for the function (I would not refer to your DLLImport declaration as a 'prototype', but that might just be me.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top