Search code examples
pinvoke

How to marshal this nested, and Pointer Used C structure in C#


typedef struct pt_input_bir 
{    
PT_BYTE byForm; 

    union {
    PT_BIR *pBIR; ///< Used when byForm = PT_FULLBIR_INPUT */
    PT_LONG lSlotNr; ///< Used when byForm = PT_SLOT_INPUT */
    PT_BYTE abyReserved[20]; /** For future use */
         } InputBIR;
} PT_INPUT_BIR

typedef struct pt_bir {
PT_BIR_HEADER Header; 
PT_BYTE Data[1]; 
} PT_BIR

typedef struct pt_bir_header {
PT_DWORD Length; 
PT_BYTE HeaderVersion; 
PT_BYTE Type; 
PT_WORD FormatOwner; 
PT_WORD FormatID; 
PT_CHAR Quality; 
PT_BYTE Purpose; 
PT_DWORD FactorsMask; 
} PT_BIR_HEADER

and the C function is

PT_STATUS StoreFinger (
IN PT_CONNECTION hConnection,
IN PT_INPUT_BIR *pTemplate,
OUT PT_LONG *plSlotNr
)

Now I need to do the wrapper for the above C function in C#.

How should I marshal the PT_INPUT_BIR* structure and how should I unmarshal it after return of this function?

Please help me to solve this.

/********************** FOR MORE DETAIL ABOUT THIS QUESTION**************************/

  1. C struct and function are defined in above. pls refer there.

  2. C# Struct :

For C# struct declaration i have maintatined two struct for the one C struct. bcz one is for setting the values and another one id for passing to c function.

C# app struct:

[StructLayout(LayoutKind.Sequential)]//for app
    public struct FPINPUTBIR
    {
        public byte byForm;
        public InputBIRType InputBIR;       
    }
    [StructLayout(LayoutKind.Sequential)] // here when i use explicit it throws exception so i         removed it.
    public struct InputBIRType
    {
       // [FieldOffset(0)]
        public FPBIR pBIR;
        //[FieldOffset(0)]
        public int lSlotNr;
        //[FieldOffset(0)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] abyReserved;
    }

     C# wrapper struct:

[StructLayout(LayoutKind.Sequential)]
    public struct FP_INPUTBIR
    {
        public byte byForm;
        public IntPtr mIPBIR;
    }

    [StructLayout(LayoutKind.Explicit, Size = 20, CharSet = CharSet.Ansi)]
    public struct Input_BIRType
    {
        [FieldOffset(0)]
        public IntPtr mBIR;
        [FieldOffset(0)]
        public int lSlotNr;
        //[FieldOffset(8)]
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        //public byte[] abyReserved;
    }

finally i will copy the value from the C# app struct to wrapper struct before the call the C fun()

2a) C# App Side Code is :

 //here mAppMemory is already known value

 FPINPUTBIR lfipdata = new FPINPUTBIR();
            FPDATA lfpdata = new FPDATA();    

                lfipdata.byForm = (byte)eFPVerifyBy.FULLBIR_INPUT;
                lfipdata.InputBIR = new InputBIRType();
                lfipdata.InputBIR.abyReserved = new byte[20];
                lfipdata.InputBIR.pBIR.Data = new        byte[mAppMemory[listBox2.SelectedIndex].Header.Length];
                Array.Copy(mAppMemory[listBox2.SelectedIndex].Data, lfipdata.InputBIR.pBIR.Data, mAppMemory[listBox2.SelectedIndex].Header.Length);
                lfipdata.InputBIR.pBIR.Header = mAppMemory[listBox2.SelectedIndex].Header;

                 Verify(ref lfipdata); //calling from C# APP side to C# wrapper

C# wrapper side:

public int Verify(ref FPINPUTBIR apStoredTemplate )
        {
           // i passed the args (apStoredTemplate ) but throws exception struct mismatch with C struct.

            //here i don't know what should i do. 

           CDLL.StoreFinger(..,ref apStoredTemplate,.. );  //pls refer the C function above
        }

Questions:

  1. Do i really need two C# structures for this.
  2. what should i do inside the C# wrapper function. please remeber i have two C# struct with diff members.

Thanks.


Solution

  • You just need a little extension on what you used in the previous question for PT_BIR. There we marshalled that variable length struct as byte[]. You can use the same code to generate the byte array, and I won't revisit that.

    Next you need the union. That is:

    [StructLayout(LayoutKind.Explicit, Size = 20)]
    public struct PT_INPUT_BIR_UNION
    {
        [FieldOffset(0)]
        public IntPtr pBIR; 
        [FieldOffset(0)]
        public int lSlotNr; // I'm guessing what PT_LONG is
    }
    

    No need to declare the reserved part of the union. The size takes care of that.

    Then PT_INPUT_BIR is

    [StructLayout(LayoutKind.Sequential)]
    public struct PT_INPUT_BIR
    {
        Byte byForm;
        PT_INPUT_BIR_UNION InputBirUnion;
    }
    

    Then you need to use GCHandle to pin the PT_BIR byte array. Let's keep to the same naming as used at that question, and assume that the PT_BIR is held in a byte[] variable named data.

    GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); 
    try
    {
        PT_INPUT_BIR inputBir;
        inputBir.byForm := ...;
        inputBir.InputBirUnion.pBIR = handle.AddrOfPinnedObject();
        // now call StoreFinger passing ref inputBir
    }
    finally
    {
        handle.Free();
    }
    

    When you declare StoreFinger the PT_BIR* parameter should be declared as ref PT_BIR.