2
Vote

Resource.SaveTo method sometimes temporarily still keeps file in use even after it returns

description

Hi.

Resource.SaveTo method sometimes temporarily still keeps file in use even after it returns.

I am experiencing this behavior in different solutions.
One of them being in dotNetInstaller (see http://dotnetinstaller.codeplex.com/Thread/View.aspx?ThreadId=83032).
The other solution makes a call to EndUpdateResource as well and have noticed that sometimes the file is still in use even after EndUpdateResource returns.

As noted in the dotNetInstaller post, I am thinking that the managed garbage collector has not yet freed open handles\resources from making those windows api calls through pinvoke.

I am thinking that we may need to explicitly call GC.Collect() or wait until the file is no longer in use.

In my code, if I loop while checking if the file is not in use and sleep for 1 second, with a maximum of 5 tries, things work as expected.

This is just a work-around for the time being and is not the best solution I know.

I am not sure how a unit test can be written that can be used to test the unexpected behavior and correct functionality consistently and accurately.

Here is a code snippet for FileInUse mostly borrowed from http://www.dotnetscraps.com/dotnetscraps/post/FileInUse.aspx.
    public static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                //If required we can check for read/write by using fs.CanRead or fs.CanWrite
            }
            return false;
        }
        catch (IOException)
        {
            return true;
        }
    }
Thank you.

comments

dblock wrote Feb 10, 2010 at 2:22 PM

Can you demonstrate this in ResourceLib. While I acknowledge that there's a problem in DNI with writing resources, I think this is speculation as far as ResourceLib is concerned. I re-read the code and I don't see any handles that would require GC to kick in to cleanup.

icnocop wrote Jun 25, 2010 at 9:44 PM

Hi.

I received the following exception when trying to call the SaveTo method:
System.ComponentModel.Win32Exception: The system cannot open the device or file specified
 at Vestris.ResourceLib.Resource.SaveTo(String filename, ResourceId type, ResourceId name, UInt16 lang, Byte[] data)
 at Vestris.ResourceLib.Resource.SaveTo(String filename, ResourceId type, ResourceId name, UInt16 langid)
 at Vestris.ResourceLib.Resource.SaveTo(String filename)
Although it works fine 99% of the time.

This exception was also previously briefly mentioned\reported here:
http://dotnetinstaller.codeplex.com/Thread/View.aspx?ThreadId=207797

I am (also) running on Windows 7 Ultimate x64.

icnocop wrote Nov 1, 2010 at 11:03 PM

This may be due to the "Windows Search" \ "Indexing Service" windows service.

icnocop wrote Nov 5, 2010 at 11:04 PM

Once I disabled the "Windows Search" \ "Indexing Service" windows service, I ran into this issue less often.

I am wondering if there is a way to easily find out the process id that has an open handle to the file like SysInternal's handle.exe that can be incorporated in the exception message.

MarcioAH wrote Mar 21, 2011 at 5:38 PM

Pretty simple EXE files are usually quite monitored by anti-virus, virus, memory managers, in order a bunch of crap, so to solve the problem just rename the file extension for any one (eg. zTemp) and when everything is complete rename it again to. exe.

icnocop wrote Apr 26, 2011 at 7:24 PM

I also see this error if I have another machine (Windows Server 2003 Enterprise x86) with windows explorer open to the same output directory (\win7-x64\c$\MyProject\output) as the dotNetInstaller output directory on the development machine (C:\MyProject\output). I'm wondering if we can add some simple retry logic. (Not sure if MarcioAH's suggestion will work all the time)