【问题标题】:Memory leak while using C# dll in a C++在 C++ 中使用 C# dll 时发生内存泄漏
【发布时间】:2021-01-15 08:59:18
【问题描述】:

情况是,我包装了 C# dll 以在 C++ 项目中使用它,当我执行 C++ 项目时,我看不到任何有关内存泄漏的迹象,但内存逐渐增加。我认为这是因为 C# 库中的 GC 在 C++ 项目中不起作用,我不知道如何解决。请帮帮我。

我的代码如下:

  1. C#
    using Microsoft.Win32.SafeHandles;
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    namespace CSharpLib
    {

        [Guid("8EA9EAA8-CA3D-4584-B1E0-7B9561757CA4")]
        public interface ICSharpLibrary
        {
            int[] GetData();
            string GetName();
            bool Init();
        }

        [Guid("B62A2B51-621D-41AA-8F4F-021E404B593C")]
        public class CSharpLibrary : ICSharpLibrary, IDisposable
        {
            private bool disposed = false;

            private int[] data;

            private string name = "CSharpLib";

            private SafeHandle safeHandle = new SafeFileHandle(IntPtr.Zero, true);

            private void MakeData()
            {
                var list = new List<int>();
                var rnd = new Random();
                for(var i = 0; i < 1000; i++)
                {
                    list.Add(rnd.Next(0, 255));
                }
    
                data = list.ToArray();
            }
    
            public int[] GetData()
            {
                MakeData();
                return data;
            }
    
            public string GetName()
            {
                return name;
            }
    
            public bool Init()
            {
                data = null;
    
                return true;
            }
    
            public void Dispose()
            {
                Dispose(true);
            }
    
            protected virtual void Dispose(bool disposing)
            {
                if (disposed)
                {
                    return;
                }
                if (disposing)
                {
                    data = null;
                    safeHandle.Dispose();
                }
    
                disposed = true;
            }
        }
    }
  1. C++

    #pragma once
    
    #ifdef CPLUSLIBRARY_EXPORTS
    #define CPLUSLIBRARY_API __declspec(dllexport)
    #else
    #define CPLUSLIBRARY_API __declspec(dllimport)
    #endif
    
    extern "C" CPLUSLIBRARY_API bool Init();
    
    extern "C" CPLUSLIBRARY_API int* GetData();
    
    extern "C" CPLUSLIBRARY_API char* GetName();

    #include <Windows.h>
    #include "pch.h"
    #include "CPlusLibrary.h"
    
    #import "lib/CSharpLibrary.tlb" no_namespace named_guids
    
    ICSharpLibrary* lib = NULL;
    int* data = NULL;
    bool isWorking = false;
    char* name = NULL;
    
    bool Init() {
        HRESULT hr = CoInitializeEx(NULL, tagCOINIT::COINIT_MULTITHREADED);
    
        if (!SUCCEEDED(hr))
            return false;
    
        hr = CoCreateInstance(CLSID_CSharpLibrary, NULL, CLSCTX_INPROC_SERVER, IID_ICSharpLibrary, reinterpret_cast<void**>(&lib));
    
        if (!SUCCEEDED(hr))
            return false;
    
        return true;
    }
    
    int* GetData() {
        auto raw = lib->GetData();
        void* pVoid = NULL;
    
        HRESULT hr = ::SafeArrayAccessData(raw, &pVoid);
    
        if (!SUCCEEDED(hr))
            return NULL;
        auto result = reinterpret_cast<int*>(pVoid);
    
        return result;
    }
    
    char* GetName() {
        char str[10];
        strcpy_s(str, lib->GetName());
        name = str;
    
        return name;
    }
  1. 执行者
    #include <iostream>
    #include <CPlusLibrary.h>
    #include <CoreWindow.h>
    
    int main()
    {
        if (!Init())
            return 0;
    
        while (true) {
            auto name = GetName();
            std::cout << name << std::endl;
            auto data = GetData();
    
            for (int i = 0; i < 1000; i++) {
                std::cout << data[i];
            }
    
            std::cout << std::endl;
    
            Sleep(100);
        }
    }

【问题讨论】:

  • 这里使用auto 使得无法确切地看到发生了什么。 documentation for #import 表示它确实使用智能指针作为接口指针。它没有提到智能指针包装器是否用于 SAFEARRAY 和 BSTR,这对于理解此代码至关重要。我认为因为没有在导入中指定raw_native_types 属性,所以_bstr_t 包装器正在使用中。仍然没有迹象表明 SAFEARRAY 是否已包装。

标签: c# c++ memory dll memory-leaks


【解决方案1】:

来自SafeArrayAccessData documentation

调用SafeArrayAccessData后,必须调用SafeArrayUnaccessData函数解锁数组。

不确定这是泄漏的实际原因。但是在调试问题时,第一步是确保您遵循文档中指定的所有规则。

【讨论】:

  • 情况更糟。根据将类型库转换为 C++ 包装器的选项,它可以使用需要手动释放的原始 SAFEARRAY 和 BSTR,或者 SAFEARRAY 和 BSTR 由作用域末尾的智能指针释放,在这种情况下,GetData() 将一个悬空指针返回到一个已释放的数组中。
  • @Ben Voigt 你似乎比我更关心这个。我主要认为根本没有资源清理很奇怪。我主要习惯于从 c# 到 c++ 的接口。我认为如果你能写一个更详细地解释问题的答案会很有用。
  • 不幸的是,我不熟悉的部分(#import 的默认行为)是了解这里发生了什么的关键。我不确定 auto raw = lib-&gt;GetData(); 是否声明了 _variant_tSAFEARRAY* 或其他一些隐式转换允许它直接与 SafeArrayAccessData 一起使用的 SAFEARRAY 包装器。
  • 谢谢,您的回答对我帮助很大!我对SAFEARRAY不是很了解,原因是我没有调用SafeArrayUnaccessData和SafeArrayDestroy函数。
猜你喜欢
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多