none
Yet another interop question: Vb.net calling C-dll RRS feed

  • Question

  • Hi everyone,

    I am struggling with an interop problem. I'm working with this VB.net service calling a legacy library made in C (called TBN.dll). I don't get the marshalling right!

    Alright, let's start with function singature for the native code:

    long TBAPI TbeAndring (short, 
                short,
                PTB_PU,
                PTB_ANDRING_INFO,
                PTB_PARAMS,
                PTB_PREMIE_NIVA_INFO,
                PTB_PREMIE,
                PTB_FORMAN_INFO,
                PTB_FORMAN,
                PTB_FUNK,
                PTB_PARAMS,
                PTB_PREMIE_NIVA_INFO,
                PTB_PREMIE,
                PTB_FORMAN_INFO,
                PTB_FORMAN,
                PTB_FUNK);
    

    All PTB's are pointers to struct arrays (arrays with no fixed length) and the array lengths is defined by the two first short values. TBAPI is defined as:

    #define TBAPI __declspec( dllexport ) __stdcall
    

    If we look at the struct PTB_ANDRING_INFO it looks like this:

    typedef struct _TB_ANDRING_INFO
    {  
      char    chBerTyp;
      double   dTAdiff;  
      short   sNrPremiebef;
      short   sNrHmom;  
      double   dAndelResten;
      short   sDelAvPU;
    
    } TB_ANDRING_INFO;
    
    typedef TB_ANDRING_INFO TBPTR PTB_ANDRING_INFO;  
    

    Another example, PTB_PARAMS:

    typedef struct _TB_PARAMS
    {
      char  szDatBer     [LEN_DAT + 1];
      char  chBerKod;
      char  szDatTeckning  [LEN_DAT + 1];
      
      //More fields of double, short, etc.. 
    
      PTB_FLYTTAVGIFT ptbflyttavgift;
    
    } TB_PARAMS;
    
    typedef TB_PARAMS TBPTR PTB_PARAMS;
    

    As you see there are some strings declared, like szDatBer which is of length 8.

    I'm doing baby steps here. First I did a declaration of the function TbeAndring in VB.net:

    <DllImport(NativeLibraryName, CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Ansi, SetLastError:=True)>
      Public Shared Function TbeAndring(ByVal sAntMoment As Short, _
                       ByVal sAntPU As Short, _
                       <[In]()> ByVal atbpu As PTB_PU(), _
                       <[In]()> ByRef atbandringinfo() As PTB_ANDRING_INFO, _
                       ByRef atbparamsEfter() As PTB_PARAMS, _
                       ByRef aNivaInfoEfter() As PTB_PREMIE_NIVA_INFO, _
                       ByRef atbpremieEfter() As PTB_PREMIE, _
                       ByRef atbFormanInfoEfter() As PTB_FORMAN_INFO, _
                       ByRef atbFormanEfter() As PTB_FORMAN, _
                       ByRef atbfunkEfter() As PTB_FUNK, _
                       ByRef atbparamsFore() As PTB_PARAMS, _
                       ByRef aNivaInfoFore() As PTB_PREMIE_NIVA_INFO, _
                       ByRef atbpremieFore() As PTB_PREMIE, _
                       ByRef atbFormanInfoFore() As PTB_FORMAN_INFO, _
                       ByRef atbFormanFore() As PTB_FORMAN, _
                       ByRef atbfunkFore() As PTB_FUNK) As Int32
      End Function
    
    Mind that it is a work in progress! Baby steps! PTB_PU is just a struct containing only one double field. From the logs I see that it is mashalled correctly. For the moment I'm sending only 1 value in each array. But the struct PTB_ANDRING_INFO isn't marshalled correctly. This is how I define my struct in VB.net:
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)>
      Friend Structure PTB_ANDRING_INFO
        Public chBerTyp As Char
    
        Public dTAdiff As Double
    
        Public sNrPremiebef As Short
    
        Public sNrHmom As Short
    
        Public dAndelresten As Double
    
        Public sDelAvPu As Short
      End Structure
    

    (In the real code I have XML-comments for each field, but that shouldn't matter). I'm sending a struct which is zeroed except for the chBerTyp which I'm setting to: R and sDelAvPu which is set to 1. I'll paste a piece from the log so you can how the fields are interperated(the log is in swedish, but you can read the field names and it's value:
    Antal moment = 1, antal PU = 1

    PUinformation
        PU = 100.000000


    Ändringsinformation
      Moment nr 1
        Bertyp                            (chBerTyp) = (
        Ändring av återköpsvärde           (dTAdiff) = 0.000000
        Pbmom                         (sNrPremiebef) = 0
        Hmom                               (sNrHmom) = -27648
        Andel av resten av PU         (dAndelResten) = -0.000000
        Del av PU                         (sDelAvPU) = 4576

    The first rows represents my two short values. PUInformation is marshaled correctly but as you see with Ändringsinformation (which is the struct PTB_ANDRING_INFO) the field chBerTyp is not correctly marshaled!

    Well I need some help here. How should I define all structs in Vb.net and how should my DllImport-method be declared? One more thing (as Steve Jobs always says): Some fields in some structs in some arrays are altered and used as output parameters. For example some values in PTB_FUNK is changed during the call so the struct acts as an input and output.

    Any thoughts?

    Regards,
    Joakim.

    Friday, November 26, 2010 9:40 AM

Answers

  • Hi,

    I managed to solve the problem for the moment. At least I can call the function with no errors and I get the result that I expect for a case. The managed declaration for TbeAndring looks like this:

    <DllImport(NativeLibraryName, CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Ansi, SetLastError:=True)>
      Public Shared Function TbeAndring(ByVal sAntMoment As Short, _
                       ByVal sAntPU As Short, _
                       <[In]()> ByVal atbpu As PTB_PU(), _
                       <[In]()> ByVal atbandringinfo() As PTB_ANDRING_INFO, _
                       <[In]()> ByVal atbparamsEfter() As PTB_PARAMS, _
                       <[In]()> ByVal aNivaInfoEfter() As PTB_PREMIE_NIVA_INFO, _
                       <[In](), Out()> ByVal atbpremieEfter() As PTB_PREMIE, _
                       <[In]()> ByVal atbFormanInfoEfter() As PTB_FORMAN_INFO, _
                       <[In](), Out()> ByVal atbFormanEfter() As PTB_FORMAN, _
                       <[In](), Out()> ByVal atbfunkEfter() As PTB_FUNK, _
                       <[In]()> ByVal atbparamsFore() As PTB_PARAMS, _
                       <[In]()> ByVal aNivaInfoFore() As PTB_PREMIE_NIVA_INFO, _
                       <[In](), Out()> ByVal atbpremieFore() As PTB_PREMIE, _
                       <[In]()> ByVal atbFormanInfoFore() As PTB_FORMAN_INFO, _
                       <[In](), Out()> ByVal atbFormanFore() As PTB_FORMAN, _
                       <[In](), Out()> ByVal atbfunkFore() As PTB_FUNK) As Int32
      End Function
    

    As you see all arguments are sent as ByVal, using the InAttribue. For those arrays where an element can change value, I use the OutAttribute. All seem to work as far as I know at least.

    I might need some help in the future, so stay tuned.

    Thanks eryang.

    Regards,
    Joakim.

    Monday, November 29, 2010 1:14 PM

All replies

  •  

    Hi,

     

    Thank you for your question. We're doing research on this issue. It might take some time before we get back to you.


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, November 29, 2010 2:58 AM
  • Hi,

    I managed to solve the problem for the moment. At least I can call the function with no errors and I get the result that I expect for a case. The managed declaration for TbeAndring looks like this:

    <DllImport(NativeLibraryName, CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Ansi, SetLastError:=True)>
      Public Shared Function TbeAndring(ByVal sAntMoment As Short, _
                       ByVal sAntPU As Short, _
                       <[In]()> ByVal atbpu As PTB_PU(), _
                       <[In]()> ByVal atbandringinfo() As PTB_ANDRING_INFO, _
                       <[In]()> ByVal atbparamsEfter() As PTB_PARAMS, _
                       <[In]()> ByVal aNivaInfoEfter() As PTB_PREMIE_NIVA_INFO, _
                       <[In](), Out()> ByVal atbpremieEfter() As PTB_PREMIE, _
                       <[In]()> ByVal atbFormanInfoEfter() As PTB_FORMAN_INFO, _
                       <[In](), Out()> ByVal atbFormanEfter() As PTB_FORMAN, _
                       <[In](), Out()> ByVal atbfunkEfter() As PTB_FUNK, _
                       <[In]()> ByVal atbparamsFore() As PTB_PARAMS, _
                       <[In]()> ByVal aNivaInfoFore() As PTB_PREMIE_NIVA_INFO, _
                       <[In](), Out()> ByVal atbpremieFore() As PTB_PREMIE, _
                       <[In]()> ByVal atbFormanInfoFore() As PTB_FORMAN_INFO, _
                       <[In](), Out()> ByVal atbFormanFore() As PTB_FORMAN, _
                       <[In](), Out()> ByVal atbfunkFore() As PTB_FUNK) As Int32
      End Function
    

    As you see all arguments are sent as ByVal, using the InAttribue. For those arrays where an element can change value, I use the OutAttribute. All seem to work as far as I know at least.

    I might need some help in the future, so stay tuned.

    Thanks eryang.

    Regards,
    Joakim.

    Monday, November 29, 2010 1:14 PM