从 c# 代码调用带有 void *parameter 的本机 c++ 函数

Posted

技术标签:

【中文标题】从 c# 代码调用带有 void *parameter 的本机 c++ 函数【英文标题】:Calling native c++ function with void *parameter from c# code 【发布时间】:2015-07-28 13:03:35 【问题描述】:

我有一个必须从我的 C# 代码调用的本机 C++ 函数,C++ 函数如下所示:

__declspec(dllexport) int getCameraFrame(void *buffer);

在 C++ 中调用函数如下所示:

unsigned char* data = new unsigned char[640 * 480 * 4];
getCameraFrame(data);

在 C# 中,我使用 PInvoke 将函数导入为:

[DllImport("CameraSDK.dll")]
public static extern int getCameraFrame(???);

我在这里使用什么作为参数,如何从我的 C# 代码中调用该函数?

byte[] buffer;
getCameraFrame(out buffer); //???

更新

我尝试使用 string、byte[]、char[]、IntPtr 作为参数,但我不断收到 PInvokeStackImbalance 异常。

对 PInvoke 函数 'Test!Test.Form1::getCameraFrame' 的调用具有 堆栈不平衡。这可能是因为托管的 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数匹配 目标非托管签名。

更新 - 已解决

根据大卫和迈克尔给出的答案,我已经设法用下面的代码解决了我的问题

[DllImport("CameraSDK.dll", CallingConvention.Cdecl)]
public static extern int getCameraFrame([Out] StringBuilder buffer);

StringBuilder data = new StringBuilder(1024 * 768 * 4)
int result = getCameraFrame(data);

大卫,你的回答确实是正确的,请注意我的 StringBuilder 初始化是 1024 * 768 * 4(而不是 640 * 480 * 4 - 这是抛出 AccessViolationException 的原因) - 在 C++ 代码中,缓冲区是从 ( 1024 * 768 * 4) 到 (640 * 480 * 4) - 我一定错过了那部分,所以,这是我的错。

我能够获取原始数据,但无法将其转换为位图(原始数据采用某种奇怪的格式),但这是另一个问题。

谢谢

【问题讨论】:

看看 [这里][1] 希望对您有所帮助。 [1]:***.com/a/15528053/2928544 @raidensan - 非常有趣的感谢,但仍然没有运气,我不断收到 PInvokeStackImbalance 异常 我撤消了您的编辑,因为它们更改了问题并使答案看起来很愚蠢。 【参考方案1】:

我有以下工作代码:

C++ 定义:

__declspec(dllexport) void SendRenderedImage(byte buffer[]);

C# 声明

[DllImport("External.dll", EntryPoint = "SendRenderedImage", CallingConvention = CallingConvention.Cdecl)]
public static extern void SendRenderedImage(
    [MarshalAs(UnmanagedType.LPArray)] byte[] buffer
);

C#用法:

SendRenderedImage(new byte[10]);

希望对你有帮助。

【讨论】:

对,更近一步,我可以调用本机函数,但是,当我第二次调用它时,我得到一个 AccessViolationException - 试图读取或写入受保护的内存 - 有什么想法吗? 你能显示调用函数的代码吗?在你到达 C++ 代码之前你得到异常了吗? 我已经包含了调用该函数的 C++ 和 C# 位,异常被抛出(使用上面的修改代码)getCameraFrame(data) 行。 调用函数时,不需要out关键字。在您的示例中,您使用未初始化的byte[] buffer 调用该函数,您必须首先执行new byte[n],并且可能还要填写数据。 @Michael No. 数据流出。无需初始化。【参考方案2】:

我会这样声明:

[DllImport(dllname, CallingConvention = ???)]
public static extern int getCameraFrame(
    [Out] byte[] buffer
);

这样调用函数:

byte[] data = new byte[640 * 480 * 4];
int retval = getCameraFrame(data);

请注意,您需要弄清楚调用约定是什么。从表面上看,它看起来像 cdecl。

我使用[Out] 属性表示数据从被调用者流向调用者。但是,由于编组器将简单地固定阵列,因此没有明显的影响。它至少确实记录了未来读者的意图。

更新

您在 cmets 中说您在使用此代码时遇到访问冲突 根据您提供的信息,此处的代码是准确的。因此,问题一定出在我们看不到的东西上。可能库还没有初始化。也许函数是实例方法。或者也许还有其他无法辨别的问题。

您需要超越您在问题中显示的内容。如果我在你的位置,我会首先制作一个使用该库的完整 C++ 程序,以证明该库有效并且你知道要进行什么调用顺序。然后我会把它翻译成 C#。

【讨论】:

如何确定调用约定是什么?该函数的返回类型是 int,我将编辑我的问题...将 [Out] 放在 byte[] 参数之前让我回到第一方,即获得 PInvokeStackImbalance 异常。 您是否粘贴了确切的声明?问题历史让我怀疑我们是否见过实际的 C++ 代码。 我已经包含了与我的问题相关的所有代码,我不知道函数体是什么样的,因为它位于 SDK dll 文件中(被混淆了) 调用约定确实是Cdecl 鉴于问题中的信息,我的回答是准确的。

以上是关于从 c# 代码调用带有 void *parameter 的本机 c++ 函数的主要内容,如果未能解决你的问题,请参考以下文章

C#线程调用方法时,怎么传参数过去

如何从 C# 调用具有 void* 回调和对象参数的 C++ Dll 中的函数

调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()

C# 从 HttpResponseMessage 读取

如何从 C# 中的文件路径中去除引号和命令行参数?

从 C# 调用带有 C++ 标头的 dll