qq_43045565 2022-01-06 04:49 采纳率: 75%
浏览 565

C# 加载动态加载卸载DLL 超过两次直接崩溃返回内存已损坏 (-1073740940)

问题遇到的现象和发生背景

我的程序需要动态加载DLL,这本来是一个非常简单的事。但是我遇到了一点问题,我定义了一个Plugin类型
用来存放DLL 里面封装了 LoadLibrary、FreeLibrary 和一些简单的通信功能。
当我尝试卸载它们的时候,程序直接报错(并没有报错
直接返回了 程序“[20112] Lang.exe”已退出,返回值为 3221226356 (0xc0000374)。
这就很奇怪了,我新建了一个程序测试了无数遍 都没有发现这个奇怪的现象

img

Ps:虽然名字一样但是都是不同的DLL

经过我的调试每次出错都在此处(plugin的Dispose)

img


这是我的部分代码

报错的地方有"<标记>" 或许你可以直接Ctrl+F找"<标记>"

这里是我加载DLL的地方

using CSharpClient;
using Lang.Api;
using PInvoke;
using Lang.Entity;
using System.Runtime.InteropServices;
using static Lang.Plugin.SiriusApiDelegates;
using static Lang.Log.Log;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Lang.Plugin
{
    public static class SiriusEvent
    {
        public static void SiriusOnEvent(string data)
        {
            LogOut(data);
        }
    }
    public static class PluginManager
    {
        public static readonly Mutex eventMutex = new(false);
        public static Mutex EventMutex { get => eventMutex; }
        public static List<Plugin> plugins = new List<Plugin>();

        private static bool FileCompare(string file1, string file2)
        {
            int file1byte;
            int file2byte;
            FileStream fs1;
            FileStream fs2;

            // Determine if the same file was referenced two times.
            if (file1 == file2)
            {
                // Return true to indicate that the files are the same.
                return true;
            }

            // Open the two files.
            fs1 = new FileStream(file1, FileMode.Open);
            fs2 = new FileStream(file2, FileMode.Open);

            // Check the file sizes. If they are not the same, the files
            // are not the same.
            if (fs1.Length != fs2.Length)
            {
                // Close the file
                fs1.Close();
                fs2.Close();

                // Return false to indicate files are different
                return false;
            }

            // Read and compare a byte from each file until either a
            // non-matching set of bytes is found or until the end of
            // file1 is reached.
            do
            {
                // Read one byte from each file.
                file1byte = fs1.ReadByte();
                file2byte = fs2.ReadByte();
            }
            while ((file1byte == file2byte) && (file1byte != -1));

            // Close the files.
            fs1.Close();
            fs2.Close();

            // Return the success of the comparison. "file1byte" is
            // equal to "file2byte" at this point only if the files are
            // the same.
            return ((file1byte - file2byte) == 0);
        }

        private static bool FilesCompare(FileInfo target, List<FileInfo> files)
        {
            bool compare = false;
            foreach (FileInfo f in files)
            {
                if (FileCompare(target.FullName, f.FullName))
                {
                    compare = true;
                    break; 
                }
            }
            return compare;
        }
        public static void LoadPlugin()
        {
            var path = $"{AppDomain.CurrentDomain.SetupInformation.ApplicationBase}Plugin";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            DirectoryInfo directoryInfo = new(path);
            FileInfo[] files = directoryInfo.GetFiles();//文件

            List<FileInfo> pluginFiles = new();

            foreach (FileInfo file in files)
            {
                if (!FilesCompare(file, pluginFiles))
                {
                    pluginFiles.Add(file);
                }
                else
                {
                    LogWar($"存在完全相同的文件:{file.FullName},已跳过");
                }
            }

            foreach(FileInfo pluginFile in pluginFiles)
            {

                if (Path.GetExtension(pluginFile.FullName).ToLower() == ".dll")
                {
                    Plugin plugin = new(pluginFile.FullName);
                    if (!plugin.Refuse)
                    {
                        if (null != plugin.PluginInfo)
                        {
                            LogOk($"{plugin.PluginInfo.pluginName} 加载完毕," +
                            $"作者:{plugin.PluginInfo.pluginAuthor} " +
                            $"版本:{plugin.PluginInfo.pluginVersion}," +
                            $"描述:{plugin.PluginInfo.pluginDis}," +
                            $"SDK版本:{plugin.PluginInfo.pluginSDKVer}");
                            plugins.Add(plugin);
                        }
                        else
                        {
                            LogErr(Path.GetFileName($"{pluginFile.Name}未公开AppInfo,加载失败!"));
                            plugin.Dispose(); // 执行多次时.. 崩溃 <标记>
                        }
                    }
                    else
                    {
                        if (null != plugin.PluginInfo)
                        {
                            LogErr(Path.GetFileName($"{plugin.PluginInfo.pluginName}插件主动拒绝被加载."));
                        }
                        else
                        {
                            LogErr(Path.GetFileName($"{pluginFile.Name}未公开AppInfo,加载失败!"));
                        }
                        RemovePlugin(plugin); // 执行多次时.. 崩溃 <标记>
                    }
                }

            }

        }
        public static void RemovePlugin(Plugin plugin)
        {
            EventMutex.WaitOne();
            plugins.Remove(plugin);
            plugin.Dispose(); // 崩溃 <标记>
            EventMutex.ReleaseMutex();
        }
        public static void RemovePlugin(int index)
        {
            try
            {
                EventMutex.WaitOne();
                var plugin = plugins[index];
                plugin.Dispose(); // 崩溃 <标记>
                File.Delete(plugin.pluginFilePath);
                LogOk(plugin.PluginInfo.pluginName + "->卸载成功!");
                plugins.Remove(plugin);
                EventMutex.ReleaseMutex();
            }
            catch (Exception e)
            {
                LogErr("卸载插件失败:" + e);
            }
        }
    }
    public static class SiriusApi
    {
    }
    public static class SiriusApiDelegates
    {
    }
    public static class SiriusTools
    {
        public static string GetRandomKey()
        {
            var random = new Random();
            const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return new string(Enumerable.Repeat(chars, 32)
              .Select(s => s[random.Next(s.Length)]).ToArray());
        }
    }
    /// <summary>
    /// 用户自定义插件信息
    /// 适用于x86的
    /// 易语言 C++ 等...
    /// </summary>
    public class PluginInfo
    {
        [JsonPropertyName("name")]
        public string pluginName { get; set; }
        [JsonPropertyName("author")]
        public string pluginAuthor { get; set; }
        [JsonPropertyName("dis")]
        public string pluginDis { get; set; }
        [JsonPropertyName("ver")]
        public string pluginVersion { get; set; }
        [JsonPropertyName("sdk")]
        public string pluginSDKVer { get; set; }
    }
}


这里是我定义的Plugin

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lang.Plugin.Interface;
using PInvoke;

namespace Lang.Plugin
{
    public class Plugin : IPlugin
    {
        public readonly Kernel32.SafeLibraryHandle handle;
        private readonly PluginFunc pluginFunc;
        public bool Refuse { get; set; }
        public PluginInfo PluginInfo { get; set; }
        public string pluginFilePath { get; set; }
        public string pluginConfigPath { get; set; }
        public string pluginKey { get; set; }
        public Plugin(string path)
        {
            try
            {
                handle = Kernel32.LoadLibrary(path);
                pluginFunc = PluginFunc.CreatePluginFunction(handle);
                pluginKey = SiriusTools.GetRandomKey();
                PluginInfo = pluginFunc.OnInfo()!;
                pluginFunc.OnInit(pluginKey, PluginData.CreatePluginData(PluginInfo.pluginName));
                Refuse = pluginFunc.OnLoad() == 1;
                pluginFilePath = path;
            }catch(Exception ex)
            {
                Log.Log.LogErr($"载入插件时出错! {ex}");
                Refuse = true;
            }
        }
        public void Dispose()
        {
            PluginManager.eventMutex.WaitOne();
            GC.SuppressFinalize(this);
            OnStop();
            handle.Dispose();// 这里才是真正的卸载销毁 最后崩溃都会在此处 <标记>
            PluginManager.eventMutex.ReleaseMutex();
        }
    }
}

  • 写回答

4条回答 默认 最新

  • qq_43045565 2022-01-08 13:49
    关注

    此问题已经解决了
    问题是调用DLL中返回的String没有进行深拷贝

    我将PluginInfo这里 DLL中的返回值是文本(char*) 我将C#中的Delegate返回值从string改为了IntPtr

        public class PluginDelegates
        {
            public delegate void Init(string key, string runPath); //初始化插件
            public delegate IntPtr AppInfo(); //初始化插件 -> 返回插件信息
            public delegate void Menu();// 插件菜单
            public delegate int OnLoad();// 加载插件
            public delegate void OnUnLoad();// 卸载插件
            public delegate void OnGuildMessage(string jsonData); // 收到消息
            public delegate int OnEnable();
            public delegate void OnDisable();
            public delegate void OnCreateGuild(string jsonData);
            public delegate void OnDeleteGuild(string jsonData);
            public delegate void OnUpDateGuild(string jsonData);
            public delegate int OnCommand(string command);
        }
    

    注意看AppInfo 原来是String

    随后调用解析时 使用

    var data = appInfo();
    Marshal.PtrToStringAnsi(data)
    

    即可

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 1月6日

悬赏问题

  • ¥100 用数字代替ip主机号断与旋转链接号段
  • ¥20 学校内手机热点不能使用
  • ¥15 qwen模型微调训练时grad_norm为0.0或为nan
  • ¥15 若依- Spring cloud-plus集成阿里云schedulerX再控制台应用管理找不到实例
  • ¥15 用vs2022 fortran调试只无法查看数组的值,显示发生未指定的错误
  • ¥50 SGD Gateway 是否可以与 SGD Server 装在一台服务器上?
  • ¥15 gge梦幻西游的地图制作
  • ¥15 对计算机的学习没有方向
  • ¥15 用VBA抓取淘宝的发票号码和下载发票pdf遇到的问题
  • ¥20 java excel导出有什么解决方案