我在C ++中有一个导出到DLL的函数。我包含一个struct指针作为参数之一。我需要在C#中使用这个函数,所以我使用DLLImport作为函数,并使用StructLayout在C#中重新创建了struct。我尝试使用ref传递参数,并尝试使用MarshalAs(UnmangedType.Struct)和Marshal.PtrToStructure进行Marshaling。参数仍未正确传递。
示例:
[DllImport("testdll.dll")]
public static extern int getProduct(int num1, int num2, [MarshalAs(UnmanagedType.Struct)] ref test_packet tester);
另外一小段信息,结构包含一个字节* var,我认为这可能导致将param作为ref传递的问题。有任何想法吗?我是在正确的轨道上吗?谢谢你的帮助。
感谢nobugz的回复。这是struct 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;
答案 0 :(得分:4)
使用“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 :(得分:1)
这是我个人的一个例子。它可能非常复杂。请注意,将数组作为指针移动并不容易,因此您应该看看如何在c#端执行此操作。这应该会涉及很多主要的数据类型。您必须确保您的元素完全排列。否则它会看起来像它的工作,但你会得到糟糕的ptrs(充其量)。移动这个结构时我遇到了很多麻烦,这是唯一有效的方法。祝你好运
功能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#side -
[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
}
答案 2 :(得分:0)
你原来的P / Invoke声明应该没问题,尽管你根本不需要UnmanagedType.Struct
。问题似乎与您的C#结构声明有关。特别是,为什么字段声明的顺序与C ++版本不同?