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#中DllImport是啥意思啊

调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()

DLL引用摘录

c# dllimport怎么弱引用

从 C# 中 WM_MOUSEHWHEEL 消息中的 wParam 检索 WHEEL_DELTA

DllImport 与 VB.NET 中的声明