将结构指针作为参数传递给C#

时间:2010-02-19 15:55:21

标签: c# struct pointers marshalling

我在C ++中有一个导出到DLL的函数。我包含一个struct指针作为参数之一。我需要在C#中使用这个函数,所以我使用DLLImport作为函数,并使用StructLayout在C#中重新创建了struct。我尝试使用ref传递参数,并尝试使用MarshalAs(UnmangedType.Struct)和Marshal.PtrToStructure进行Marshaling。参数仍未正确传递。

示例:

[DllImport("testdll.dll")]
public static extern int getProduct(int num1, int num2, [MarshalAs(UnmanagedType.Struct)] ref test_packet tester);

另外一小段信息,结构包含一个字节* var,我认为这可能导致将param作为ref传递的问题。有任何想法吗?我是在正确的轨道上吗?谢谢你的帮助。

感谢nobugz的回复。这是struct def的一个例子:

//C# DEFINITION 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 

public struct test_packet 
{ 

     public UInt32 var_alloc_size; 

    public byte* var; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_TAG_LENGTH)] 
    public byte[] tag; 

} 

//ORIGINAL UNMANAGED STRUCT

typedef struct test_packet_tag 
{

    unsigned int var_alloc_size;

    unsigned char *var;

    unsigned char tag[MAX_TAG_LENGTH];
} test_packet;

3 个答案:

答案 0 :(得分:4)

使用“ref”是正确的方法,摆脱[MarshalAs]属性。真正的问题几乎可以肯定是结构宣言。你没有发布任何可以帮助我们帮助你的东西。


DllImportAttribute.CharSet属性是错误的,使它成为CharSet.Ansi。 “var”成员声明错误,使其成为byte []。请务必在通话前对其进行初始化:

 var product = new test_packet();
 product.var_alloc_size = 666;    // Adjust as needed
 product.var = new byte[product.var_alloc_size];
 int retval = getProduct(42, 43, ref product);

最好的猜测,希望它有效。

答案 1 :(得分:1)

这是我个人的一个例子。它可能非常复杂。请注意,将数组作为指针移动并不容易,因此您应该看看如何在c#端执行此操作。这应该会涉及很多主要的数据类型。您必须确保您的元素完全排列。否则它会看起来像它的工作,但你会得到糟糕的ptrs(充其量)。移动这个结构时我遇到了很多麻烦,这是唯一有效的方法。祝你好运

功能sig -

  [DllImport("stochfitdll.dll", EntryPoint = "Init", ExactSpelling = false,  CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    public static extern void Init([MarshalAs(UnmanagedType.LPStruct)] ModelSettings settings);

C ++方

#pragma pack(push, 8)
struct ReflSettings
{
    LPCWSTR Directory;
    double* Q;
    double* Refl;
    double* ReflError;
    double* QError;
    int QPoints;
    double SubSLD;
    double FilmSLD;
    double SupSLD;
    int Boxes;
    double FilmAbs;
    double SubAbs;
    double SupAbs;
    double Wavelength;
    BOOL UseSurfAbs;
    double Leftoffset;
    double QErr;
    BOOL Forcenorm;
    double Forcesig;
    BOOL Debug;
    BOOL XRonly;
    int Resolution;
    double Totallength;
    double FilmLength;
    BOOL Impnorm;
    int Objectivefunction;
    double Paramtemp;
    LPCWSTR Title;

 };
 #pragma pack(pop)

C#side -

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)]
public class ModelSettings:IDisposable
{
    #region Variables

    public string Directory;
    public IntPtr Q;
    public IntPtr Refl;
    public IntPtr ReflError;
    public IntPtr QError;
    public int QPoints;
    public double SubSLD;
      public double SurflayerSLD;
        public double SupSLD;
         public int Boxes;
         public double SurflayerAbs;
         public double SubAbs;
       public double SupAbs;
        public double Wavelength;
         public bool UseAbs;
         public double SupOffset;
     public double Percerror;
       public bool Forcenorm;
         public double Forcesig;
         public bool Debug;
         public bool ForceXR;
    public int Resolution;
    public double Totallength;
    public double Surflayerlength;
    public bool ImpNorm;
    public int FitFunc;
    public double ParamTemp;
    public string version = "0.0.0";

    [XmlIgnoreAttribute] private bool disposed = false;

#endregion

    public ModelSettings()
    { }

    ~ModelSettings()
    {
        Dispose(false);
    }


    #region Public Methods
    public void SetArrays(double[] iQ, double[] iR, double[] iRerr, double[] iQerr)
    {
        //Blank our arrays if they hold data
        if (Q == IntPtr.Zero)
            ReleaseMemory();

        int size = Marshal.SizeOf(iQ[0]) * iQ.Length;

            try
            {
                QPoints = iQ.Length;
                Q = Marshal.AllocHGlobal(size);
                Refl = Marshal.AllocHGlobal(size);
                ReflError = Marshal.AllocHGlobal(size);

                if (iQerr != null)
                    QError = Marshal.AllocHGlobal(size);
                else
                    QError = IntPtr.Zero;

                Marshal.Copy(iQ, 0, Q, iQ.Length);
                Marshal.Copy(iR, 0, Refl, iR.Length);
                Marshal.Copy(iRerr, 0, ReflError, iRerr.Length);

                if (iQerr != null)
                    Marshal.Copy(iQerr, 0, QError, iQerr.Length);
            }
            catch (Exception ex)
            {
               //error handling
            }
    }
    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            ReleaseMemory();
            // Note disposing has been done.
            disposed = true;
        }
    }

    private void ReleaseMemory()
    {
        if (Q != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(Q);
                Marshal.FreeHGlobal(Refl);
                Marshal.FreeHGlobal(ReflError);

                if (QError != IntPtr.Zero)
                    Marshal.FreeHGlobal(QError);
            }
    }

    #endregion
}

答案 2 :(得分:0)

你原来的P / Invoke声明应该没问题,尽管你根本不需要UnmanagedType.Struct。问题似乎与您的C#结构声明有关。特别是,为什么字段声明的顺序与C ++版本不同?