locked
Calling return struct using PInvoke RRS feed

  • Question

  • Hi everybody.

     

    Does anybody know how to make P/invoke signature for function which returns custom struct?

    I have code like so:

    //struct 
    typedef struct
    {
         int i;
         double d;
        .
        .
        . // and so on
    }A;
    
    // function
    A callculate(int number)
    {
    }
    
    

    and I am doing

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct A
    {
      int i:
      double d;
      .
      .
      . // and so on
    }
    
    [DllImport(Constants.LibraryPath, EntryPoint = "callculate", CallingConvention = CallingConvention.Cdecl)]
    public static extern void callculate(ref A, int i);
    

    Now here I have a problem..

    code:

    static void Main()
    {
    
         IntPtr pointerA = NativeMethod.InitA();
         A structA = (A)Marshal.PtrToStructure(pointerA, typeof(A));
         // this is OK
    
    
         NativeMethod.Callculate(ref A, 2)
         // Stack unballanced
    }
    

    When I call this function I get PInvokeStackUnballanced error. But some values in struct are filled (????). Does anybody knows anything about this?

     

    Thank you for any hints, help or solution.

     

     

    Sunday, December 11, 2011 9:12 PM

Answers

  • Well, it seems that this will be your first C++ work.  Welcome!  C++ is an amazing language.  Super powerful, but beware of the many pitfalls!  It will actually be a good idea if you were to handle the C++ wrapper to an experienced C++ developer.  If you don't, just remember this:  DON'T allocate memory inside the exported functions.  Always request the memory buffer as a parameter.  Meaning:  The wrapper function must have a parameter of type pointer to structure that receives an empty structure.  The function the fills the structure.  I think the structure you use also receives pointers.  Make sure you know how these pointers are allocated and provide a function that is capable of deleting them in order to prevent memory leaks, but ideally you should not allocate for them either.

    We can deviate from the main topic and continue to discuss many different ways of properly creating the wrapper, so I'll just stop here.  If you want help with this, open a new thread in the C++ forums.


    Jose R. MCP
    • Marked as answer by Paul Zhou Thursday, December 15, 2011 6:05 AM
    Tuesday, December 13, 2011 3:47 PM

