【问题标题】:Passing C# array by ref into C++ DLLImport method trims values通过 ref 将 C# 数组传递给 C++ DLLImport 方法修剪值
【发布时间】:2014-07-03 23:05:30
【问题描述】:

我正在为一些 C++ 代码制作 DLL 包装器,但遇到了问题。提供一些示例代码后,我会更容易说明我的问题。

在我的 C++ 应用程序的 .h 文件中:

#pragma once
namespace Wrapper {
extern __declspec(dllexport)
    void SANDBOX(int arrayLength, int* intArray);
}

在我的 C++ 应用程序的 .cpp 文件中:

#include "stdafx.h"
#include "Wrapper.h"
#include <windows.h>
using namespace Wrapper;

extern "C"{

    BOOL APIENTRY DllMain( 
                            HANDLE hModule,
                            DWORD  ul_reason_for_call,
                            LPVOID lpReserved )
    {
        return TRUE;
    } 
    __declspec(dllexport) void SANDBOX(int* arrayLength, int* intArray[])
    {
        if(*intArray == NULL)
        {
            *arrayLength = 5;
            return;
        }
        else
        {
            int i = 0;
            for(i = 0; i < (*arrayLength); i++)
            {
                (*intArray)[i] = 1 + i*i;
            }
        }
    }
}

我的 C# 应用程序像这样导入方法:

    const string dllFileLocation = "Wrapper.dll";

    [DllImport(dllFileLocation, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public extern static void SANDBOX(ref int arrayLength, ref int[] intArray);

最后,我的 C# 应用程序以如下形式调用代码:

        //Initialize some things
        string outString;
        int numInArray = -1;
        int[] iArray = null;
        //Fill the numInArray integer
        Wrapper_Handle.SANDBOX(ref numInArray, ref iArray);

        //Lets initialize the array and print the values
        iArray = new int[numInArray];
        outString = "";
        foreach(int i in iArray)
        {
            outString += i + "\n";
        }
        MessageBox.Show(outString);

        //Now lets put values into the array and print the new ones out
        Wrapper_Handle.SANDBOX(ref numInArray, ref iArray);
        outString = "";
        foreach(int i in iArray)
        {
            outString += i + "\n";
        }
        MessageBox.Show(outString);

所以,我期望发生的是我创建一个空数组来获取要初始化的值的数量。所以我拿出 5 个,制作一个新数组,然后打印出来。它得到了我的期望:一个 0 的消息框五次。但是,再次调用该方法后,第二个消息框只是数字 1,没有其他值。看起来其他 4 个值已从数组中“删除”,C# 应用程序再也看不到它们了。

我已经成功地从 C++ .DLL 导入了许多其他函数。它的这个数组表示法导致了问题。在调试 .DLL 时,这些值被正确地传递给 C++ 代码,并被赋值。

有谁知道为什么 C# 应用程序会“丢失”其他 4 个值?数组的长度无关紧要,只有第一个值会在调用方法后保留。

提前致谢。非常感谢任何帮助。


编辑—— 我的问题的解决方案在对此问题的评论中提供。我在下面发布我的更改:

C++ .h 文件:

#pragma once
namespace Wrapper {
extern __declspec(dllexport)
    void SANDBOX(int arrayLength, int* intArray);
}

C++ .cpp 文件:

#include "stdafx.h"
#include "Wrapper.h"
#include <windows.h>
using namespace Wrapper;

extern "C"{

    BOOL APIENTRY DllMain( 
                            HANDLE hModule,
                            DWORD  ul_reason_for_call,
                            LPVOID lpReserved )
    {
        return TRUE;
    } 
    __declspec(dllexport) void SANDBOX(int* arrayLength, int* intArray)
    {
        if(intArray == NULL)
        {
            *arrayLength = 5;
            return;
        }
        else
        {
            int i = 0;
            for(i = 0; i < (*arrayLength); i++)
            {
                (intArray)[i] = 1 + i*i;
            }
        }
    }
}

C# 导入:

    const string dllFileLocation = "Wrapper.dll";

    [DllImport(dllFileLocation, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public extern static void SANDBOX(ref int arrayLength, int[] intArray);

C# 调用:

        string outString;
        int numInArray = -1;
        int[] iArray = null;

        Wrapper_Handle.SANDBOX(ref numInArray, iArray);
        iArray = new int[numInArray];       

        outString = "";
        foreach(int i in iArray)
        {
            outString += i + "\n";
        }


        MessageBox.Show(outString);
        Wrapper_Handle.SANDBOX(ref numInArray, iArray);
        outString = "";
        foreach(int i in iArray)
        {
            outString += i + "\n";
        }
        MessageBox.Show(outString);

【问题讨论】:

  • pinvoke marshaller 不知道数组可能有多大。您的 C 代码实际上并没有创建数组(好),因此您的声明不合适。从 C 声明中删除 []。从 C# 声明中删除 ref
  • 您的更改创造了我想要的效果。我仍然试图围绕 PInvoke marshaller 的细微差别。非常感谢!

标签: c# c++ arrays dll dllimport


【解决方案1】:

在 P/Invoke 中通过 ref 传递数组有点奇怪,因为它可能允许被调用的函数替换本机端的引用。我不完全确定您发布的代码为什么不起作用,因为它似乎是合理的(除了前面提到的 ref 的奇怪之处以及您的标头中的声明与参数类型不匹配)。

但是,我认为您可以通过不传递 ref 并仅获取指向数组的指针来轻松更改代码以解决问题。您不是要重新分配数组引用本身;你只关心改变它的内容。

【讨论】:

  • 我试过这个(使用指针),它确实解决了我的问题。但是,它会将事情置于我试图避免的不安全环境中。不过,谢谢!
  • @user2340633:请注意,MikeP 建议的更改与 Hans 评论的完全相同。 "不通过 ref = 从 C# 声明中删除 ref" "只取一个指针 = 从 C 声明中删除 []"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2012-07-29
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
相关资源
最近更新 更多