You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 16:54:03