当参数之一是指针数组时,如何从 C# 调用 C++ DLL

Posted

技术标签:

【中文标题】当参数之一是指针数组时,如何从 C# 调用 C++ DLL【英文标题】:How to call C++ DLL from C# when one of the parameters is an array of pointers 【发布时间】:2017-11-27 23:32:24 【问题描述】:

我有一个 DLL pin C++,它接受两个参数,类似于 C 中的 main() 函数:参数数量,后跟指向各个参数的指针数组:

__declspec(dllexport) void Calculate(int argc, void** argv)

    if (argc >= 7)
    
        auto sourceX = *((int*)(argv[0]));
        auto sourceY = *((int*)(argv[1]));
        auto iterations = *((int*)(argv[2]));
        auto resize = *((bool*)(argv[3]));
        auto input = (double*)(argv[4]);
        auto targetX = *((int*)(argv[5]));
        auto targetY = *((int*)(argv[6]));

        // Do computations
    

我能够以这种方式从另一个 C 代码调用导出的函数(在通过 LoadLibrary 和 GetProcAddress 调用加载 DLL 库之后):

int X, Y, iterations;
bool Resize;
double* Input;

// Initialize variables, allocate data for Input
// ...

void* Params[] =  &X, &Y,  &iterations, &Resize, Input, &X, &Y ;

_Calculate(7, Params);

但是,当我尝试从 C# 调用 DLL 时,它崩溃了。这是我正在使用的 C# sn-p:

[DllImport("computedll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Calculate(int argc, void** argv);

public void Compute()

    int X, Y, iterations;
    bool Resize;
    double* Input;

    // Initialize variables, allocate data for Input
    // ...

    var Parameters = stackalloc void*[7];

    Parameters[0] = &X;
    Parameters[1] = &Y;
    Parameters[2] = &iterations;
    Parameters[3] = &Resize;
    Parameters[4] = Input;
    Parameters[5] = &X; 
    Parameters[6] = &Y;

    Calculate(7, Parameters);

我在这里做错了什么?有没有办法使这种使用指针数组(void**)的模式起作用?提前致谢。

【问题讨论】:

见:***.com/tags/pinvoke/info 猜测您已经验证了您的 C++ 代码并且您的 C# 代码使用相同的指针大小? 【参考方案1】:

构建为 x64,以下工作。这几乎是您的代码。我讨厌在 C/C++ 之外使用 cdecl,所以如果这对你很重要……对不起。重点是...如果您遇到问题,这不是您的实际代码或概念的基础。

Interopy.h:

extern "C"

  __declspec(dllexport) void __stdcall Calculate(int argc, void** argv);

来源:

#include "Interopy.h"
void __stdcall Calculate(int argc, void** argv)

  if (argc >= 7)
  
    auto sourceX = *((int*)(argv[0]));
    auto sourceY = *((int*)(argv[1]));
    auto iterations = *((int*)(argv[2]));
    auto resize = *((bool*)(argv[3]));
    auto input = (double*)(argv[4]);
    auto targetX = *((int*)(argv[5]));
    auto targetY = *((int*)(argv[6]));
    *(int*)argv[5] = 42; //to test

    // Do computations
  

c#(平台目标 x64):

  public static class iop
  

  [DllImport("InteropyDll1.dll",  CallingConvention = CallingConvention.StdCall)]
    unsafe public static extern void Calculate(int argc, void** argv);

    unsafe static public void Compute()
    
        int X, Y, iterations;
        bool Resize;
        double* Input = (double*)IntPtr.Zero;

        // Initialize variables, allocate data for Input
        // ...

        void **Parameters = stackalloc void*[7];

        Parameters[0] = &X;
        Parameters[1] = &Y;
        Parameters[2] = &iterations;
        Parameters[3] = &Resize;
        Parameters[4] = Input;
        Parameters[5] = &X;
        Parameters[6] = &Y;

        Calculate(7, Parameters);
    
  

请注意,此程序集现在依赖于平台。我过去通过多种构建配置解决了这个问题,但我相信还有更好的方法可以解决。无论如何,出于这个原因和其他原因,您都希望将这个不安全的代码放在它自己的程序集中。

【讨论】:

谢谢你!我实施了你的建议,效果很好!我尝试对您的答案进行投票,不幸的是,我的声誉 (6) 还不够。 @StephenDaedalusSepara 不过,您始终可以接受它作为提问者。我相信它旁边有一个复选标记? @StephenDaedalusSepara - 顺便说一句,我不知道这段代码的用途,但如果你要维护它,我会留意 C# 中的 Span<T>Memory<T> 7.2.它们专为此类不需要不安全的场景而设计。不过,目前还不错。 (旧版)c++ 代码用于执行在 c# 中不可用或速度较慢的计算。是的,我会维护这些代码一段时间,直到我们得到更好的计算实现。看起来 Span<T>Memory<T> 将成为 c# 的有趣和有用的开发,再次感谢。

以上是关于当参数之一是指针数组时,如何从 C# 调用 C++ DLL的主要内容,如果未能解决你的问题,请参考以下文章

C# 调用C++ DLL,而c++函数的有一个参数需要是null,该怎么传递?

当引入参数时,从指针调用成员函数的 C++ 调用内存访问冲突

使用指针参数 (WCT) 从 C# 调用 C++ 方法

SWIG:如何将 C++ 对象数组从 C# 传递到 C++?

如何从 C# 调用 C++ 类

从 C++ 传递函数指针以由 C# 调用 - 函数参数包括宽字符字符串 (LPCWSTR)