C# 中用于从 DllImport 函数中检索引用的指针
Posted
技术标签:
【中文标题】C# 中用于从 DllImport 函数中检索引用的指针【英文标题】:Pointers in C# to Retrieve Reference From DllImport Function 【发布时间】:2010-02-26 21:26:21 【问题描述】:我在我的 C# 项目中引用了一个 DLL,如下所示:
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, string cout, string flimit,
string frate, string fwindow, string fincrement, string fbird,
string fparameter, string fvalidation, string fcoupon);
FeeCalculation函数在DLL中导出如下:
extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin,
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);
DLL 函数以 char * 的形式返回对其内部结构的引用,因此如果您要在 C++ 中引用此 DLL,您将执行以下操作来进行计算并获取返回的结构:
FeeCalculation(buff, (char *)&fans, (char *)fl, (char *)ft, (char *)fw, (char *)fi, (char *)fe, (char *)&fm, (char *)val, (char *)cpn);
现在,如何使用 C# 检索那些通过引用返回的值?意思是,我如何在 C# 中做同样的事情来获取返回的结构来获取我返回的计算?我知道我需要创建一个不安全的方法,但我不清楚如何像在 C++ 中那样处理 C# 中的内存地址。
编辑:以下状态使用 IntPtr 但如何放入相同的结构中以便可以引用结构的字段?
编辑:这是我感兴趣的返回结构(cout):
struct feeAnswer
unsigned int fee;
unsigned int tax1;
unsigned int tax2;
unsigned int tax3;
unsigned int tax4;
unsigned int surcharge1;
unsigned int surcharge2;
unsigned int validationFee;
unsigned int couponFee1;
unsigned int couponFee2;
unsigned int couponFee3;
unsigned int couponFee4;
unsigned short int dstay; //Day Stay
unsigned short int mstay; //Minute Stay
;
这是我将与其他结构一起传递的 (cin)(它们目前是零字节,我想先让它工作,然后我将实现其余的):
struct feeRequest
unsigned char day;
unsigned char month;
unsigned int year; //2000 ~ 2099
unsigned char hour;
unsigned char minute;
unsigned char rate;
unsigned char validation;
unsigned char coupon1;
unsigned char coupon2;
unsigned char coupon3;
unsigned char coupon4;
;
【问题讨论】:
你应该向我们展示你的结构,(至少其中一些) 结构要好得多,我已经修改了对 feeAnswer 结构的答案,您应该可以从那里进行概括。 【参考方案1】:在这种情况下,char* 参数不是字符串,而是指向表示数据的原始字节块的指针。您应该将参数编组为 IntPtr 类型的实例,并与 Marshal.AllocHGlobal 一起创建一块内存,然后 Marshal.PtrToStructure 将该块内存转换为可用的 .NET 类型。
举个例子:
[StructLayout(LayoutKind.Sequential)]
struct MyUnmanagedType
public int Foo;
public char C;
IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyUnmanagedType)));
try
FeeCalculation(memory);
MyUnmanagedType result = (MyUnmanagedType)Marshal.PtrToStructure(
memory, typeof(MyUnmanagedType));
finally
Marshal.FreeHGlobal(memory);
【讨论】:
您能否将该函数称为 FeeCalculation(memory),即使它需要更多变量? 这只是为了示例而进行的简化。您需要在 C# 代码中重新声明每个非托管结构,然后单独编组每个结构。 啊,好吧,所以如果我想要 cout 和 fm 结果,我只需将它传递给 PtrToStructure 就可以了……基本上是两个语句? John 的方法现在更容易了,因为我们有明确的结构可以使用。我的方法在一般情况下更健壮,但对于简单的结构,John 的方法更简洁。【参考方案2】:编辑:现在我们有了可以使用的结构,可以使用更好的解决方案。只需在 C# 中声明与您的 C++ 结构匹配的结构,并在 extern 声明中使用它们
[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer
public uint fee;
public uint tax1;
public uint tax2;
public uint tax3;
public uint tax4;
public uint surcharge1;
public uint surcharge2;
public uint validationFee;
public uint couponFee1;
public uint couponFee2;
public uint couponFee3;
public uint couponFee4;
public ushort dstay; //Day Stay
public ushort mstay; //Minute Stay
;
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct feeRequest
public byte day;
public byte month;
public uint year; //2000 ~ 2099
public byte hour;
public byte minute;
public byte rate;
public byte validation;
public byte coupon1;
public byte coupon2;
public byte coupon3;
public byte coupon4;
;
[DllImport ("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation (
feeRequest cin,
out feeAnswer cout,
...
....
下面的原始答案(在我们有结构之前)
在我看来,这些不是对内部字符串的引用,而是指向将由调用填充的字符串缓冲区的指针。如果您返回字符串指针,那么这些指针将被声明为char**
而不是char*
。
所以我认为这些只是标准输出参数。他们有很多。所以你的 C# 互操作看起来像这样
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string cout,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string flimit,
或者如果你的“字符串”不是真正的字符串
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] cout,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] flimit,
....
【讨论】:
他在问题陈述中说他们不是真正的字符串。 @MikeP:他还说它们是参考,但显然不是。 所以请原谅我的无知,但是如果它们以字节数组的形式出现,那么我可以将其转换为我定义的结构,然后我还必须将其作为字节数组传递回来以获得我想要的响应? 我的印象是他指的是传递引用的概念,而不是传递值。尤其不是 C++ 或 C# 引用。这意味着参数用于引用数据,该术语没有特定于 API 的含义。 @MikeP:你明白了..这正是我所说的,因此输出参数【参考方案3】:要回答您的编辑,您需要创建一个结构,然后在字段上使用 StructLayoutAttribute 以使字节顺序和填充与原始 dll 相同。
【讨论】:
以上是关于C# 中用于从 DllImport 函数中检索引用的指针的主要内容,如果未能解决你的问题,请参考以下文章
调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()