我试图在我的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;
}
答案 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();
}
}