将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;
}
我知道这可能不是世界上最好的解决方案,但它在我的测试中对我有效,所以我会让你推断出你个人可能需要做的关于内存释放的事情,并让你自己判断。
我个人讨厌使用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();
}
}