0

I am writing a small program to automatically change the desktop wallpaper, from an online image gallery (imgur). Currently I have this code:

namespace Imgur_Wallpapers
{
    public partial class Form1 : Form
    {
        //IMGUR API = 3f1e4339e7bad35ff801bf76e369ae17

        private int Count = 1;

        public Form1()
        {
            InitializeComponent();
            timer1.Enabled = false;
            if (!File.Exists("image_black"))
            {
                Bitmap black = new Bitmap(10, 10);
                black.Save("transitionpaper.bmp");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            DownloadFromImgur download = new DownloadFromImgur("gjZEc", Path.GetDirectoryName(Application.StartupPath));
            Wallpaper.Set(new Uri(Application.StartupPath + "/Image.bmp"), Wallpaper.Style.Centered);
            Count++;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            DownloadFromImgur download = new DownloadFromImgur(imgurAlbumIdBox.Text, Path.GetDirectoryName(Application.StartupPath));

            Wallpaper.Set(new Uri(Application.StartupPath + "/Image.bmp"), Wallpaper.Style.Centered);
            Count++;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            timer1.Interval = (int)whenToRefreshBox.Value;
            timer1.Enabled = true;
        }
    }

        private string url;
        public List<string> hash;
        private Random random;
        private string albumID;


        public DownloadFromImgur(string albumID, string folderToSaveTo)
        {
            try
            {
                this.albumID = albumID;
                hash = new List<string>();
                random = new Random();
                GetWebSite();
                Wallpaper.Set(new Uri(Application.StartupPath + "/transitionpaper.bmp"), Wallpaper.Style.Centered);
                WebClient client = new WebClient();
                File.Delete(Application.StartupPath + "/Image.bmp");
                client.DownloadFile(url, Application.StartupPath +  "/Image.bmp");
                client.Dispose();
            }
            catch (WebException ex)
            {

            }
        }

        private void GetWebSite()
        {
            var doc = XDocument.Load("http://api.imgur.com/2/album/" + albumID);

            hash.Clear();
            RecursiveWrite(doc.Root);

            url = hash[random.Next(hash.Count - 1)];
        }

        private void RecursiveWrite(XElement node)
        {
            foreach (var attribute  in node.Value)
            {
                if (node.Name == "original")
                {
                    hash.Add(node.Value);
                    break;
                }
            }

            foreach (var child in node.Elements())
            {
                RecursiveWrite(child);
            }
        }
    }

    public sealed class Wallpaper
    {
        Wallpaper() { }

        const int SPI_SETDESKWALLPAPER = 20;
        const int SPIF_UPDATEINIFILE = 0x01;
        const int SPIF_SENDWININICHANGE = 0x02;

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);

        public enum Style : int
        {
            Tiled,
            Centered,
            Stretched
        }

        public static void Set(Uri uri, Style style)
        {
            System.IO.Stream s = new System.Net.WebClient().OpenRead(uri.ToString());

            System.Drawing.Image img = System.Drawing.Image.FromStream(s);
            string tempPath = Path.Combine(Path.GetTempPath(), "wallpaper.bmp");
            img.Save(tempPath, System.Drawing.Imaging.ImageFormat.Bmp);

            RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
            if (style == Style.Stretched)
            {
                key.SetValue(@"WallpaperStyle", 2.ToString());
                key.SetValue(@"TileWallpaper", 0.ToString());
            }

            if (style == Style.Centered)
            {
                key.SetValue(@"WallpaperStyle", 1.ToString());
                key.SetValue(@"TileWallpaper", 0.ToString());
            }

            if (style == Style.Tiled)
            {
                key.SetValue(@"WallpaperStyle", 1.ToString());
                key.SetValue(@"TileWallpaper", 1.ToString());
            }

            SystemParametersInfo(SPI_SETDESKWALLPAPER,
                0,
                tempPath,
                SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
        }
    }
}

It works fine when on the first run, but returns an IOException on the second run. Apparently the file is still being used somewhere. How can I find where it's still used? Here's the exception message:

