使用pinvoke与结构和指针

时间:2014-04-10 13:23:03

标签: c# c pinvoke

我试图在我的c#代码中调用c函数。它将struct和double作为输入,并返回相同类型的结构。我在c和c#代码中定义了相同的结构。当pinvoking c函数时,我得到一个异常"方法类型签名不是PInvoke兼容"。有人能发现我做错了什么吗?感谢

C:

typedef struct myStruct_struct
{
     double prefix[8];
     int length;
     double array[1];
}
myStruct;

extern "C" __declspec( dllexport ) myStruct *doSomething(const myStruct *inStruct, double val)
{
   myStruct *outStruct;
   //doSomething ...
   return outStruct;
}    

C#:

[StructLayout(LayoutKind.Sequential)]
public struct myStruct
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public double[] Prefix;
    public int Length;
    public IntPtr ArrayPtr;

    public void MarshalArray(double[] array)
    {
        Length = array.Length;
        int pointerSize = IntPtr.Size + (8 * Length);
        ArrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(pointerSize));
        Marshal.Copy(array, 0, ArrayPtr, Length);
    }

    public double[] UnMarshalArray()
    {
        double[] array = new double[Length];
        Marshal.Copy(ArrayPtr, array, 0, Length);
        return array;
    }
}

[DllImport("testing.dll", EntryPoint = "doSomething", SetLastError = true, CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr test_doSomething(IntPtr inStruct, double val); 

private void button1_Click(object sender, EventArgs e)
{
    try
    {
        myStruct s = createStruct();
        myStruct result = MarshalIn(test_doSomething(MarshalOut(s), 2));
    }
    catch (Exception exc)
    {
        Console.WriteLine(exc.Message);
    }
}

private myStruct MarshalIn(IntPtr intPtr)
{
    myStruct s = (myStruct)Marshal.PtrToStructure(intPtr, typeof(myStruct));

    s.UnMarshalArray();

    return s;
}

private IntPtr MarshalOut(myStruct s)
{
    double[] array = new double[] { 1, 1, 2, 2, 1, 1, 2, 2 };
    s.MarshalArray(array);

    IntPtr outPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(myStruct)));

    Marshal.StructureToPtr(s, outPtr, true);

    return outPtr;
}

1 个答案:

答案 0 :(得分:0)

我知道这可能不是世界上最好的解决方案,但它在我的测试中对我有用,所以我会让你推断出你可能需要做的关于释放内存的事情,并留下你根据你自己的判断。

我个人讨厌使用Marshal.Copy和Buffer.BlockCopy。我为此编写了我自己的函数,这里:http://nmcsmem.codeplex.com/(特别是DTCSMemory项目 - > MemPtr结构),但我使用标准函数对其进行了编程,以便于移植。我不得不承认,我可能会用那里的字节做一些时髦的事情,但我只是做了双倍(没有双关语意)确定。

如果结构采用正常包装,则必须使用IntPtr.Size。通常,一个简单的结构互操作会解决这个问题,但我们在这里并没有使用简单的结构互操作。如果结构是字节打包的,那么您将要更改它所说的位置' IntPtr.Size'回到刚才的那个'。

那就是说,只要我们在C中有这个:

typedef struct myStruct_struct
{
    int length;
    double array[1];
}
myStruct;

__declspec(dllexport) myStruct *doSomething(const myStruct *inStruct, double val)
{
    int i = sizeof(double);

    //doSomething ...
    myStruct *outStruct = (myStruct*)GlobalAlloc(0, sizeof(void*) + (8 * 256));
    ZeroMemory(outStruct, sizeof(void*) + (8 * 256));
    outStruct->length = 256;
    outStruct->array[0] = inStruct->array[0] + val;

    return outStruct;
}

然后C#中的这段代码就可以了:

  public class Program
    {

        /// <summary>
        /// myStruct is not marshaled, directly.
        /// </summary>
        public struct myStruct
        {

            public int Length;
            public double[] Array;


            private IntPtr _ptr;

            /// <summary>
            /// Custom marshal a structure in from interop (and optionally free the original pointer).
            /// </summary>
            /// <param name="ptr"></param>
            /// <param name="freeOrig"></param>
            /// <returns></returns>
            public static myStruct MarshalIn(IntPtr ptr, bool freeOrig = true)
            {
                byte[] by = new byte[4];
                myStruct ns = new myStruct();

                Marshal.Copy(ptr, by, 0, 4);
                ns.Length = BitConverter.ToInt32(by, 0);

                ns.Array = new double[ns.Length];
                by = new byte[ns.Length * 8];

                Marshal.Copy(ptr + IntPtr.Size, by, 0, by.Length);
                Buffer.BlockCopy(by, 0, ns.Array, 0, by.Length);
                if (freeOrig) Marshal.FreeHGlobal(ptr);

                return ns;
            }

            /// <summary>
            /// Custom marshal a structure for calling interop.
            /// </summary>
            /// <returns></returns>
            public IntPtr MarshalOut()
            {
                IntPtr ptr;
                int l = IntPtr.Size + (8 * Array.Length);
                ptr = Marshal.AllocHGlobal(l);

                byte[] by = BitConverter.GetBytes(Length);

                Marshal.Copy(by, 0, ptr, 4);

                by = new byte[Length * 8];
                Buffer.BlockCopy(Array, 0, by, 0, by.Length);
                Marshal.Copy(by, 0, ptr + IntPtr.Size, by.Length);

                _ptr = ptr;
                return ptr;
            }

            /// <summary>
            /// Free any associated pointer with this structure created with MarshalOut().
            /// </summary>
            public void Free()
            {
                if (_ptr != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(_ptr);
                    _ptr = IntPtr.Zero;
                }
            }
        }

        [DllImport("mylib.dll", SetLastError = true, CharSet = CharSet.None, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr doSomething(IntPtr inStruct, double val);


        static void Main()
        {

            // let's do some math.

            myStruct ms = new myStruct(), ms2;
            ms.Array = new double[1];

            ms.Length = 1;
            ms.Array[0] = 424.444;

            ms2 = myStruct.MarshalIn(doSomething(ms.MarshalOut(), 524.444));

            // Free this after the call.
            ms.Free();

        }


    }