操作文件会阻止它们
本文关键字:文件 操作 | 更新日期: 2023-09-27 18:14:02
我正在编写一个使用MEF加载程序集的WinForms程序。这些程序集与可执行文件不在同一个文件夹中。由于我需要执行一些文件维护,在加载实际的WinForm之前,我在文件program. cs中实现了一些代码,因此这些文件(即使是程序集)不会被程序加载(或者不应该被程序加载)。
我正在执行两个操作:—将文件夹从一个位置移动到另一个位置-从存档中解压缩文件并覆盖移动文件夹中的dll文件(如果存档中的文件比移动的文件更新)
问题是移动文件夹后,其中的文件被锁定,不能被覆盖。我还尝试过一个一个地移动文件,在移动完成后处理它们。
谁能告诉我为什么文件被阻止了,我怎么才能避免呢?
感谢private static void InitializePluginsFolder()
{
if (!Directory.Exists(Paths.PluginsPath))
{
Directory.CreateDirectory(Paths.PluginsPath);
}
// Find archive that contains plugins to deploy
var assembly = Assembly.GetExecutingAssembly();
if (assembly.Location == null)
{
throw new NullReferenceException("Executing assembly is null!");
}
var currentDirectory = new FileInfo(assembly.Location).DirectoryName;
if (currentDirectory == null)
{
throw new NullReferenceException("Current folder is null!");
}
// Check if previous installation contains a "Plugins" folder
var currentPluginsPath = Path.Combine(currentDirectory, "Plugins");
if (Directory.Exists(currentPluginsPath))
{
foreach (FileInfo fi in new DirectoryInfo(currentPluginsPath).GetFiles())
{
using (FileStream sourceStream = new FileStream(fi.FullName, FileMode.Open))
{
using (FileStream destStream = new FileStream(Path.Combine(Paths.PluginsPath, fi.Name), FileMode.Create))
{
destStream.Lock(0, sourceStream.Length);
sourceStream.CopyTo(destStream);
}
}
}
Directory.Delete(currentPluginsPath, true);
}
// Then updates plugins with latest version of plugins (zipped)
var pluginsZipFilePath = Path.Combine(currentDirectory, "Plugins.zip");
// Extract content of plugins archive to a temporary folder
var tempPath = string.Format("{0}_Temp", Paths.PluginsPath);
if (Directory.Exists(tempPath))
{
Directory.Delete(tempPath, true);
}
ZipFile.ExtractToDirectory(pluginsZipFilePath, tempPath);
// Moves all plugins to appropriate folder if version is greater
// to the version in place
foreach (var fi in new DirectoryInfo(tempPath).GetFiles())
{
if (fi.Extension.ToLower() != ".dll")
{
continue;
}
var targetFile = Path.Combine(Paths.PluginsPath, fi.Name);
if (File.Exists(targetFile))
{
if (fi.GetAssemblyVersion() > new FileInfo(targetFile).GetAssemblyVersion())
{
// If version to deploy is newer than current version
// Delete current version and copy the new one
// FAILS HERE
File.Copy(fi.FullName, targetFile, true);
}
}
else
{
File.Move(fi.FullName, targetFile);
}
}
// Delete temporary folder
Directory.Delete(tempPath, true);
}
检查这部分代码中使用的GetAssemblyVersion()
方法的实现:
if (File.Exists(targetFile))
{
if (fi.GetAssemblyVersion() > new FileInfo(targetFile).GetAssemblyVersion())
{
// If version to deploy is newer than current version
// Delete current version and copy the new one
// FAILS HERE
File.Copy(fi.FullName, targetFile, true);
}
}
fi
变量具有FileInfo
类型,GetAssemblyVersion()
看起来像一个扩展方法。您应该检查如何从文件检索程序集版本。如果此方法加载程序集,则还应卸载该程序集以释放文件。
单独的AppDomain
是有帮助的,如果你需要加载程序集,完成工作,然后卸载它。下面是GetAssemblyVersion
方法的实现:
public static Version GetAssemblyVersion(this FileInfo fi)
{
AppDomain checkFileDomain = AppDomain.CreateDomain("DomainToCheckFileVersion");
Assembly assembly = checkFileDomain.Load(new AssemblyName {CodeBase = fi.FullName});
Version fileVersion = assembly.GetName().Version;
AppDomain.Unload(checkFileDomain);
return fileVersion;
}
以下GetAssemblyVersion()
的实现可以在不将程序集加载到AppDomain
的情况下检索程序集版本。感谢@usterdev的提示。它还允许您获取没有程序集引用的版本resolve:
public static Version GetAssemblyVersion(this FileInfo fi)
{
return AssemblyName.GetAssemblyName(fi.FullName).Version;
}
您必须确保您没有将Assembly
加载到您的域以从中获取版本,否则文件将被锁定。
通过使用AssemblyName.GetAssemblyName()
静态方法(参见MSDN),程序集文件被加载,版本被读取,然后卸载,但不添加到您的域。
这里是FileInfo
的扩展:
public static Version GetAssemblyVersion(this FileInfo fi)
{
AssemblyName an = AssemblyName.GetAssemblyName(fi.FullName);
return an.Version;
}
下面的语句锁定文件
destStream.Lock(0, sourceStream.Length);
但是在那之后你还没有解锁文件。
我会开始检查你的程序是否实际上已经加载了程序集。
两个建议:
1 -在调用InitializePluginsFolder
之前调用这样的方法static void DumpLoadedAssemblies()
{
var ads = AppDomain.CurrentDomain.GetAssemblies();
Console.WriteLine(ads.Length);
foreach (var ad in ads)
{
Console.WriteLine(ad.FullName);
// maybe this can be helpful as well
foreach (var f in ad.GetFiles())
Console.WriteLine(f.Name);
Console.WriteLine("*******");
}
}
2 -在Main的第一行,注册AssemblyLoad事件并在事件处理程序
中转储加载的程序集public static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += OnAssemlyLoad;
...
}
static void OnAssemlyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("Assembly Loaded: " + args.LoadedAssembly.FullName);
}
您肯定使用AssemblyName加载程序集。不幸的是,. net没有常规的方法在不加载程序集的情况下检查程序集元数据。要避免这种情况,可以:
- 加载组件在分离的AppDomain尼基塔建议,我可以添加:加载它与ReflectionOnlyLoad
- 或使用Mono获取汇编版本。Cecil库作为Reflector做
- 只是为了完整:实际上你可以在不锁定汇编文件的情况下将汇编加载到同一个AppDomain中,分为两个阶段:将文件内容读入byte[]和使用assembly。加载(byte[] rawAssembly),但这种方式有严重的"加载上下文"问题,你将如何处理几个加载的程序集:)