How to close a file opened by another thread? - c#

I've a background thread which loads an image file using Image.FromFile, I want to close the opened image file from another thread
Is there a possibility to close a file opened by a background thread in c#?
Edit:
I used this code at first - on the same thread- but I don't know why I can't free the file or delete it, or even the new file which I saved using img.Save(...). So I tried to force closing on another thread. that's why I'm asking this question.
var img = Image.FromFile(filepath);
img.Save(filepath + ".jpg", ImageFormat.Jpeg);
img.Dispose();
if (File.Exists(filepath+ ".jpg"))
File.Delete(+ ".jpg");
if (File.Exists(filepath))
File.Delete(filepath);

The file isn't "Closed" until the image has been disposed. Open and close the file immediately in a single step by loading the image with the following snippet:
System.Drawing.Bitmap image;
using (var fileLoadImage = System.Drawing.Bitmap.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"))
{
image = new System.Drawing.Bitmap(fileLoadImage);
}
You need to be very careful with this pattern though because you are still going to have to explicitly call image.Dispose() when you are finished with your image. At least this will get you around file locking though.
Best of luck!
EDIT - A Runnable Snippet
Copy and paste the following into a new Console application to see that it works. Make sure you have a file called "test.jpg" in your sample pictures before you start and you will see this deletes both the original file and the new file just fine:
public class Program
{
public static void Main(string[] args)
{
System.Drawing.Bitmap image;
var originalFile = #"C:\Users\Public\Pictures\Sample Pictures\test.jpg";
var newFile = #"C:\Users\Public\Pictures\Sample Pictures\test2.jpg";
using (var fileLoadImage = System.Drawing.Bitmap.FromFile(originalFile))
{
image = new System.Drawing.Bitmap(fileLoadImage);
}
image.Save(newFile);
System.IO.File.Delete(originalFile);
image.Dispose();
System.IO.File.Delete(newFile);
}
}

Related

C# Save image without using Dialog

I am having trouble saving a user uploaded image to my "logos" file when the user presses "Save Team" on my form.
For you to get a taster of what the application does I have provided a screenshot here, upon pressing add team all the text box data is written to a text file and I want the image to automatically save into a predefined folder "logos", however I get the GDI++ error whenever I press save.
After reading through a few old threads on the web, I found that it could be caused by a few things such as file permissions, file size, the files name or even an open stream.
Below is the code I am currently using to save out my file which prompts the error:
private void btnAddTeam_Click(object sender, EventArgs e)
{
//VALIDATION FOR TEXT FILE AND OTHER STUFF HERE...
//SAVE THE IMAGE FILE
string filePath = picTeamLogo.ImageLocation;
Image tempImage = Image.FromFile(filePath);
tempImage.Save(#"../logos/");
}
If you are viewing the screenshot, please do not get confused by "arsenal.png" I am aware that that is not the full file path, its conversion is handled in another method, as only the filename is required to be written to the text file.
If anybody has any ideas on where I am going wrong then please point me in the right direction, vague errors such as the GDI one I just received are such a headache!
Alex.
Try this method
public void storeFile(FileUpload File, string dirName)
{
if (!Directory.Exists(MapPath("~/uploads/" + dirName)))
{
Directory.CreateDirectory(MapPath("~/uploads/" + dirName));
}
string saveLocation = MapPath("~/uploads/" + dirName + "/" + Path.GetFileName(File.FileName));
File.SaveAs(saveLocation);
}
that's because you have not specified the file name for save function !
take a look at this :
string filePath = picTeamLogo.ImageLocation;
FileInfo fi = new FileInfo(filePath);
Image tempImage = Image.FromFile(fi.FullName);
tempImage.Save(#"../logo/" + fi.Name);
now it works properly
See this earlier post of mine for similar issue...
A Generic error occurred in GDI+ in Bitmap.Save method
The stream remains locked - you can use memory stream as a temp location then save.
When either a Bitmap object or an Image object is constructed from a
file, the file remains locked for the lifetime of the object. As a
result, you cannot change an image and save it back to the same file
where it originated.
http://support.microsoft.com/?id=814675
string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
tempImage.Save(memory, ImageFormat.Jpeg);
// memory.ToStream(fs) // I think the same
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
Or alternative is loading image via MemoryStream into the image - so the stream doesn't get locked in the first place...

The action can't be completed because the file is open in IIS Worker Process

I am uploading a image for a user & saving it on a specified location. The code is working fine in normal scenario i.e. user selects a image & save it, it works.
Challenge occurs when user selects a image( probably wrong image due to mistake) & don't save it, but again selects a new image & then saves it.
This scenario gives me the error :
"The process cannot access the file because it is being used by another process."
When I try to delete image from the location at the time of error, file can't be deleted with message:
"The action can't be completed because the file is open in IIS Worker Process
Close the file and try again."
Code is like this:
try
{
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
msf = new MemoryStream();
bytes=FileUpload1.FileBytes;
msf.Write(bytes, 0, bytes.Length);
using (FileStream stream = new FileStream(folder + "/" + filename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//converting any graphic file type to jpeg format
Image img = Image.FromStream(msf);
img.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
msf.WriteTo(stream);
IsuploadSuccess = true;
img.Dispose();
}
}
catch
{
IsuploadSuccess = false;
}
finally
{
if (msf != null)
{
msf.Close();
msf.Dispose();
}
}
I have tried adding "FileAccess.ReadWrite" & "FileShare.ReadWrite" in file stream, but doesn't work either with all options in File stream.
Please help...
Instead of getting solution of problem, I finally change approach to get rid to source of challenge. I change file name with UserID (unique) appended with CurrentDateTime converted to string & store it to temp folder until user save the change. This forms a different file each time for the challenge scenario. After save each file in temp folder created by the user (preceded by unique userid) is deleted & last changes are saved to respective directory.
I had this problem and took me few hours to figure it out.
I my case the Bitmap new(path) had locked the file. I had to dispose of it before exiting the using block.
However it is the method by which I debugged it is important.
My approach in pinpointing the source of the problem was to open the folder where the image was saved and tracing the code line by line. After each line I deleted the file and did Ctrl+Z to put it back. If it gave an error when I tried to delete it, then that is the line that locked it. So I got to that line within 5 min.
using (MemoryStream memoryStream = new())
{
if (System.IO.File.Exists(path))
{
using(Bitmap bitmap = new(path)) <-- this solved it
{
ImageConverter imageConverter = new();
bitmap.Save(memoryStream, ImageFormat.Bmp);
bytes = memoryStream.ToArray();
}
}
}

Deleting a bitmap from local directory

Alright it has come to this. I searched this website among many others and no one can seem to give me a straight answer so I'm going to try just asking outright. Been on this issue for about a solid 3 days and I can't afford to waste any more time on it.
Goal: The app I am building is in WPF and is going to be used as a bug tracker for a project my design team and I will be undertaking soon. Since we are going to be building a game in C++ most of the errors that occur will have a visual element to them so I inlcuded functionality to provide an image of the error in question when the user adds a bug to the list. I then take that image and save it to a local directory (for testing). Now the image path in the Error object points to a path that leads to the local directory. This functionality has been tested and works fine. My problem showes up when I want to delete a bug from the list. I am getting that very infamous "IO Exception" saying that the image I want to delete is being used by another process.
So Far: At first I tried very elegant solutions, but as with all things you get to a point where you just want to see if you can get the thing to even work at all. So I am at the point where most of the code I am using is experimental and radical. So please when looking at it note that the code being used is out of desperation, so any "simple" solutions have probably already been tried (I did research this a lot becuase I hate having to do this). Things i can think of off the top of my head are the obsurd amount of disposes and forced garbage collections being called so please to not comment on the negative nature of this practice, I am well aware :).
The Code
Saving image to local directory
public void OnBrowseClick()
{
Microsoft.Win32.OpenFileDialog openBox = new Microsoft.Win32.OpenFileDialog();
// Show dialog box to user and grab output
Nullable<bool> result = openBox.ShowDialog();
if (result == true)
{
// Create temp variable to hold local path string
string localPath = Directory.GetCurrentDirectory();
// Grab the extension of the specified file path
string extension = openBox.FileName.Substring(openBox.FileName.LastIndexOf("\\"));
// Add extension to local path
localPath += extension;
// Create local copy of image at given file path (being ridiculous at this point)
using (Stream stream = new FileStream(openBox.FileName, FileMode.Open, FileAccess.ReadWrite))
{
using (Bitmap bmp = LoadImage(stream))
{
using (Bitmap temp = (Bitmap)bmp.Clone())
{
temp.Save(localPath);
temp.Dispose();
}
bmp.Dispose();
}
stream.Dispose();
}
// Set the URL in the image text box (UI stuff)
LocalError.ImagePath = localPath;
}
}
The following is the LoadImage function that is used in the function above
private Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap bitmap = new Bitmap(stream))
{
retval = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);
using (Graphics gdi = Graphics.FromImage(retval))
{
gdi.DrawImageUnscaled(bitmap, 0, 0);
gdi.Flush();
gdi.Dispose();
bitmap.Dispose();
}
}
// Garbage collection here to be safe
GC.WaitForPendingFinalizers();
GC.Collect();
return retval;
}
And finally we come to where I try to delete the image
public void OnDeleteClick()
{
// Ask user to make sure they want to delete selected item(s)
MessageBoxResult result = MessageBox.Show("Are you sure you want to delete selected item(s) from the list?",
"Delete", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
for( int i = 0; i < Parent.ErrorListControl.ErrorDataGrid.SelectedItems.Count; ++i)
{
// Get path to image
string path = (Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i] as Error).ImagePath;
// Even tried calling garbage collection here!!!!!
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
File.Delete(path);
// Remove the error from the error list
Parent.ErrorListVM.ErrorList.Remove((Error)Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i]);
// Decrement counter because we altered the list while in a loop
i--;
}
}
}
Notes: If anyone would like me to explain anything further or if you need to know something I left out please just ask I will get back to you ASAP! Any suggestions are helpful at this point I have absolutley no idea what I am doing wrong. I generally only program in a C++ environment so I tend to manage my own memory this whole "garbage collection" thing is really throwing a wrench in our project! (Off topic note: I do not know why I am not getting any color highlighting so I apologize to anyone who takes the time to read this).
Here's a simple way to do what you want. In this example, I'm using Path.GetTempFileName() to generate a random file name in the local user's temp directory. If you don't need to persist the files then it's a good place to store them temporarily. Also, the user could theoretically import two files with the same name. So you want to use some kind of random filename generation or other mechanism to avoid conflicts.
private void browseButton_Click(object sender, RoutedEventArgs e)
{
var openFileDialog = new Microsoft.Win32.OpenFileDialog();
if (openFileDialog.ShowDialog(this) == true)
{
using (Bitmap originalImage = new Bitmap(openFileDialog.FileName))
{
string tempFileName = System.IO.Path.GetTempFileName();
originalImage.Save(tempFileName);
// LocalError.LocalPath
LocalPath = tempFileName;
}
}
}
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(LocalPath))
{
File.Delete(LocalPath);
}
}
Although a simple File.Copy should suffice as long as you have the right paths, I was just providing a solution that matched your question.
EDIT:
Actually the current directory does not seem to be changed by the OpenFileDialog. I could swear that it did at some point. So I don't think this is your problem. Regardless, this code still works for me and you shouldn't require anything more complicated than this.
EDIT #2:
It seems the lock is actually caused by the image being databound to the view and presumably locked by the BitmapSource. You should be able to create it without locking the file. Generally, this is slower so don't do it this way unless you need to be able to modify or delete the file.
bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.CacheOption = BitmapCacheOption.OnLoad;
bitmapSource.CreateOption = BitmapCreateOptions.IgnoreImageCache;
bitmapSource.UriSource = new Uri(ImagePath, UriKind.Absolute);
bitmapSource.EndInit();
Since your LoadImage method does simple copy of the image, why not use File.Copy(source, dest) and avoid all the bitmaps, drawings, etc? Your goal might be to modify local bitmap after it's created, but it can still be done after copy.
Also, when using the using block, explicit .Dispose() is not required, as using block does it for you:
using (var obj = new SomeDisposableObject())
{
// code here
// obj.Dispose(); <-- not needed, since...
} // ...at this point obj.Dispose is called automatically.

