[英]Shared memory between C++ DLL and C# code
我目前正在一個期限很短的項目上工作,所以我沒有太多時間來了解所有內容。 另外,我不是 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#部分。 當調用Callback時,C#部分將要求使用GetResults()
方法獲取結果。
我知道此演示文稿中的體系結構似乎確實不合適,但是我不想解釋整個項目以驗證模型,請確保一切正確。
最后,問題在於C#回調調用GetResults()
方法時。 從C#中似乎無法訪問resultsForCS
。
C ++部分-標頭
// 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堆中分配需要由調用方釋放的內存分配。 CoTaskMemAlloc()以本機代碼顯示,在.NET中也顯示為Marshal.AllocCoTaskMem()。
這很少有一個好的結局,大多數本機代碼使用malloc()或:: operator new進行分配,並從C運行時庫創建的堆中進行分配。 錯誤的堆。 因此,不可避免地,CoTaskMemFree()調用將失敗。 在Windows XP和更早版本(在Vista及更高版本上為kaboom)中被無聲忽略。
您必須停止嘗試釋放內存的pinvoke編組器。 為此,將返回值聲明為IntPtr。 並使用Marshal.PtrToStringAnsi()恢復字符串。
您仍然有一個大問題,這種問題使嘗試使用此功能的任何本機代碼也陷入困境。 您仍然有一個需要釋放的字符串緩沖區。 您無法從C#中執行此操作,也無法調用free()或:: operator delete的正確版本。 內存泄漏是不可避免的。 您唯一希望的是,本機代碼會以某種方式處理它。 如果不是,則必須使用C ++ / CLI進行互操作。 另外還需要使用相同的編譯器重建本機代碼,以使其使用相同的共享CRT。 很難從本地代碼正確使用的代碼也很難確定。 這是一個設計缺陷,始終允許調用者傳遞要填充的緩沖區,因此,誰擁有內存就沒有問題了。
看着:
at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr)
at System.StubHelpers.CSTRMarshaler.ClearNative(IntPtr pNative)
at NMSPRecognitionWrapper.Program.GetResultsExt()
我可以看到調用了您的回調,但是運行時嘗試釋放一些內存。 我認為它假設您的指針將指向com內存。 嘗試自己轉換字符串,這很容易!
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "GetResults")]
public static extern IntPtr GetResultsExt();
[...]
string result = Marshal.PtrToStringAnsi(GetResultsExt())
沒有100%的保證,但值得一試。
我發現使用C ++本機代碼在C ++ / CLI中編寫包裝程序通常更容易。 C ++ / CLI類可以直接調用和使用本機C ++,但是可以從C#(和任何.Net語言)訪問。 以我的經驗,在使用DLLImport時會導致難以調試和發現錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.