如何从C ++ DLL中传递C#函数中未知大小的数组?

时间:2014-03-31 04:16:34

标签: c# c++ dll pinvoke

我有一个C ++ dll文件,其中包含一个名为fn(double* p, int* pLength)的导出函数,其中,p是一个指针(是C#中使用的out数组),pLength是在这个函数是p的长度(大小)。代码在这里:

void _stdcall fn(double* p, int* pLength)
{
    int i=0;
    double p_=0;
    do
    {
        p[i]=p_;
        p_=an expression!!!!!;
        i++;
    }
    while (condition with p_);  //Condition of loop

    *pLength=i;

    return;
}

我成功编译成dll文件。此文件名为“testFile.dll”并将其移动到System32文件夹。 现在,我启动C#控制台项目并从“testFile.dll”声明导出的函数fn(),此代码为:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("testFile.dll")]
        public static extern void fn(double[] p, ref int pLength);
        static void Main(string[] args)
        {
            // I want to declare an array with unknown length (size),
            // because the length is calculated from fn() function!!!
            // And pass to fn() function to get p[i].....
            double[] p;
            int pLength;
            fn(p, ref pLength);

            // Display pLength and p[i]
            Console.WriteLine(pLength);
            for (int i = 0; i < pLength; i++)
                Console.WriteLine(p[i]);
            Console.ReadKey();
        }
    }
}

我跑步并得到两个错误:

  

错误1使用未分配的局部变量'p'

     

错误2使用未分配的局部变量'pLength'

如何修复它们?我想从“testFile.dll”中的pLength函数中获取p[i]fn() 完全。提前谢谢。

2 个答案:

答案 0 :(得分:2)

据我了解,您需要两次调用该函数。一旦你传递一个空数组并要求函数计算所需的长度。然后再次使用已分配的数组调用该函数将填充。

非托管代码可能如下所示:

void __stdcall fn(double p[], int* pLength)
{
    int i = 0;
    double p_ = 0;
    do
    {
        if (p) p[i] = p_;
        p_ = ...;
        i++;
    }
    while (...);
    *pLength = i;
}

请注意,代码会检查是否已分配p,并且只会写入数组。

在托管方面,代码如下所示:

[DllImport(...)]
public static extern void fn(double[] p, out int pLength);
....
int pLength;
fn(null, out pLength);
double[] p = new double[pLength];
fn(p, out pLength);

更安全的版本是将数组的长度传递给函数。这将允许该函数确保它不会写出结束。

void __stdcall fn(double p[], int* pLength)
{
    int i = 0;
    double p_ = 0;
    do
    {
        if (p)
        {
            if (i >= *pLength) return -1; // or some other error code
            p[i] = p_;
        }
        p_ = ...;
        i++;
    }
    while (...);
    *pLength = i;
}

管理方:

[DllImport(...)]
public static extern void fn(double[] p, ref int pLength);
....
int pLength = 0;
fn(null, ref pLength);
double[] p = new double[pLength];
fn(p, ref pLength);

如果您只想调用该功能一次,则需要执行以下操作:

  1. 在非托管代码中动态分配内存。使用GlobalAlloc以便托管代码可以取消分配它。
  2. 如果您的初始分配证明长度不足,请准备在非托管代码中重新分配。
  3. 在托管代码中使用out类型的IntPtr参数来获取指向非托管内存的指针。
  4. 使用Marshal.Copy将数组内容复制到托管double[]对象中。
  5. 调用Marshal.FreeHGlobal以释放非托管内存。
  6. 这种技术要复杂得多,涉及更多锅炉板。只有在我概述的第一种方法中存在显着的性能开销时才采用这种方法。


    一边说。请不要将您的文件放在系统目录中。它属于系统,您不应该修改它。将DLL放在与主机可执行文件相同的目录中。

答案 1 :(得分:1)

您获得的错误与您没有为数组分配任何值的事实有关。虽然该函数并不关心数组的大小,但它仍然需要处理有效的内存。

尝试以下方法:

int pLength = 20;
double[] p = new double[pLength];
fn(p, ref pLength);

看看错误是否消失。