将pinvoke与结构和指针结合使用

本文关键字:结合 指针 pinvoke 结构 | 更新日期: 2023-09-27 17:57:48

我正在尝试在我的c#代码中pinvoke一个c函数。它接受一个结构和一个double作为输入,并返回一个相同类型的结构。我在c和c代码中定义了相同的结构。当对c函数进行PInvoke操作时,我得到一个异常"Methods type signature is not PInvoke compatible"。有人能发现我做错了什么吗?感谢

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;
}

将pinvoke与结构和指针结合使用

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

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

如果结构采用普通包装,则必须使用IntPtr.Size。通常,一个简单的结构互操作可以解决这一问题,但我们在这里并没有使用简单的结构间操作。如果该结构是字节压缩的,那么您将需要将其显示"IntPtr.Size"的位置改回"4"。

也就是说,假设我们在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();
        }

    }