gpt4 book ai didi

c# - C++ DLL 和 C# 代码之间的共享内存

转载 作者:太空宇宙 更新时间:2023-11-03 10:31:02 29 4
gpt4 key购买 nike

我目前正在做一个期限非常短的项目,所以我没有太多时间了解所有内容。此外,我不是 C++ 开发和内存管理方面的专家。

所以,我想做的是用 C 和 C++ 代码创建一个 DLL。然后,我想在 C# 代码中调用这个 DLL。目前,C++和C#之间的通信是可以的。当我尝试将字符串从 DLL 传输到 C# 代码时出现问题。错误是这个:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr)
at System.StubHelpers.CSTRMarshaler.ClearNative(IntPtr pNative)
at NMSPRecognitionWrapper.Program.GetResultsExt()
at NMSPRecognitionWrapper.Program.<Main>b__0() in <my dir>\Program.cs:line 54
at NMSPRecognitionWrapper.Program.StartRecognitionExt()
at NMSPRecognitionWrapper.Program.Main(String[] args) in <my dir>\Program.cs:line 60

另外,我可以在下面给你一些代码(真的很简单!)。实际上,C++ 公开了两个方法: StartRecognition() 启动操作以从麦克风获取一些数据,然后处理它们并存储结果。 GetResults() 返回先前存储的结果的实例。 WrapperCallback() 允许在 Result 能够进行处理时调用 C# 部分。 C# 部分,当调用回调时,将要求使用 GetResults() 方法获取结果。

我知道这个架构在这个演示中可能看起来真的不合适,但我不想解释整个项目来验证模型,请确保一切正确。

最后,问题出在 C# 回调调用 GetResults() 方法时。尝试从 C# 访问 resultsForCS 似乎是不可能的。

C++ 部分 - header

// NMSPRecognitionLib.h

#pragma once
#include <iostream>

using namespace std;

extern "C" __declspec(dllexport) char* GetResults();
extern "C" static void DoWork();
extern "C" __declspec(dllexport) void StartRecognition();

C++ 部分 - 来源

#include "stdafx.h"
#include "NMSPRecognitionLib.h"

static char * resultsForCS;

static SUCCESS ProcessResult(NMSPCONNECTION_OBJECTS *pNmspConnectionObjects, LH_OBJECT hResult)
{
[...]
char* szResult;
[...]

resultsForCS = szResult;

DoWork();

[...]
return Success;

error:
return Failure;
} /* End of ProcessResult */


extern "C" __declspec(dllexport) char* GetResults()
{
return resultsForCS;
}

extern "C"
{
typedef void (*callback_function)();
callback_function gCBF;

__declspec(dllexport) void WrapperCallback(callback_function callback) {
gCBF = callback;
}

static void DoWork() {
gCBF();
}
}

extern "C" __declspec(dllexport) void StartRecognition()
{
char* argv[] = { "path", "params" };
entryPoint(2, argv);
}

C# 部分

class Program
{
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "GetResults")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetResultsExt();

public delegate void message_callback_delegate();

[DllImport("NMSPRecognitionLib.dll", EntryPoint = "WrapperCallback")]
public static extern void WrapperCallbackExt(message_callback_delegate callback);

[DllImport("NMSPRecognitionLib.dll", EntryPoint = "StartRecognition")]
public static extern void StartRecognitionExt();

static void Main(string[] args)
{
WrapperCallbackExt(
delegate()
{
Console.WriteLine(GetResultsExt());
}
);

StartRecognitionExt();

Console.WriteLine("\nPress any key to finish... ");
var nothing = Console.ReadLine();
}
}

我知道问题是因为我使用指针来存储结果(char *),但我实际上不知道如何用另一种方式来做到这一点。 szResults 类型也是 char *,我无法更改它!

最佳答案

是的,返回类型是问题所在。 pinvoke 编码器必须做一些事情来释放分配给字符串的内存。契约是调用者需要释放的内存分配必须从 COM 堆中分配。 native 代码中的 CoTaskMemAlloc(),也在 .NET 中公开为 Marshal.AllocCoTaskMem()。

这很少有好结果,大多数 native 代码使用 malloc() 或::operator new 进行分配,从 C 运行时库创建的堆中进行分配。错误的堆。因此 CoTaskMemFree() 调用不可避免地会失败。在 Windows XP 和更早版本中被静默忽略,在 Vista 和更高版本中是一个 kaboom。

您必须阻止 pinvoke 编码器尝试释放内存。通过将返回值声明为 IntPtr 来实现。并使用 Marshal.PtrToStringAnsi() 来恢复字符串。

您仍然有一个大问题,这种问题困扰着任何试图使用此功能的 native 代码。您仍然有一个需要释放的字符串缓冲区。您不能从 C# 执行此操作,您不能调用正确版本的 free() 或::operator delete。内存泄漏是不可避免的。您唯一可以希望的是 native 代码以某种方式处理它。如果没有,那么您必须使用 C++/CLI 与其进行互操作。额外的要求是需要使用相同的编译器重建 native 代码,以便它使用相同的共享 CRT。难以从 native 代码中正确使用的代码也很难调用。这是一个设计缺陷,始终允许调用者传递要填充的缓冲区,这样就不会出现谁拥有内存的问题。

关于c# - C++ DLL 和 C# 代码之间的共享内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16715985/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com