C#中已释放Image仍提示文件被其他进程占用无法删除如何解决
解决方案
问题根本原因
System.Drawing 下的 Bitmap 类和 Image.FromFile 方法在直接从文件路径加载资源时,会在对象生命周期内持续锁定源文件,你的代码存在两个核心问题:
- 给PictureBox赋值的Bitmap对象从未主动释放,持续占用源文件
- 定时器处于激活状态时会持续触发图片加载逻辑,会随机占用待删除的图片文件
修复步骤
1. 新增不锁定文件的图片加载辅助方法
通过将文件读取到内存流再构建Bitmap的方式,完全释放对源文件的锁定:
private Bitmap LoadImageWithoutLock(string filePath) { using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { // 从文件流读取数据到内存后构建Bitmap,不占用原文件句柄 using (var originalImg = Image.FromStream(fs)) { return new Bitmap(originalImg); } } }
2. 新增PictureBox图像安全赋值方法
每次赋值新图像前主动释放旧图像,避免内存泄漏和资源残留占用:
private void SetPictureBoxImage(PictureBox pb, Bitmap newImg) { pb.Image?.Dispose(); pb.Image = newImg; }
3. 修改所有图片加载逻辑,替换为不锁文件的加载方式
3.1 修改CheckIfImagesExist方法
把所有new Bitmap(文件路径)的代码替换为调用LoadImageWithoutLock,配合安全赋值方法:
private void CheckIfImagesExist() { GetImagesFiles(); if (filesRadar != null) { if (filesRadar.Length > 1) { SetPictureBoxImage(pictureBox1, LoadImageWithoutLock(filesRadar[0])); trackBar1.Enabled = true; timer1.Enabled = true; } else if (filesRadar.Length == 1) { trackBar1.Enabled = false; SetPictureBoxImage(pictureBox1, LoadImageWithoutLock(filesRadar[0])); } } if (filesSatellite != null) { if (filesSatellite.Length > 1) { SetPictureBoxImage(pictureBox2, LoadImageWithoutLock(filesSatellite[0])); trackBar1.Enabled = true; timer2.Enabled = true; } else if (filesSatellite.Length == 1) { trackBar1.Enabled = false; SetPictureBoxImage(pictureBox2, LoadImageWithoutLock(filesSatellite[0])); } } }
3.2 修改定时器Tick事件的加载逻辑
同样替换为新的加载和赋值方法:
int satImagesCount = 0; private void timer2_Tick(object sender, EventArgs e) { satImagesCount++; if (satImagesCount == filesSatellite.Length) { satImagesCount = 0; } SetPictureBoxImage(pictureBox2, LoadImageWithoutLock(filesSatellite[satImagesCount])); if (isInsideSat) { SetPictureBoxImage(pb, LoadImageWithoutLock(filesSatellite[satImagesCount])); timer2.Interval = trackBar1.Value * 100; } }
4. 修改生成GIF的核心方法
执行转换前先停止定时器,避免转换过程中定时器触发加载文件,转换完成后再恢复定时器并刷新图片列表:
private void GenerateAnimatedGifs() { // 先停止所有定时器,避免转换过程中触发图片加载 timer1.Enabled = false; timer2.Enabled = false; UnFreezWrapper unfreezWrapper = new UnFreezWrapper(); checkBoxGetImages = false; checkBoxGetAllImages.Checked = false; GetImagesFiles(); for (int i = 0; i < filesSatellite.Length; i++) { // 使用不锁文件的方式读取图片 using (Image img = LoadImageWithoutLock(filesSatellite[i])) { img.Save(filesSatellite[i] + "ConvertedToGif.gif", System.Drawing.Imaging.ImageFormat.Gif); } File.Delete(filesSatellite[i]); } GetImagesFiles(); unfreezWrapper.MakeGIF(filesRadar.ToList(), @"d:\Downloaded Images\Animates Gifs\radanim.gif", 100, true); unfreezWrapper.MakeGIF(filesSatellite.ToList(), @"d:\Downloaded Images\Animates Gifs\satanim.gif", 100, true); // 转换完成后重新检查图片,恢复定时器 CheckIfImagesExist(); }
额外注意事项
- radar相关的timer1逻辑也需要按照timer2的修改方式做同样调整
- 窗体关闭时要主动释放所有PictureBox的Image对象,避免GDI资源泄漏
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

