How to kill the process which is accessing an image file, so I can delete it in WinForms App.

john zyd 421 Reputation points
2021-10-21T09:23:50.077+00:00

Hello:
I have one WinForms App target .NET 5.0 on Windows 10.
There are around 100 PNG format files in one folder, I have to use one PictureBox to view one PNG file one at a time, and some PNG files will be deleted, as their quality is too bad.
So, I added one ListView to go through all the PNG files, and one button (ButtonDelete) to delete the currently selected PNG file.
But I don’t know how I can delete the PNG file, as it is being used by image = image.FromFile().
The following is part of my C# code:

private void ListViewImage_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if (ListViewImage.SelectedItems.Count >= 1)
{
string image_file1 = ListViewImage.SelectedItems[0].Text;
Image source_bmp = Image.FromFile(image);
PictureBoxImage0.Invoke((MethodInvoker)delegate
{ PictureBoxImage0.Image = source_bmp; });
}
}
}
catch (InvalidOperationException ex)
{
Debug.Print(ex.Message);
}
}

    private async void ButtonDelete_Click(object sender, EventArgs e)
    {
        try
        {
            PictureBoxImage0.Invoke((MethodInvoker)delegate { PictureBoxImage0.Dispose(); });
            await Task.Delay(2000);
            string png_file1 = @"C:\Images\1.png";
            // Image source_bmp = Image.FromFile(png_file1);
            // source_bmp.Dispose();
            File.Delete(png_file1);
        }
        catch (IOException ex)
        {
            Debug.Print("[ButtonDelete_Click]: " + ex.Message);
        }
    }

When I run my code, I got the following error:
[ButtonDelete_Click]: The process cannot access the file 'C:\Images\1.png' because it is being used by another process.
I know the file is used by this statement in ListViewImage_SelectedIndexChanged:
string image_file1 = ListViewImage.SelectedItems[0].Text;
Image source_bmp = Image.FromFile(image);
But how I can find which process is accessing the file, and how I can kill the process?
I find there is a new System.Management name space (Version 5.0.0), but I can’t find any good example on how to find the process using the file, then kill the process, so I can delete the PNG file.
Please advise!

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,097 questions
0 comments No comments
{count} votes

Accepted answer
  1. Abdelmalek Aitouche 176 Reputation points
    2021-10-24T10:25:26.44+00:00

    I have adapted my code to your requirement.
    Hope it will work, I have not tested it yet.

    private void Form1_Load(object sender, EventArgs e)
    {
    string sFile = @"D:\Images\Temp\Test.png";
    New System.IO.FileStream(sFile ,imgFileStream ,
    IO.FileMode.Open,IO.FileAccess.Read);
    Image source_bmp = Image.FromStream(imgFileStream);
    pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.Image = source_bmp; });
    }


4 additional answers