The process cannot access the file ' ' because it is being used by another process

I am trying to delete local copy(on the computer) of an image file once uploaded using file dialog. It throws The process cannot access the file 'C:\Documents and Settings\username\My Documents\My Pictures\1220.bmp' because it is being used by another process.
private void _btnImportPhoto_Click(object sender, RoutedEventArgs e)
{
//user clicked import/change photo, open file dialog to browse for photo
System.Windows.Forms.OpenFileDialog fileDialog = new System.Windows.Forms.OpenFileDialog();
fileDialog.Multiselect = false;
fileDialog.Filter = ResourceFile.PhotoFileTypes;
if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
FilePath = fileDialog.FileName;
FilePathCopy = fileDialog.FileName;
string safeFilePath = fileDialog.SafeFileName;
Bitmap bitmap = new Bitmap(FilePath);
CurrentPhoto = bitmap;
Bitmap bitmap1 = new Bitmap(FilePathCopy); //A copy to save when if delete local copy is chosen
m_PhotoCopy = bitmap1;
FileSystem.DeleteFile(FilePath);
}
_btnSave.IsEnabled = _btnCancel.IsEnabled = true;
}
}
Please let me know how to work around this issue.
Thanks.
you need to dispose of the bitmap object try doing this. As this will dispose of the bitmap object as soon as it leaves the using context { }
using (Bitmap bitmap1 = new Bitmap(FilePathCopy))
{
//do all bitmap stuff in here
}
First I see
Bitmap bitmap = new Bitmap(FilePath);
CurrentPhoto = bitmap;
Where CurrentPhoto I presume some global variable that you want hold.
This, instead throws and exception:
FileSystem.DeleteFile(FilePath);
Cause image file at FilePath is actually CurrentPhoto. What you can do.
1) If use of CurrentPhoto has any sense inside this function, do what you want to do inside this function, and after dispose CurrentPhoto object, even in a way like #Bobby suggests (using block)
2) If you want to have it by the way, you can try to use Bitmap's Clone
method like this:
CurrentPhoto = bitmap.Clone();
and after call your:
bitmap.Dispose();
FileSystem.DeleteFile(FilePath);
Should work.
Try this...
http://www.lockergnome.com/blade/2006/11/28/windows-error-message-error-deleting-file-or-folder/
It will allow you to delete files or folder.

