【问题标题】:Passing Array of Bool to C++ Code from C#从 C# 将 Bool 数组传递给 C++ 代码
【发布时间】:2015-07-14 15:02:49
【问题描述】:

我目前正在与其他几个人一起开展一个项目,遗憾的是,他们不再提供参考资料。该项目使用 C# 作为 GUI,使用 C++ 更新我们的古老数据库。目前,我在 C# 中有这个功能:

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    //Synch bool Synch(void * voidPtr, const TCHAR * databaseDir, const TCHAR * truckDir)
    static extern Int32 Synch(IntPtr pSync, string databaseDir, string destDir, ref bool[] wholeFiles);

wholeFiles 是一个布尔数组,用于根据用户输入确定是否应同步某些文件。

C++ 应该接受这个数组,并且能够在满足某些条件时将一些布尔值翻转为真或假。

if (numSyncRecords > 0){
        Log::WriteVerbose(_T("   %d recs, %d differences found"), (int) numRecsUsed, (int) numSynRecords);
        if(wholeFiles[sourceFileID]){
            CString tempPath = _T("");
            tempPath += destDir;
            tempPath += fileName;
            DeleteFile(tempPath);
        }
        else
            wholeFiles[sourceFileID] = false;
    }
    else
        wholeFiles[sourceFileID] = false;

在上面的例子中,我传入Trues,如果文件没有变化,设置为false,避免处理不需要的文件。

现在,我的问题是,当 C++ 代码完成后,仍然有 True 标记应该设置为 false。如果我通过 ref 发送它,那么我会得到堆栈溢出。没有 ref,数组似乎没有变化。

编辑:这是请求的。

SYNC_API Int32 Synch(void * voidPtr, const TCHAR * databaseDir, const TCHAR * truckDir, bool wholeFiles[])

Edit2:在 C++ 代码中,我有一个日志语句,它遍历数组并在整个文件数组中的每个位置每行输出一次真/假,我最终得到了适当的日志输出,但是当我查看完成后在C#代码上的Array处,不变。

【问题讨论】:

  • 你不想使用ref - wholeFiles 是一个数组,它已经通过引用传递了。您可以将ref 用于TCHAR **,即返回一个不同的 数组。您可能想在参数上使用[In, Out] 属性,将其视为输入/输出,但我认为这不会有帮助。但是,您对 C 声明的评论不符合 P/Invoke - 签名是否更改,还是错误?您正在传递 destDir 字符串,而不是现在看起来的 wholeFiles 数组。如果签名发生变化,请贴出头文件的相关部分。
  • 我忘记更新评论了,很抱歉。

标签: c# c++ arrays


【解决方案1】:

如果DllImport 下方的注释是 C++ 端的确切函数声明,那么您的 wholeFiles 数组将永远不会出现在 C++ 端,因为它不需要第 5 个参数。请务必先解决该问题。

在 C# 中,只有一个大小为 bool,即 1 个字节。在 C++ 中有两种类型,C 风格的 1 字节 bool 和 Win32 4 字节 BOOLBy default P/Invoke will marshal bools as BOOL.

因此,如果第一个问题不是您的全部问题并且它不起作用,那么您正在使用 C 风格的 bool 并且需要像这样使用 [MarshalAs] 告诉 C#:

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
static extern Int32 Synch(
    IntPtr pSync,
    string databaseDir,
    string destDir,
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.I1)]
    bool[] wholeFiles);

您也不需要ref,因为数组本身就是一个引用类型。 ref 数组仅在您调用的函数想要将数组与另一个交换数组时才有用。

另外请注意,您不必将其设为多行,如果函数都在一行中,则该属性将起作用,我只是为了可读性而这样做。

【讨论】:

  • 我查看了您提出的解决方案,并在编辑中更改了我的问题。
【解决方案2】:

如果没有完整的 C++ 函数,很难准确判断发生了什么,但您可以尝试的一件事是让 C++ 函数返回修改后的数组(而不是依赖就地修改)。

[DllImport("Synch.DLL", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
// Now return an IntPtr
static extern IntPtr Synch(IntPtr pSync, string databaseDir, string destDir, ref bool[] wholeFiles);

wholeFiles = Synch(..., wholeFiles);

选项 2

另一种选择是在 C# 中声明一个缓冲区,将整个文件数组复制到该缓冲区,然后将缓冲区的地址传递给 C#(这是我过去所做的)。然后 C++ 代码会修改缓冲区,然后您可以在对 dll 的调用完成后将其复制回 wholeFiles。

选项 3

最好的选择(在我看来)是使用SWIG。 SWIG 使得包装 C++ 函数以供 C# 使用(实际上是许多其他语言)变得非常容易。这将为您提供一个名为 Synch 的 C# 函数,该函数在后台调用 dll,为您处理任何内存问题。

示例(简单)synch.i 文件假设 C++ 标头被称为 synch.h:

%module SynchSWIG
%{
#include "synch.h"
%}

%include "synch.h

使用命令生成包装器(未测试):

swig -c++ -csharp -dllimport Synch.DLL synch.i

这将为您提供 C# 文件以编译到您的 C# 项目中,以及一个 C++ 文件以编译到 C++ dll 项目中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 2012-02-10
    • 1970-01-01
    相关资源
    最近更新 更多