Sort by: Most helpful
  1. Castorix31 81,061 Reputation points
    2021-10-21T10:02:28.823+00:00

    If you don't dispose the pictureBox image associated with the file you delete, you will get "The process cannot access ..."

    For example, this test works (I use Tag to store the image file) :

    string sImageFile = @"E:\Temp\Images\Test.png";
    Image source_bmp = Image.FromFile(sImageFile);
    pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
    pictureBox1.Image = source_bmp;
    pictureBox1.Tag = sImageFile;
    

    then I can delete the file :

                if (pictureBox1.Image != null)
                {
                    pictureBox1.Image.Dispose();
                    pictureBox1.Image = null;
                }
                if (pictureBox1.Tag != null)
                {
                    System.IO.File.Delete((string)pictureBox1.Tag);
                    pictureBox1.Tag = null;
                }
    

  2. Abdelmalek Aitouche 176 Reputation points
    2021-10-24T09:52:42.19+00:00

    ' I had the same issue a few months back. If I understand the issue, your source file ' is locked and as such you can't delete it. You may need to do the following
    ' Streaming works better for source file deletion as it does not lock source file. No 'need to kill process.
    String filePath = String.Concat(Application.StartupPath, "\Images\myImage.jpg");
    If IO.File.Exists(filePath)
    {
    New System.IO.FileStream(filePath ,imgFileStream ,
    IO.FileMode.Open,IO.FileAccess.Read);
    PictureBox.Image = Image.FromStream(imgFileStream );
    }
    IO.File.Delete(filePath);
    }
    ' hope this will work in your case

    ' Translated from Visual Basic syntax

    /*I had the same issue a few months back. If I understand the issue, your source file ' is locked and as such you can't delete it. You may need to do the following:
    Streaming works better for source file deletion as it does not lock them. No need to kill process. */
    
    String  filePath = String.Concat(Application.StartupPath, "\Images\myImage.jpg");
                If IO.File.Exists(filePath) 
    {
                    New System.IO.FileStream(filePath ,imgFileStream , 
                                       IO.FileMode.Open,IO.FileAccess.Read);
                        PictureBox.Image = Image.FromStream(imgFileStream );
    }
                                       IO.File.Delete(filePath);
                }
    /* hope this will work in your case
    
    Translated from Visual Basic syntax*/
    

  3. Karen Payne MVP 35,016 Reputation points
    2021-10-21T11:20:07.137+00:00

    Seems attempting to find and kill a process is needed because whatever process is working with said images is not properly disposing the files and that is where you need to focus on.

    Not knowing your other process, perhaps review the following Stackoverflow post to see if the solutions might help. Several of the solutions are similar to something I wrote years ago.

    Ignore the PictureBox extension

    public static class ImageHelpers
    {
        /// <summary>
        /// Load a clone of an image
        /// </summary>
        /// <param name="fileName">Image file to load</param>
        /// <returns><see cref="Bitmap"/></returns>
        public static Bitmap LoadBitmap(string fileName)
        {
            Bitmap imageClone = null;
            var imageOriginal = Image.FromFile(fileName);
    
            imageClone = new Bitmap(imageOriginal.Width, imageOriginal.Height);
    
            Graphics gr = Graphics.FromImage(imageClone);
            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
            gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height);
            gr.Dispose();
            imageOriginal.Dispose();
    
            return imageClone;
        }
    
        #region PictureBox specific
    
        public static void LoadClone(this PictureBox pictureBox, string fileName)
        {
            Bitmap imageClone = null;
            var imageOriginal = Image.FromFile(fileName);
    
            imageClone = new Bitmap(imageOriginal.Width, imageOriginal.Height);
    
            Graphics gr = Graphics.FromImage(imageClone);
            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
            gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height);
            gr.Dispose();
            imageOriginal.Dispose();
    
            pictureBox.Image = imageClone; // assign the clone to picture box
            pictureBox.Tag = fileName;
        }
    
        public static void DeleteImage(this PictureBox pictureBox)
        {
            if (File.Exists(pictureBox.Tag.ToString()))
            {
                File.Delete(pictureBox.Tag.ToString());
            }
        }
    
        #endregion
    
    }
    

    It might be as simple as this (from the link above)

    Image img;
    using (var bmpTemp = new Bitmap("image_file_path"))
    {
        img = new Bitmap(bmpTemp);
    }
    

  4. Castorix31 81,061 Reputation points
    2021-10-21T16:43:21.57+00:00

    From comments, a test with IFileIsInUse and GetInfoForFileInUse
    to get the executable locking a file
    As it returns only the friendly name, I added code to get the .exe from this name : maybe it needs to be improved....

    // To lock an image file  
    //string sFile = @"E:\Hulk.png";  
    //pictureBox1.Image = System.Drawing.Image.FromFile(sFile);  
      
    // Notepad does not lock the file  
    // Wordpad locks the file  
    //string sFile = @"E:\test.txt";  
      
    // Winword locks the file  
    string sFile = @"E:\test.doc";  
      
    // Adobe locks the file  
    //string sFile = @"E:\test.pdf";            
      
    IShellItem pShellItem = null;  
    IFileIsInUse pFileIsInUse = null;  
    HRESULT hr = SHCreateItemFromParsingName(sFile, IntPtr.Zero, typeof(IShellItem).GUID, out pShellItem);  
    if (hr == HRESULT.S_OK)  
    {  
        hr = GetInfoForFileInUse(pShellItem, out pFileIsInUse);  
        if (hr == HRESULT.S_OK)  
        {  
            StringBuilder sbString = new StringBuilder(260);  
            hr = pFileIsInUse.GetAppName(out sbString);  
            if (hr == HRESULT.S_OK)  
            {  
                uint nFlags = 0;  
                hr = pFileIsInUse.GetCapabilities(out nFlags);  
                string sRightProcessPath = null;  
                string sProcessPath = null;  
                foreach (var procs in System.Diagnostics.Process.GetProcesses())  
                {                            
                    int nPID = procs.Id;  
                    IntPtr hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, nPID);  
                    if (hProcess != IntPtr.Zero)  
                    {  
                        uint nSize = 260;  
                        StringBuilder sProcessImageName = new StringBuilder((int)nSize);  
                        QueryFullProcessImageName(hProcess, 0, sProcessImageName, ref nSize);  
                        sProcessPath = sProcessImageName.ToString();  
                        CloseHandle(hProcess);  
                    }  
                    if (sProcessPath != null)  
                    {                                 
                        try  
                        {  
                            System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(sProcessPath);  
                            string sFileDescription = fvi.FileDescription;  
                            if (sFileDescription != null)  
                            {  
                                if (sFileDescription.ToLower().Contains(sbString.ToString().ToLower()))  
                                {  
                                    sRightProcessPath = sProcessPath;  
                                    break;  
                                }  
                            }  
                        }  
                        catch (Exception Ex)  
                        {  
                            string sText = Convert.ToString(Ex);  
                        }  
                    }  
                }  
                string sMessage = "Application : " + sbString.ToString();  
                if (sRightProcessPath != null)  
                    sMessage += Environment.NewLine + "Process path : " + sRightProcessPath;  
                System.Windows.Forms.MessageBox.Show(sMessage, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);  
            }  
            else  
            {  
                string sMessage = sFile + " not locked";  
                System.Windows.Forms.MessageBox.Show(sMessage, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);  
            }  
        }  
    }  
    

    Declarations :

    public enum HRESULT : int  
    {  
        S_OK = 0,  
        S_FALSE = 1,  
        E_NOINTERFACE = unchecked((int)0x80004002),  
        E_NOTIMPL = unchecked((int)0x80004001),  
        E_FAIL = unchecked((int)0x80004005),  
        E_UNEXPECTED = unchecked((int)0x8000FFFF),  
        E_OUTOFMEMORY = unchecked((int)0x8007000E)  
    }  
      
    [DllImport("Windows.storage.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern HRESULT GetInfoForFileInUse(IShellItem psi, out IFileIsInUse ppof);  
      
    [ComImport()]  
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]  
    [Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]  
    public interface IShellItem  
    {  
        HRESULT BindToHandler(IntPtr pbc, ref Guid bhid, ref Guid riid, ref IntPtr ppv);  
        HRESULT GetParent(ref IShellItem ppsi);  
        [PreserveSig]  
        HRESULT GetDisplayName(SIGDN sigdnName, ref System.Text.StringBuilder ppszName);  
        HRESULT GetAttributes(uint sfgaoMask, ref uint psfgaoAttribs);  
        HRESULT Compare(IShellItem psi, uint hint, ref int piOrder);  
    }  
      
    public enum SIGDN : int  
    {  
        SIGDN_NORMALDISPLAY = 0x0,  
        SIGDN_PARENTRELATIVEPARSING = unchecked((int)0x80018001),  
        SIGDN_DESKTOPABSOLUTEPARSING = unchecked((int)0x80028000),  
        SIGDN_PARENTRELATIVEEDITING = unchecked((int)0x80031001),  
        SIGDN_DESKTOPABSOLUTEEDITING = unchecked((int)0x8004C000),  
        SIGDN_FILESYSPATH = unchecked((int)0x80058000),  
        SIGDN_URL = unchecked((int)0x80068000),  
        SIGDN_PARENTRELATIVEFORADDRESSBAR = unchecked((int)0x8007C001),  
        SIGDN_PARENTRELATIVE = unchecked((int)0x80080001)  
    }  
      
    [ComImport()]  
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]  
    [Guid("64a1cbf0-3a1a-4461-9158-376969693950")]  
    public interface IFileIsInUse  
    {  
        [PreserveSig]  
        HRESULT GetAppName(out StringBuilder ppszName);   
        HRESULT GetUsage(out FILE_USAGE_TYPE pfut);   
        HRESULT GetCapabilities(out uint pdwCapFlags);  
        [PreserveSig]  
        HRESULT GetSwitchToHWND(out IntPtr phwnd);  
        HRESULT CloseFile();  
    }  
      
    public enum FILE_USAGE_TYPE  
    {  
        FUT_PLAYING = 0,  
        FUT_EDITING = (FUT_PLAYING + 1),  
        FUT_GENERIC = (FUT_EDITING + 1)  
    }  
      
    [DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern HRESULT SHCreateItemFromParsingName(string pszPath, IntPtr pbc, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);  
      
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);  
      
    public const int PROCESS_QUERY_LIMITED_INFORMATION = (0x1000);  
      
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern bool QueryFullProcessImageName(IntPtr hProcess, int dwFlags, StringBuilder lpExeName, ref uint lpdwSize);  
      
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern bool CloseHandle(IntPtr hObject);