All replies

  • The stack unbalanced errors typically occur because either the struct is not defined exactly right, or the calling convention is incorrect.

     

    Can you show your C/C++ declaration for the struct and method, as well as the full C# definitions?

     

    (BTW - A common issue is that a "long" in C++ needs to map to "int" in C#...)

     


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, December 12, 2011 5:51 PM
  • Hey Reed. Thank you for replying.

     

    The API is made in C, without option of modification.

    C declaration of struct:

     

    typedef struct {
        int t1;
        int t2;
    } SAMPLE;
    
    typedef struct {
        int *arlist;          /* list of autoreg lags */
        double *rho;          /* array of autoreg. coeffs. */
        double *sderr;        /* and their standard errors */
    } ARINFO;
    
    typedef struct {
        int ID;                      /* ID number for model */
        int refcount;                /* for saving/deleting */
        int ci;                      /* "command index" -- estimation method */
        gretlopt opt;                /* record of options */
        int t1, t2, nobs;            /* starting observation, ending
                                        observation, and number of obs */
        char *submask;               /* keep track of sub-sample in force
                                        when model was estimated */
        char *missmask;              /* missing observations mask */
        SAMPLE smpl;                 /* numeric start and end of current sample
                                        when model was estimated */
        int full_n;                  /* full length of dataset on estimation */
        int ncoeff, dfn, dfd;        /* number of coefficents; degrees of
                                        freedom in numerator and denominator */
        int *list;                   /* list of variables by ID number */
        int ifc;                     /* = 1 if the equation includes a constant,
    				    else = 0 */
        int nwt;                     /* ID number of the weight variable (WLS) */
        int aux;                     /* code representing the sort of
    				    auxiliary regression this is (or not) */
        double *coeff;               /* array of coefficient estimates */
        double *sderr;               /* array of estimated std. errors */
        double *uhat;                /* regression residuals */
        double *yhat;                /* fitted values from regression */
        double *xpx;                 /* X'X matrix, in packed form */
        double *vcv;                 /* VCV matrix for coefficient estimates */
        double ess, tss;             /* Error and Total Sums of Squares */
        double sigma;                /* Standard error of regression */
        double rsq, adjrsq;          /* Unadjusted and adjusted R^2 */     
        double fstt;                 /* overall F-statistic */
        double chisq;                /* overall chi-square statistic */
        double lnL;                  /* log-likelihood */
        double ybar, sdy;            /* mean and std. dev. of dependent var. */
        double criterion[C_MAX];     /* array of model selection statistics */
        double dw, rho;              /* Durbin-Watson stat. and estimated 1st
    				    order autocorrelation coefficient */
        ARINFO *arinfo;              /* pointer to struct to hold special info for 
    				    autoregressive model */ 
        int errcode;                 /* Error code in case of failure */
        char *name;                  /* for use in GUI */
        char *depvar;                /* name of dependent var in special cases */
        int nparams;                 /* number of named model parameters */
        char **params;               /* for named model parameters */
        int ntests;                  /* number of attached test results */
        ModelTest *tests;            /* attached hypothesis test results */
        DATASET *dataset;            /* for handling models estimated on a
    				    sub-sampled portion of the dataset */
        int n_data_items;            /* number of extra data items */
        model_data_item **data_items; /* pointer to additional data */
    } MODEL;
    
    

     

     

    C Function declaration:

    MODEL               lad                                 (const int *list,
                                                             DATASET *dset);

     

     

    Ok in C# I am doing....

     

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct MODEL
            {
                public int ID;           // ID number for model
                public int refcount;     // for saving/deleting
                public int ci;           // "command index" -- estimation method
                public Enums.Gretlopt opt;     // record of options gretlopt
                public int t1;           // starting observation
                public int t2;           // ending observation
                public int nobs;         // number of observations
                public IntPtr submask;
                public IntPtr missmask;
                public SAMPLE smpl;          // numeric start and end of current sample when model was estimated
                public int full_n;           // full length of datase on esimation          
                public int ncoeff;           // number of coefficents
                public int dfn;              // degress of freedom in numerator
                public int dfd;              // degress of freedom in denominator
                public IntPtr list;
                public int ifc;             // =1 if the equation includes a constant, else = 0 
                public int nwt;             // ID number of the weight variable (WLS)
                public int aux;             // code representing the sort of auxiliary regression this is (or not)
                public IntPtr coeff;
                public IntPtr sderr;
                public IntPtr uhat;
                public IntPtr yhat;
                public IntPtr xpx;
                public IntPtr vcv;
                public double ess;              // Error 
                public double tss;              // Total Sums of Squares
                public double sigma;            // Standart error of regression
                public double rsq;              // Unadjusted R^2
                public double adjrsq;           // Adjusted R^2
                public double fstt;             // overall F-statistic
                public double chisq;            // overall chi-square statistic
                public double lnL;              // log-likelihood
                public double ybar;             // mean dev. of depandent var.
                public double sdy;              // std. dev. of depandent var.
    
                [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
                public double[] criterion;
                public double dw;               // Durbin-Watson stat. 
                public double rho;              // estimated 1st order autocorrelation coefficient
                public IntPtr arinfo;           // ARINFO *arinfo -- pointer to struct to hold special info for autoregressive model
                public int errcode;             // Error code in case of failure
                public IntPtr name;            // for use in GUI   --char*
                public IntPtr depvar;
                public int nparams;             // number of named model parameter 
                public IntPtr params1;
                public int ntests;              // number of attached test results
                public IntPtr tests;            // ModelTest *tests -- attached hypothesis test results
                public IntPtr dataset;          // for handeling models estimated on a sub-sampled portion of the dataset
                public int n_data_items;        // number of extra data items
                public IntPtr data_items;       // model_data_item **data_items -- pointer to additional data
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public struct SAMPLE
            {
                public int t1;
                public int t2;
            }
    

     


    C# function:

     

    /// <summary>
    /// Estimate the model given in list using the method of Least Absolute Deviation (LAD). 
    /// </summary>
    /// <param name="list">	dependent variable plus list of regressors.</param>
    /// <param name="dataset">dataset struct.</param>
    /// <returns></returns>
    [DllImport(Constants.gretlLibraryPath, EntryPoint = "lad", CallingConvention = CallingConvention.Cdecl)]
    public static extern void lad(ref Structures.MODEL model, IntPtr list, ref Structures.DATASET dataset);
    

     

     

    The problem is calling function in C# which calls a stuct. 


    Hope this helps ilustrate the problem.

    Monday, December 12, 2011 9:27 PM
  • Is C_MAX defined as 4 in your C project?



    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, December 12, 2011 9:34 PM
  • yes C_MAX is 4
    Monday, December 12, 2011 11:36 PM
  • The problem I see here is that the function's return value is a copy of the struct, which you try to capture assuming Interop will place it in the first argument.  Do you have documentation that states that .Net's Interop works this way?  Because it is the first time I see this.

    If you don't have supporting documentation, then my guess is that the error reflects this invalid assumption.

    Never have I had the experience of using a function with this type of signature, but I am guessing this function simply cannot be marshaled.  If it returned a pointer to struct, then you could declare the function's return value as type IntPtr, but I just don't see how you can specify this function's signature.  I might be wrong, though.

    Have you tried declaring the function as returning the struct instead of listing it as the first parameter?


    Jose R. MCP
    Tuesday, December 13, 2011 12:00 AM
  • Yes - that's likely the issue. 

     

    You can return the struct directly. The runtime will marshal the struct across.  You shouldn't be passing it by ref.

     


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Tuesday, December 13, 2011 12:24 AM
  • Yes first time I tried using directly. Like so

    /// <summary>
    /// Estimate the model given in list using the method of Least Absolute Deviation (LAD). 
    /// </summary>
    /// <param name="list">	dependent variable plus list of regressors.</param>
    /// <param name="dataset">dataset struct.</param>
    /// <returns></returns>
    [DllImport(Constants.gretlLibraryPath, EntryPoint = "lad", CallingConvention = CallingConvention.Cdecl)]
    public static extern Structures.MODEL model lad(IntPtr list, ref Structures.DATASET dataset);
    

    But that causes "Method's type signature is not PInvoke compatible."

    I also tried to return struct as an IntPtr but still got the same error. Doing some reserch on MSDN on forums I have found thead which says that you should return struct this way. But It works on C++. I thought it will work the same. Will try to find that tread and post the link....
    Thank you for "bearing" with me.
    Tuesday, December 13, 2011 8:28 AM
  • It probably works as long as you change the function's signature in the DLL, which you say you cannot.  At this point I believe that function simply cannot be marshaled.

    Workaround:  Create a new DLL that makes the call to this immutable DLL and return the struct via a pointer parameter as you are trying to do now.


    Jose R. MCP
    Tuesday, December 13, 2011 1:42 PM
  • Yes I think this would work if I change DLL. :)

    I was hoping in solution in P/Invoke principle. I was recommended to make c++ wrapper that will handle this situation, but I haven't made anything with c++ yet. So I'm hopping to some kind of solution in managed "world".

    Tuesday, December 13, 2011 3:37 PM
  • Well, it seems that this will be your first C++ work.  Welcome!  C++ is an amazing language.  Super powerful, but beware of the many pitfalls!  It will actually be a good idea if you were to handle the C++ wrapper to an experienced C++ developer.  If you don't, just remember this:  DON'T allocate memory inside the exported functions.  Always request the memory buffer as a parameter.  Meaning:  The wrapper function must have a parameter of type pointer to structure that receives an empty structure.  The function the fills the structure.  I think the structure you use also receives pointers.  Make sure you know how these pointers are allocated and provide a function that is capable of deleting them in order to prevent memory leaks, but ideally you should not allocate for them either.

    We can deviate from the main topic and continue to discuss many different ways of properly creating the wrapper, so I'll just stop here.  If you want help with this, open a new thread in the C++ forums.


    Jose R. MCP
    • Marked as answer by Paul Zhou Thursday, December 15, 2011 6:05 AM
    Tuesday, December 13, 2011 3:47 PM
  • Thank you webJose, let's give it a try. I would pass this assignment to some experienced C++ developer or ask for his help, but I am solo on this project. You can call it "One man band". :) 

    I have made a tread in c++ Forums, link: http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/93fd5a1a-0095-4c48-93fa-b0de343a532f 

     

    Still hoping for some different solution.

    Tuesday, December 13, 2011 9:02 PM
  • Hi,

     

    Has your issue been resolved? Would you mind letting us know the result of the suggestions? I see that you have marked answers in your new posted thread.

     

    Now I will mark an answer, you can mark others that you think to be so useful to your issue.

    If you still have any questions about this issue, please feel free to let me know. 

     

    Have a nice day!


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us
    Thursday, December 15, 2011 6:05 AM