Reloading and refreshing flash files inside a windows form

Using DOTNET 3.5 . I have a app which shows load a flash movie in form, using this code
axShockwaveFlash1 = new AxShockwaveFlashObjects.AxShockwaveFlash()
axShockwaveFlash1.LoadMovie(0, Form1.currentGame);
The problem is that whenever I make a changes in the flash hosted in our application and try to refresh the to see the changes, the new changes is 'messed' up. to be more specific , it seems that the background and some controls of the previous flash still remain, 'spoiling' the new flash that is loaded. why?
Using the following methods before loading the second flash video makes no difference
axShockwaveFlash1.Refresh();
axShockwaveFlash1.Stop();
I tried other methods, it doesn't work for me. Here is what I did to achieve the desired result.
private void btnReload_Click(object sender, EventArgs e)
{
byte[] fileContent = File.ReadAllBytes(Application.StartupPath + #"\yourflashfile.swf");
if (fileContent != null && fileContent.Length > 0)
{
using (MemoryStream stm = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(stm))
{
/* Write length of stream for AxHost.State */
writer.Write(8 + fileContent.Length);
/* Write Flash magic 'fUfU' */
writer.Write(0x55665566);
/* Length of swf file */
writer.Write(fileContent.Length);
writer.Write(fileContent);
stm.Seek(0, SeekOrigin.Begin);
/* 1 == IPeristStreamInit */
//Same as LoadMovie()
this.axShockwaveFlash1.OcxState = new AxHost.State(stm, 1, false, null);
}
}
fileContent = null;
GC.Collect();
}
}
I copied the core code somewhere in SO but I don't remember the source.
Have you tried loading an "empty" flash video before loading your new video?
e.g.
axShockwaveFlash1.LoadMovie(0,"");
I'm sure that I encountered a similar problem and resolved it this way.
Try this.
Make a blank swf. "Blank.swf" load it first and then re-load your game.
axShockwaveFlash1.LoadMovie(0,"Blank.swf");
axShockwaveFlash1.Play();
axShockwaveFlash1.LoadMovie(0, Form1.currentGame);
axShockwaveFlash1.Play();
Ensure you give the correct path for the Blank.swf.

Categories