本文介绍了在C#和C ++之间使用双精度数组数据进行P/Invoke的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已通过P/Invoke在C ++ Interop上阅读了各种MSDN页面,此处,并且此处,但我仍然感到困惑.

I've read the various MSDN pages on C++ Interop with P/Invoke here and here but I am still confused.

我需要进入本机代码的一些大双打数组,以及一些需要返回的结果数组.我不知道输出数组的大小.为简单起见,在示例中,我将只使用一个数组.该平台是x64;我读到,在32位和64位环境中,编组内部结构有很大不同,因此这可能很重要.

I have some large arrays of doubles that I need to get into native code, and some resulting arrays that need to get back. I do not know the sizes of the output arrays in advance. For simplicity, I will use only a single array in the example. The platform is x64; I read that marshalling internals are quite different between 32- and 64-bit environments so this might be important.

C#

    [DllImport("NativeLib.dll")]
    public static extern void ComputeSomething(double[] inputs, int inlen,
        [Out] out IntPtr outputs, [Out] out int outlen);

    [DllImport("NativeLib.dll")]
    public static extern void FreeArray(IntPtr outputs);

    public void Compute(double[] inputs, out double[] outputs)
    {
        IntPtr output_ptr;
        int outlen;
        ComputeSomething(inputs, inputs.Length, out output_ptr, out outlen);

        outputs = new double[outlen];
        Marshal.Copy(output_ptr, outputs, 0, outlen);

        FreeArray(output_ptr);
    }

C ++

extern "C"
{
    void ComputeSomething(double* inputs, int input_length,
        double** outputs, int* output_length)
    {
    //...
    *output_length = ...;
    *outputs = new double[output_length];
    //...
    }

    void FreeArray(double* outputs)
    {
        delete[] outputs;
    }
}

它起作用了,也就是说,我可以读出我在C ++端写到数组中的双打.但是,我想知道:

It works, that is, I can read out the doubles I wrote into the array on the C++ side. However, I wonder:

  • 这是使用P/Invoke的正确方法吗?
  • 我的签名不是不必要地复杂吗?
  • 可以更有效地使用P/Invoke解决此问题吗?
  • 我相信我读过,可以避免对内置类型的一维数组进行编组.有没有办法处理Marshal.Copy?

请注意,我们有一个工作正常的C ++/Cli版本,但是在第三方库代码中存在与本地静态相关的一些问题,这些问题会导致崩溃. Microsoft标记了此问题作为WONTFIX ,这就是为什么我要寻找替代品的原因.

Note that we have a working C++/Cli version, but there are some problems related to local statics in third-party library code that lead to crashes. Microsoft marked this issue as WONTFIX, which is why I am looking for alternatives.

推荐答案

如果将确定输出长度的代码与填充输出的代码分开是可行的,则可以:

If it were practical to separate the code that determines the output length from the code that populates the output then you could:

  • 导出一个返回输出长度的函数.
  • 从C#代码中调用它,然后分配输出缓冲区.
  • 再次调用非托管代码,这一次要求它填充输出缓冲区.

但是我假设您已经拒绝了此选项,因为这是不切实际的.在这种情况下,您的代码是解决问题的完全合理的方法.实际上,我会说您做得很好.

But I'm assuming that you have rejected this option because it is impractical. In which case your code is a perfectly reasonable way to solve your problem. In fact I would say that you've done a very good job.

一旦解决了调用约定不匹配的问题,代码将在x86中工作相同.在C ++方面,调用约定为cdecl,但在C#方面为stdcall.在x64上这无关紧要,因为只有一种调用约定.但这在x86下会是个问题.

The code will work just the same in x86 once you fix the calling convention mismatch. On the C++ side the calling convention is cdecl, but on the C# side it is stdcall. That doesn't matter on x64 since there is only one calling convention. But it would be a problem under x86.

一些评论:

  • 您不需要同时使用[Out]out.后者意味着前者.
  • 您可以通过分配共享堆来避免导出解除分配器.例如,在C ++端使用CoTaskMemAlloc,然后在C#端使用Mashal.FreeCoTaskMem取消分配.
  • You don't need to use [Out] as well as out. The latter implies the former.
  • You can avoid exporting the deallocator by allocating off a shared heap. For instance CoTaskMemAlloc on the C++ side, and then deallocate with Mashal.FreeCoTaskMem on the C# side.

这篇关于在C#和C ++之间使用双精度数组数据进行P/Invoke的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 11:08