簡體   English   中英

C ++ DLL和C#代碼之間的共享內存

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM