question

johnzyd-8697 avatar image
0 Votes"
johnzyd-8697 asked AbdelmalekAitouche-2691 commented

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

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!

dotnet-csharp
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AbdelmalekAitouche-2691 avatar image
0 Votes"
AbdelmalekAitouche-2691 answered AbdelmalekAitouche-2691 commented

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; });
}

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

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; });
source_bmp.Dispose();
}
The code works in my case! (Have to add source_bmp.Dispose();)
Thanks,

1 Vote 1 ·

Excellent. Good news.
Thanks

0 Votes 0 ·
Castorix31 avatar image
0 Votes"
Castorix31 answered Castorix31 commented

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
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello:
I don’t quite understand your code.
Please note that I loaded all the PNG files from other function, and show each image in ListViewImage_SelectedIndexChanged.
I click on Delete button to delete the currently shown image, so I don’t know where I put your code?
By the way, I have other program, in which I download HLS from internet and save as MP4 files in local hard drive. I have to copy the MP4 files to another folder every 2 hours, there is no way to find which process is accessing the MP4 file. I need to find which processes are using the files, and if any, kill the process then copy the files to other place.
So I need some function to find and kill the processes, which are impossible to dispose.
Please advise!

0 Votes 0 ·

Just put/adapt the code in your ListViewImage_SelectedIndexChanged and ButtonDelete_Click functions
For other cases, the process using a file can be found with IRunningObjectTable (IFileIsInUse)


0 Votes 0 ·
karenpayneoregon avatar image
0 Votes"
karenpayneoregon answered karenpayneoregon commented

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
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello:
I understand your code is that, you tried to load one clone image, then you can delete the original image.
However, it is not what I want, I want to find which process is accessing the image file, then kill the process, after the process is killed, then I can delete the original file.
Hope you understand my requirement. Your code may work, but it is not what I want.

0 Votes 0 ·

I can post a code with IFileIsInUse to get the process accessing a locked image file, but in your case, when you do not call pictureBox1.Image.Dispose(); as I posted
the process will be your own process... (I tested and IFileIsInUse.GetAppName returns logically my exe name in this case)

0 Votes 0 ·

Hello:
I want to see your code, can I use it to search for process which is using the image file?

0 Votes 0 ·

I understood your requirements yet as mentioned you should not need to kill a process if the process is your code which is why I replied this way.

0 Votes 0 ·
Castorix31 avatar image
0 Votes"
Castorix31 answered Castorix31 commented

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);






· 12
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello:
I would like to try your code. However, it seems your code is just a pure copy from C++ code, do you have a pure C# version. As I don't like C++ code.
Thanks,

0 Votes 0 ·

???

This is C# code with P/Invoke (!)

Interfaces and APIs must be called with P/Invoke if .NET classes don't exist...

0 Votes 0 ·

Hello:
I tested your code, it works.
But I need the code to kill the process which is accessing the file.
From your code, I don't know which process (ID) is accessing the file.
Please advise!
Thanks,

0 Votes 0 ·
Show more comments
AbdelmalekAitouche-2691 avatar image
0 Votes"
AbdelmalekAitouche-2691 answered henrihan-8261 commented

' 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*/
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello:
Your code seems to be a good way for my issue.
But I don't quite understand how to use it.
In my Form1_Load event, I have the following:
private void Form1_Load(object sender, EventArgs e)
{
string sFile = @"D:\Images\Temp\Test.png";
Image source_bmp = Image.FromFile(sFile);
pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.Image = source_bmp; });
}
How I can chage my code to use your code, and where I should put your code?



0 Votes 0 ·

You don't need any stream
you simply have to call Dispose, without delegate, as posted n times above

0 Votes 0 ·