问题遇到的现象和发生背景
我的程序需要动态加载DLL,这本来是一个非常简单的事。但是我遇到了一点问题,我定义了一个Plugin类型
用来存放DLL 里面封装了 LoadLibrary、FreeLibrary 和一些简单的通信功能。
当我尝试卸载它们的时候,程序直接报错(并没有报错
直接返回了 程序“[20112] Lang.exe”已退出,返回值为 3221226356 (0xc0000374)。
这就很奇怪了,我新建了一个程序测试了无数遍 都没有发现这个奇怪的现象
经过我的调试每次出错都在此处(plugin的Dispose)
这是我的部分代码
报错的地方有"<标记>" 或许你可以直接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();
}
}
}