Передача указателя структуры в качестве параметра в C # - PullRequest
3 голосов
/ 19 февраля 2010

У меня есть функция в C ++, которую я экспортировал в DLL. Я содержит указатель структуры в качестве одного из параметров. Мне нужно использовать эту функцию в C #, поэтому я использовал DLLImport для функции и воссоздал структуру в C #, используя StructLayout. Я попытался передать параметр с помощью ref, а также попытался выполнить маршалинг при использовании MarshalAs (UnmangedType.Struct) и Marshal.PtrToStructure. Параметр все еще не проходит правильно.

Пример:

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

Еще один кусочек информации, структура содержит байт * var, который, я думаю, может быть причиной проблемы с передачей параметра как ref. Есть идеи? Я на правильном пути? Спасибо за помощь.

Спасибо nobugz за ответ. Вот пример структуры 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 ]

4 голосов
/ 19 февраля 2010

Использование «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 голос
/ 19 февраля 2010

Вот пример из моего личного материала. Это может быть действительно сложно. Обратите внимание, что не так просто перемещать массивы как указатели, поэтому вы должны посмотреть, как это делается на стороне c #. Это должно затронуть многие основные типы данных. Вы должны убедиться, что ваши элементы выстроились точно. В противном случае он будет выглядеть как работающий, но вы получите плохие результаты (в лучшем случае). У меня было много проблем при перемещении этой структуры, и это был единственный подход, который работал. Удачи

Функция 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 # сторона -

[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
}
0 голосов
/ 19 февраля 2010

Ваше оригинальное объявление P / Invoke должно быть в порядке, хотя вам вообще не нужно UnmanagedType.StructКажется, проблема в объявлении структуры C #.В частности, почему порядок объявлений полей отличается от версии C ++?

...