System.IO.IOException was unhandled
Message=The process cannot access the file 'C:\Users\David\documents\visual studio 2010\Projects\Imgur Wallpapers\Imgur Wallpapers\bin\Debug\Image.bmp' because it is being used by another process.
Source=mscorlib
StackTrace: at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.File.Delete(String path)

Bridge
  • 29,818
  • 9
  • 60
  • 82
requinard
  • 921
  • 1
  • 11
  • 24
  • You also don't use temp directories, but the running folder. Huge problem! – Cole Tobin May 11 '12 at 15:59
  • @ColeJohnson Am I misunderstanding your comment, or were you insulting the OP for using Windows Forms? – Andrew Barber May 11 '12 at 15:59
  • 3
    @ColeJohnson That's one opinion. It's completely irrelevant to the question, though. – Andrew Barber May 11 '12 at 16:02
  • @FelicePollano he shouldn't have to do that. Also if the file is still being used, he won't be able to delete it – Cole Tobin May 11 '12 at 16:07
  • 1
    @ColeJohnson - That is pure opinion. I have a bigger problem that I have to go to a third-party site instead of view code on a website I know is safe. – Security Hound May 11 '12 at 16:07
  • @ColeJohnson I guess the file is in use because it is the current wallpapaer, but this would not explain why the first time it works, so you are probably correct. – Felice Pollano May 11 '12 at 16:09
  • @FelicePollano when windows sets a wallpaper, it saves It to a directory in the AppData, hence why you can set an image as the wallpaper and then delete it. – Cole Tobin May 11 '12 at 16:10
  • -1 to question: no code in the question. Please consider editing this question to include relevant code, for future questions please make sure to include code directly into the question, try to make code small enough so there is no scrolling required. – Alexei Levenkov May 11 '12 at 16:11
  • 1
    @ColeJohnson it's fine I removed the comment – Felice Pollano May 11 '12 at 16:13
  • I'm sorry I'm not using WPF. I'm still new to programming :D – requinard May 11 '12 at 16:25
  • @AlexeiLevenkov I put the code on pastie, seeing as it is 152 lines. – requinard May 11 '12 at 16:28
  • The problem with not pasting code is that external links can/will die and after that the question is no longer useful on this site. If you think your question is "too localized" to be useful for more than a day maybe you should see if there is broader one that should be asked instead to be useful for longer time. See http://meta.stackexchange.com/questions/80978/questions-linking-to-external-web-sites-instead-of-showing-code for discussion (covers HTML, but relevant for all languages) and this http://meta.stackexchange.com/questions/73918/asking-a-question-with-lots-of-source-code. – Alexei Levenkov May 11 '12 at 16:43

2 Answers2

1

You don't close the download stream when it completes. If you don't, the stream is still hooked to the file. Try using .Close() and .Dispose()

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
  • It's still throwing the same exception. – requinard May 11 '12 at 16:26
  • @ColeJohnson, for CLR classes there is no need to use both Close and Dispose as you've suggested - for all relevant cases Close and calls Dispose do exactly the same thing. The reason Close is provided on Stream-based classes is to support natural Open-write/read-Close way of writing code, but `using(var s=new Stream...){}` is equivalent and often shorter/more C#-style code. – Alexei Levenkov May 11 '12 at 16:56
  • using (WebClient client = new WebClient()) { File.Delete("Image.bmp"); client.DownloadFile(url, "Image.bmp"); } – requinard May 11 '12 at 16:57
  • @ColeJohnson Disposing of a stream should close it too. See [this.](http://stackoverflow.com/questions/911408/does-stream-dispose-always-call-stream-close-and-stream-flush) – Bridge May 11 '12 at 19:10
  • @Bridge Disposing shouldn't be necessary when I am using a using block – requinard May 11 '12 at 20:06
  • @user1382827 I know - I was pointing out that despite what Cole said, a using block would both close and dispose of any resources for you. – Bridge May 11 '12 at 20:20
1

I always found this tool very helpful in figuring out what was locking which file.

I haven't used in a while but it is worth a shot.

Eran
  • 387,369
  • 54
  • 702
  • 768
Osama Javed
  • 1,432
  • 1
  • 16
  • 21