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

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • HaLo2FrEeEk
    Contributor
    • Feb 2007
    • 404

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

    I've got a program that downloads a set of thumbnail images specified in a remote XML file. There can be up to 30 thumbnails downloaded. I'm using a foreach loop iterating through an XmlNodeList of all the <item> tags in the XML file and inside that foreach loop I start a new Thraed that runs the downloadThumb method. Here is my downloadThumb method:

    Code:
    public void downloadThumb(object Url)
            {
                string url = Url.ToString();
                Uri parts = new Uri(url);
                string query = parts.Query;
                string filename = query.Replace("?ssid=", "");
                if(!File.Exists("./thumbs/" + filename + "-Thumbnail.jpg"))
                {
                    HttpWebRequest thumbreq = (HttpWebRequest)WebRequest.Create(url);
                    HttpWebResponse thumbres = (HttpWebResponse)thumbreq.GetResponse();
                    BinaryReader thumb = new BinaryReader(thumbres.GetResponseStream());
                    FileStream fstream = new FileStream("./thumbs/" + filename + "-Thumbnail.jpg", FileMode.Create, FileAccess.Write);
                    BinaryWriter file = new BinaryWriter(fstream);
                    byte[] thumbnail = thumb.ReadBytes((int)thumbres.ContentLength);
                    file.Write(thumbnail);
                    file.Close();
                    thumb.Close();
                    fstream.Close();
                    thumbres.Close();
                }
            }
    Probably not the prettiest way, but it's the only way I could get it to write the thumbnail images properly to disk.

    Anyway, I want the ./thumbs/ folder (with all the images inside) to be deleted when I close the program. I set up this in my .designer.cs file:

    this.Closing += new System.Componen tModel.CancelEv entHandler(this .Form_Closing);

    And my Form_Closing method:

    Code:
    public void Form_Closing(object sender, CancelEventArgs cArgs)
            {
                dataGridView1.Dispose();
                if (Directory.Exists("./thumbs"))
                {
                    Directory.Delete("./thumbs", true);
                }
            }
    dataGridView1 being the datagridview that I'm posting the information I'm parsing out of the XML file to. I figured that because I'm displaying the thumbnail I'm downloading inside a dataGridViewIma geCell that disposing the dataGridView would sever the association with the files...that's not the case. I need to know how I can disassociate the process with those files so I can delete them automatically. Can I get some help?
  • Plater
    Recognized Expert Expert
    • Apr 2007
    • 7872

    #2
    If you open an Image or Bitmap object from a file, it counts as the file being in use

    Comment

    • tlhintoq
      Recognized Expert Specialist
      • Mar 2008
      • 3532

      #3
      Can we see the code that you use to open the thumbnails from the local harddrive?

      I'm going to guess that you are using a loop that loads the thumbs into a list or array.

      Code:
      for (int Index =0; Index<30; Index++)
      {
         mythumbs[Index] = Image.FromFile(ThumbnailPathList[Index]);
      }
      Am I close?

      Comment

      • HaLo2FrEeEk
        Contributor
        • Feb 2007
        • 404

        #4
        Here is the entire process from parsing all the <item>'s out of the XML up to the end of the foreach loop, which includes loading the bitmap.

        Code:
        XmlNodeList items = xdoc.GetElementsByTagName("item");
                        overallProgress.Maximum = items.Count;
                        foreach (XmlElement item in items)
                        {
                            XmlNodeList child = item.ChildNodes;
                            string title = child[0].InnerText.ToString();
                            string date = child[2].InnerText.ToString();
                            string full = child[6].InnerText.ToString();
                            string thumb = child[8].InnerText.ToString();
                            string description = child[9].InnerText.ToString();
                            //ThreadPool.QueueUserWorkItem(downloadThumb, thumb);
                            Thread t = new Thread(downloadThumb);
                            t.Start(thumb);
                            t.Join();
                            downloadThumb(thumb);
                            Uri parts = new Uri(thumb);
                            string query = parts.Query;
                            string filename = query.Replace("?ssid=", "");
                            DataGridViewRow newRow = new DataGridViewRow();
                            newRow.Height = 90;
                            DataGridViewImageCell thumbCell = new DataGridViewImageCell();
                            DataGridViewCheckBoxCell downloadCell = new DataGridViewCheckBoxCell();
                            thumbCell.Value = new Bitmap("./thumbs/" + filename + "-Thumbnail.jpg");
                            downloadCell.Value = true;
                            newRow.Cells.Add(downloadCell);
                            newRow.Cells.Add(thumbCell);
                            dataGridView1.Rows.Add(newRow);
                            downloadCell.Selected = true;
                            overallProgress.PerformStep();
                        }
        You can see I'm loading the bitmap like this:

        thumbCell.Value = new Bitmap("./thumbs/" + filename + "-Thumbnail.jpg") ;

        So disposing of the dataGridView should do the trick.

        Comment

        • GaryTexmo
          Recognized Expert Top Contributor
          • Jul 2009
          • 1501

          #5
          Out of curiosity, are you trying to access the file somewhere else in your program, or is the file access getting left open after you've closed it?

          If you want to close the file handle, you can read the file into a memory stream, close the file, and load the image from the memory stream. Of course, this means you'll have all your images loaded into memory (can get big if your images are large), but you won't have the open file handle.

          Also, just a heads up, if you close the memory stream on an animated bitmap you'll throw a GDI+ exception.

          Comment

          • HaLo2FrEeEk
            Contributor
            • Feb 2007
            • 404

            #6
            The images are all .jpg, and together all 30 add up to maybe 90 KB, so the size isn't an issue. So I would want to load the files into a memory stream inside the foreach loop then use that memory stream inside my new Bitmap() declaration instead of using the filename? How do I close the file once I've gotten it loaded into a memory stream? This might actually be the answer to my problem, I'm already downloading the file from the internet, maybe I'll just use the memory stream I get from the downloaded file as my new bitmap instead of writing it to a file...

            Comment

            • Plater
              Recognized Expert Expert
              • Apr 2007
              • 7872

              #7
              Yup:
              new Bitmap("./thumbs/" + filename + "-Thumbnail.jpg") ;
              As long as that image object is alive, that file is in use.

              Comment

              • GaryTexmo
                Recognized Expert Top Contributor
                • Jul 2009
                • 1501

                #8
                For the purposes of this discussion, I'll submit some code I wrote a while back exploring exactly this issue.



                There's a switch statement in there with the code for how I'm loading the image. Basically, I open the file, read it into a byte array, then close the file. After that I read that memory stream into an image object and go from there. I do not close the memory stream so as to avoid the GDI+ exception.

                Comment

                • HaLo2FrEeEk
                  Contributor
                  • Feb 2007
                  • 404

                  #9
                  Ok, I managed to make the downloadThumb method return a memorystream that I then use for my Bitmap() object. Here is my full code:

                  Code:
                  using System;
                  using System.IO;
                  using System.Net;
                  using System.Xml;
                  using System.Drawing;
                  using System.Windows.Forms;
                  
                  namespace WindowsFormsApplication1
                  {
                      public partial class Form1 : Form
                      {
                          string screenshotXML;
                  
                          public Form1()
                          {
                              InitializeComponent();
                          }
                  
                          public MemoryStream downloadThumb(object Url)
                          {
                              MemoryStream thumbMem;
                              string url = Url.ToString();
                              Uri parts = new Uri(url);
                              string query = parts.Query;
                              HttpWebRequest thumbreq = (HttpWebRequest)WebRequest.Create(url);
                              HttpWebResponse thumbres = (HttpWebResponse)thumbreq.GetResponse();
                              BinaryReader thumb = new BinaryReader(thumbres.GetResponseStream());
                              byte[] thumbnail = thumb.ReadBytes((int)thumbres.ContentLength);
                              thumbMem = new MemoryStream(thumbnail);
                              thumb.Close();
                              thumbres.Close();
                              return thumbMem;
                          }
                  
                          public void getXml(string gamertag)
                          {
                              HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.bungie.net/stats/halo3/PlayerScreenshotsRss.ashx?gamertag=" + gamertag);
                              req.Method = "GET";
                              HttpWebResponse response = (HttpWebResponse)req.GetResponse();
                              if (response.Headers["Content-Type"] == "text/html")
                              {
                                  MessageBox.Show("This gamertag is not valid.");
                                  gamertagBox.Focus();
                                  gamertagBox.SelectAll();
                              }
                              else
                              {
                                  dataGridView1.Rows.Clear();
                                  StreamReader responseStream = new StreamReader(response.GetResponseStream());
                                  screenshotXML = responseStream.ReadToEnd();
                                  XmlDocument xdoc = new XmlDocument();
                                  xdoc.LoadXml(screenshotXML);
                                  XmlNodeList items = xdoc.GetElementsByTagName("item");
                                  overallProgress.Maximum = items.Count;
                                  foreach (XmlElement item in items)
                                  {
                                      XmlNodeList child = item.ChildNodes;
                                      string title = child[0].InnerText.ToString();
                                      string date = child[2].InnerText.ToString();
                                      string full = child[6].InnerText.ToString();
                                      string thumb = child[8].InnerText.ToString();
                                      string description = child[9].InnerText.ToString();
                                      DataGridViewRow newRow = new DataGridViewRow();
                                      newRow.Height = 90;
                                      DataGridViewImageCell thumbCell = new DataGridViewImageCell();
                                      DataGridViewCheckBoxCell downloadCell = new DataGridViewCheckBoxCell();
                                      Bitmap thumbImg = new Bitmap(downloadThumb(thumb));
                                      thumbCell.Value = thumbImg;
                                      downloadCell.Value = true;
                                      newRow.Cells.Add(downloadCell);
                                      newRow.Cells.Add(thumbCell);
                                      dataGridView1.Rows.Add(newRow);
                                      downloadCell.Selected = true;
                                      overallProgress.PerformStep();
                                  }
                              }
                              dataGridView1.ClearSelection();
                              response.Close();
                          }
                  
                          private void getXML_Click(object sender, EventArgs e)
                          {
                              
                              string gamertag = gamertagBox.Text;
                              if (gamertag != "")
                              {
                                  getXml(gamertag);
                              }
                              else
                              {
                                  MessageBox.Show("You must enter a gamertag.");
                                  gamertagBox.Focus();
                              }
                          }
                      }
                  }
                  See how on line 67 I'm using the downloadThumb(t humb) method inside the new Bitmap(). There must be a better way to do this...this seems so bloated.

                  And another thing, I liked how when I was using threading to run the downloadThumb thread each time a row was added to the dataGridView it automatically showed up, instead of waiting until the foreach loop finishes THEN showing the completely updated dataGridView box. Is there any way to do what I want here, with the downloadThumb method returning a memorystream, while running downloadThumb inside a thread?

                  Comment

                  • Plater
                    Recognized Expert Expert
                    • Apr 2007
                    • 7872

                    #10
                    Use the .Clone() function on the image.
                    [code=c#]
                    Bitmap tempbm=new Bitmap("./thumbs/" + filename + "-Thumbnail.jpg") ;
                    thumbCell.Value =tempbm.Clone() ;
                    tempbm.Dispose( );
                    [/code]
                    No muss, no fuss

                    Comment

                    • HaLo2FrEeEk
                      Contributor
                      • Feb 2007
                      • 404

                      #11
                      Is there a way to run the downloadThumb method via a thread? I've found that the amount of memory used by the program for actually downloading the thumbnail files to a folder is more than he amount that I use when I just use a MemoryStream, so I'd like to stick with that, but how can I do a cross-thread access to the memory stream variable?

                      Comment

                      • GaryTexmo
                        Recognized Expert Top Contributor
                        • Jul 2009
                        • 1501

                        #12
                        Could you start a thread that will download an image, then use a callback to return the image object which will then be put into wherever you want it to go? I don't know much about threading since I haven't used it much, but I'm pretty sure you can create a thread to do the work of generating the image itself, and then set up a callback to do something with that image when the thread is done.

                        Comment

                        • HaLo2FrEeEk
                          Contributor
                          • Feb 2007
                          • 404

                          #13
                          I worded my message wrong. I've already got a method that will download the file from the supplied URL to either a file or a MemoryStream, the issue I'm having is that you can't share data between threads (at least I don't know a way to do it) so I need to get the MemoryStream (which is actually my preferred method over downloading to a file) from the method in one thread back into the main thread. Can I do that?

                          Comment

                          • tlhintoq
                            Recognized Expert Specialist
                            • Mar 2008
                            • 3532

                            #14
                            Try to not pass the MemoryStream between threads. Instead have the downloading method raise an event when it is done, with the completed downloaded image as the argument of the event.

                            Have a method as the event handler for the "ImageDownloade d" event doing whatever you need it to do: SaveToHDD, ShowImage, AddToDataBase.

                            This should help separate the activities a little bit. Later when you find your next best way to download it isn't tightly integrated with the program flow; your new download method will still raise the event and the rest of the program will not know the difference.

                            In theory you could have several different methods that obtain an image in different ways yet all raise the same event: FTP, HTTP, FileCopy, FileSystemWatch er... anything that can recognize or obtain a new image can raise the event with the rest of the program being blissfully ignorant of the *how* and just worry about its own part after the fact.

                            Comment

                            • HaLo2FrEeEk
                              Contributor
                              • Feb 2007
                              • 404

                              #15
                              I'm not sure I understand how to do this. I understand what you're saying, and what I need to do, I just don't know how. As much as I hate asking for help in this way, can you give me an example, in code, of what I should do.

                              In truth I've only been doing this for about 2 weeks total, so I'm still very new to C#.

                              Comment

                              Working...