[Mono-list] Marshaling on Mac

silver83 silver83 at gmail.com
Mon Aug 25 09:49:45 EDT 2008


I am calling a Carbon MAC OS X API function using p/invoke : 

[DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon",
CharSet = CharSet.Ansi)]
internal static extern int FSGetVolumeInfo(
                                       short volume,
                                       uint volumeIndex,
                                       ref short actualVolume, 
                                       uint whichInfo,
                                       ref FSVolumeInfo info,
                                       ref HFSUniStr255 volumeName,
                                       ref FSRef rootDirectory);

while the actual C prototype is :

OSErr FSGetVolumeInfo (
   FSVolumeRefNum volume,
   ItemCount volumeIndex,
   FSVolumeRefNum *actualVolume,
   FSVolumeInfoBitmap whichInfo,
   FSVolumeInfo *info,
   HFSUniStr255 *volumeName,
   FSRef *rootDirectory
);

I'm mostly interested in the info ref parameter, which is of type
FSVolumeInfo*.

Here are the struct defs in C , and following are my C# marshaling targets : 
C :

struct FSVolumeInfo {
   UTCDateTime createDate;
   UTCDateTime modifyDate;
   UTCDateTime backupDate;
   UTCDateTime checkedDate;
   UInt32 fileCount;
   UInt32 folderCount;
   UInt64 totalBytes;
   UInt64 freeBytes;
   UInt32 blockSize;
   UInt32 totalBlocks;
   UInt32 freeBlocks;
   UInt32 nextAllocation;
   UInt32 rsrcClumpSize;
   UInt32 dataClumpSize;
   UInt32 nextCatalogID;
   UInt8 finderInfo[32];
   UInt16 flags;
   UInt16 filesystemID;
   UInt16 signature;
   UInt16 driveNumber;
   short driverRefNum;
};

struct UTCDateTime {
   UInt16 highSeconds;
   UInt32 lowSeconds;
   UInt16 fraction;
};


C# : 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct FSVolumeInfo
    {
        public UTCDateTime createDate;
        public UTCDateTime modifyDate;
        public UTCDateTime backupDate;
        public UTCDateTime checkedDate;
        public UInt32 fileCount;
        public UInt32 folderCount;
        public UInt64 totalBytes;
        public UInt64 freeBytes;
        public UInt32 blockSize;
        public UInt32 totalBlocks;
        public UInt32 freeBlocks;
        public UInt32 nextAllocation;
        public UInt32 rsrcClumpSize;
        public UInt32 dataClumpSize;
        public UInt32 nextCatalogID;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
        public byte[] finderInfo;
        public UInt16 flags;
        public UInt16 filesystemID;
        public UInt16 signature;
        public UInt16 driveNumber;
        public Int16 driverRefNum;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct UTCDateTime
    {
        public UInt16 highSeconds;
        public UInt32 lowSeconds;
        public UInt16 fraction;
    };

The invoking code looks something like this :

//other variable initialization...

FSVolumeInfo volInfo = new FSVolumeInfo();
int status = FSGetVolumeInfo(...ref volInfo.../*and some more params....*/);

//...some more code
//...print results in volInfo ...

I'm getting back garbage in volInfo.

I've compares the results of invoking this method via Mono (managed code)
and the results when writing a native app in C which calls the same API
method. 

It seems like the entire FSVolumeInfo struct I'm getting back using Mono is
garbage....
The values are all wrong and marshaling seems not to work.

I tried changing the UTCDateTime struct in c# to use explicit layout, as
shown in the following : 

    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
    struct UTCDateTime
    {
        [FieldOffset(0)]
        public UInt16 highSeconds;
        [FieldOffset(2)]
        public UInt32 lowSeconds;
        [FieldOffset(6)]
        public UInt16 fraction;
    };

And this makes all the values in the FSVolumeInfo instance up until "byte[]
finderInfo;" look OK, but the rest of the fields after that are garbage
again.

*** Question number 1 : Why do I have to explicitly tell it the struct
layout, it is pretty obviouse here isn't it ? shouldn't this just be simple
blitting ? is it because of the fact that this is a struct being used under
another struct ?

Moving on ...The only thing I could do to make it work is kind of "give up"
on the automatic marshaling of the array, and instead of "byte[]
finderInfo;" replace it with a manually-declared-size struct, which now
looks something like this  :

    [StructLayout(LayoutKind.Sequential, Size = 32)]
    struct FinderInfoArrData
    {
        int values;
    }
   
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct FSVolumeInfo
    {
       //... same as before

         public FinderInfoArrData finderInfo;
      
       // ... the rest is same as before, and now the values here are OK
too...
    }

Changing the entire FSVolumeInfo to be Explicitly defined with [FieldOffset]
(in addition to UTCDateTime being explicit) also works. It seems too
error-prone to me and I'm trying to avoid it. 

My questions are : 
1. Is it a mono issue that the simple struct-withing-struct scenario isn't
blitted well ? or am I missing something while working against the Carbon
libraries in terms of encoding/memory layout...
2. If it is, any knowledge of an open bug/fix planned in following release ?
3. Is it a mono issue that I have to either do fancy tricks and give up on
auto-marshaling of the array in the middle, or set the Entire FSVolumeInfo
struct layout to Explicit ?
4. [same as 2]


Thanks, Yoni.S.


-- 
View this message in context: http://www.nabble.com/Marshaling-on-Mac-tp19144208p19144208.html
Sent from the Mono - General mailing list archive at Nabble.com.



More information about the Mono-list mailing list