views:

235

answers:

4

I have this piece of code (from the nokia pc connectivity 3.2 example code, in C#)

  DAContentAccessDefinitions.CA_FOLDER_INFO folderInfo =
  new DAContentAccessDefinitions.CA_FOLDER_INFO();
  folderInfo.iSize = Marshal.SizeOf(folderInfo); //(32)

  IntPtr bufItem = Marshal.AllocHGlobal(folderInfo.iSize);

  //I often get a AccessViolationException on the following line
  Marshal.StructureToPtr(folderInfo, bufItem, true);

If I run GC.Collect() at the start of this, then I don't get an AccessViolationException. But I don't want to slow down this function unless necessary. I've tried putting GC.Keepalive in various places, but without success.

CA_FOLDER_INFO is defined as:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
    public struct CA_FOLDER_INFO
    {
        public int iSize;
        public int iFolderId;
        public int iOptions;
        public string pstrName;
        public string pstrPath;
        public int iSubFolderCount;
        public IntPtr pSubFolders;
        public IntPtr pParent;
    }

I don't, in this instance, require either of the strings, and changing their definitions to IntPtr seems to make the exception go away.

What is going on here, and what is the correct way to prevent the exception?

A: 

Can't you just use the fixed keyword to get a pointer to your original folderInfo?

Blindy
A: 

Could it be that unmanaged resources aren't being release by something. Check to see if anything you're using implements IDisposable and if so, wrap it in a using { } block.

SillyMonkey
A: 

Are you sure Marshal.Sizeof(bufItem) and Marshal.Sizeof(folderInfo) are the same?

And, maybe the fact that you aren't initializing the strings? Since you say you don't get the error when they are IntPtr (which defaults to IntPtr.Zero), I'd try setting them both to empty strings before you try marshaling the buffer item.

[Edit]

Maybe you should try pinning the buffer handle, and marshaling that to the structure, rather than vis versa. Something like this:

DAContentAccessDefinitions.CA_FOLDER_INFO folderInfo;

GCHandle pinnedHandle = GCHandle.Alloc(buffItem, GCHandleType.Pinned);
folderInfo = (DAContentAccessDefinitions.CA_FOLDER_INFO)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(DAContentAccessDefinitions.CA_FOLDER_INFO));
pin.Free();

//folderInfo should contain the data from buffItem
scottm
Setting the two string fields to "" or String.Empty does not make the AccessViolationException go away...
Greg
And the sizes are the same.
Greg
+3  A: 

Your problem is that you're passing true to Marshal.StructureToPtr so it attempts to free the two string pointers (which are sometimes invalid). You need to pass false in this instance since you just allocated that memory on the heap. (i.e. there's nothing to free there).

Remi Lemarchand
Thanks, that seems to work, and is an explanation I understand.
Greg