views:

421

answers:

2

I need to read a blob field from a database into a c# app.

However the blob field was written to the database by a Delphi App using the following method:

 procedure WriteABlob(Blob : TBlobField; var Buffer; size : integer);
 var 
     s : String;
 begin
     setlength(s,size);
     move(buffer,s[1],Size);
     Blob.Value := S;
 end;

The Structure written to the database is not a simple structure and contains things like

MyVariable : Array[0..3] of String[80];

or worse some of them contain

MyRecord = Packed Record
case byte of
    1: (
        iValue:Integer;
       )
    2: (
        cValue:Char;
       )
end;

I've been trying reading in the bytes from the database and then using

Marshal.PtrToStructure()

in order to move it into the struct

My Struct is defined as follows:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1, Size = 10710)]
    public struct MyBlobField
    {
        ...
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.AnsiBStr,SizeConst = SpecificArraySize)]
        public String[] ArrayofFixedLengthStrings;
        ...
    }

But i get an error when calling Marshal.PtrToStructure():

Cannot marshal field 'ArrayofFixedLengthStrings' of type 'MyBlobField': Invalid managed/unmanaged type combination (String[] must be paired with an ArraySubType of LPStr, LPWStr, BStr or LPTStr).

I was wondering if there was a attribute I could define on a CustomMarshaler which would accept a pairing with a String[]

Any ideas how I could read the contents of the blob into c#?

A: 

My first comment is that the "String[80]" is a fixed-length string (which should be easier to handle in a structure), and in C# you are trying to put it into a "String[]" - which is actually a reference (pointer) to a string.

My next comment is that you could, at worst, try reading it into an array of bytes, and pull out the bytes you need and manipulate them into the destination structure.

Andy Jacobs
I was trying to put it in an array of string! My other thoughts were along the lines of reading in an integer followed by 3 AnsiBStr into normal Strings (i.e Flatten the array and ignore it's size parameter). Does that mean if i use an ICustomMarshal interface and return a pointer instead of an object I can use a CustomMarshaler for an Array? I assumed it was an attribute. This isn't the only blob we have like this and this one alone is 10k I'd prefer not to go down the route of byte arrays just yet.
JamesB
+1  A: 

Figured it out...

Declared a struct String20 as

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct String20
    {
        [MarshalAs(UnmanagedType.U1)]
        public byte StringSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
        public String Value;
    }

Apparently the string size identifier at the beginning of a fixed length string (i.e a delphi ShortString) is only 1 byte. Then changed the definition of header_identifiers to:

       [MarshalAs(UnmanagedType.ByValArray, SizeConst = max_header_identifiers)]
        public String20[] header_identifiers;

Also found that Delphi Packed Boolean need to be cast as

[MarshalAs(UnmanagedType.I1)]
public bool BooleanVar;
JamesB