Link to home
Start Free TrialLog in
Avatar of codefinger
codefingerFlag for United States of America

asked on

avoiding "file in use by another process" during file deletion

I have two web services that allow a multipage tiff file to be uploaded one page at a time.
StoreTiffPages(identifier, pagenumber) and
StoreTiffFinal(identifier)

StoreTiffPages is called for each page in the file and the pages received are stored in a temporary directory on the server until StoreTiffFinal is called

StoreTiffFinal assembles the files into a single multipage tiff file and moves it to its final location on the server.  Then it is supposed to delete the temporary files, however, what frequently happens instead is an error "the process cannot access the file xxxx because it is being used by another process."

Below is the code I use to assemble the multipage tiff.  What do I have to do to free up the temporary files for deletion?

 Function ToMultiPageTiff(ByVal files_list() As String, ByVal ls_destination_filename As String, ByRef as_errmsg As String) As Boolean
        Try

            Dim enc As Encoder


            'get codec info
            enc = Encoder.SaveFlag
            Dim info As System.Drawing.Imaging.ImageCodecInfo
            info = Nothing

            Dim ice As System.Drawing.Imaging.ImageCodecInfo
            For Each ice In System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders
                If ice.MimeType = "image/tiff" Then
                    info = ice
                End If
            Next


            Dim masterimg As Bitmap = Nothing

            masterimg = CType(Image.FromFile(files_list(0)), Bitmap)

            Dim ep As System.Drawing.Imaging.EncoderParameters

            ep = New System.Drawing.Imaging.EncoderParameters(2)

            ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.MultiFrame, Long))
            ep.Param(1) = New EncoderParameter(enc.Compression, CType(EncoderValue.CompressionLZW, Long))
            'Save the first image.  This is the image to which all subsequent images will be added.
            masterimg.Save(ls_destination_filename, info, ep)

            'Add the remaining images  (these next two lines are probably unneccessary)
            ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.FrameDimensionPage, Long))
            ep.Param(1) = New EncoderParameter(enc.Compression, CType(EncoderValue.CompressionLZW, Long))


            Dim li_cntr As Integer
            For li_cntr = 1 To files_list.Length - 1
                masterimg.SaveAdd(Image.FromFile(files_list(li_cntr)), ep)
            Next

            ep.Param(0) = New EncoderParameter(enc, CType(EncoderValue.Flush, Long))
            masterimg.SaveAdd(ep)

            Return True



        Catch ex As Exception
            as_errmsg = ex.Message
            Return False


        End Try
    End Function


Thanks in advance for your help.
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Remember to close the file.

Bob
Avatar of codefinger

ASKER

Syntax please?   Remember, all I have to work with during this process is
strings.  

Image.FromFile(ls_filename)

Thanks.
masterimg.Dispose()

Bob
masterimg.Dispose() did not prevent the problem.

Note, the file complained about (xxxx) is the first of the temporary files.
(Sorry, should have specified that sooner.)

Should I also invoke garbage collection and/or waitforpendingfinalizers?

Thanks.
Where do you create the temporary files?  It should be as simple as File.Close(), but it's hard to tell from what you've shown.   Since garbage collection in non-deterministic (you can't tell when it will happen next), GC.Collect() will force it to happen when you want.  Just remember that forcing garbage collection can be very detrimental to your application depending on how it is used.

Bob
All code for StoreTiffPages and StoreTiffFinal is shown below.  Note xfilestream.close() after each temporary file is created.

  Public Function StoreTiffPages(ByVal iBinStream As String, _
                               ByVal iDirectory As String, _
                               ByVal iName As String, _
                               ByVal iPageNum As String, _
                               ByVal iOptions As String _
                               ) As doStoreTiffPages.Result

        Dim xResult As New doStoreTiffPages.Result()

        '>>> Do work here and set results...


        Dim lfilelen As Long
        Dim fs_new_file_name As String
        Dim ls_dirname As String
        Dim fs_64 As String

        Dim ls_splitname() As String
        Dim upperbound As Integer
        Dim ls_tempfilepath As String
        Dim fs_temp_file_name As String


        Dim ls_current_filename As String
        Dim ls_last_version_no As String
        Dim lb_file_exists As Boolean
        lb_file_exists = False
        Dim ls_ext As String
        Dim ls_arrayfilenames As String

        ls_splitname = Split(iName, ".")

        upperbound = UBound(ls_splitname)

        ls_ext = ls_splitname(upperbound)
        ls_dirname = System.Configuration.ConfigurationSettings.AppSettings("MAINDIR") + iDirectory
        ls_tempfilepath = System.Configuration.ConfigurationSettings.AppSettings("MAINDIR") + iDirectory + "\TEMPFILES"

        fs_temp_file_name = ls_tempfilepath + "\" + "_" + CStr(iPageNum) + "_" + iName

        Try

            Dim xDoc As New MSXML2.DOMDocument()
            Dim xnode As MSXML2.IXMLDOMNode
            Dim xbase64String As String
            iBinStream = iBinStream.Replace("dt:", "")
            xDoc.loadXML(iBinStream)
            xnode = xDoc.selectSingleNode("DOCUMENT/FILE")
            xbase64String = xnode.text
            xbase64String = xbase64String.Replace(" ", "+")
            xDoc = Nothing
            xnode = Nothing

            Dim xByteArray() As Byte
            xByteArray = System.Convert.FromBase64String(xbase64String)

            If System.IO.File.Exists(fs_temp_file_name) Then
                System.IO.File.Delete(fs_temp_file_name)
            End If

            Dim xFileStream As New _
            System.IO.FileStream(fs_temp_file_name, System.IO.FileMode.Create, FileAccess.ReadWrite)

            Dim xBinWrite As System.IO.BinaryWriter = New System.IO.BinaryWriter(xFileStream)
            xBinWrite.Write(xByteArray, 0, xByteArray.Length)
            xBinWrite.Flush()
            xBinWrite.Close()



             xFileStream.Close()


            xResult.Stored = True
            xResult.Message = "Page stored to temporary directory.  Expecting additional pages or notice of completion."
            xResult.Exception.Code = 0
            Return xResult


        Catch ex As Exception

            xResult.Message = "StoreTiffPages failed:   " + ex.Message
            xResult.Exception.Msg = ex.Message
            xResult.Exception.Code = -1

            Return xResult
        End Try


    End Function


    Function StoreTiffFinal(ByVal iName As String, ByVal iDirectory As String, ByVal ioverwrite As String, ByVal ioptions As String)

        Dim xResult As New doStoreTiffFinal.Result()
        Try

            Dim ls_tempfilepath As String
            Dim ls_dirname As String
            ls_dirname = System.Configuration.ConfigurationSettings.AppSettings("MAINDIR") + iDirectory

            ls_tempfilepath = ls_dirname + "\TEMPFILES"

            Dim tiffpagesarray() As Image
            tiffpagesarray = MakeTiffArray(iName)

            Dim ls_filelist As String()

            ls_filelist = MakeSortedArrayofFileNames(iName)


            Dim ls_destfilename As String
            ls_destfilename = ls_tempfilepath + "\" + iName

            Dim ls_errmsg As String


           

            If Not ToMultiPageTiff(ls_filelist, ls_destfilename, ls_errmsg) Then
                xResult.Exception.Code = -1
                xResult.Exception.Msg = ls_errmsg
                xResult.Message = ls_errmsg
                Return xResult
            End If


            Dim fs_new_file_name As String
            Dim ls_last_version_no As String
            Dim lb_file_exists As Boolean
            Dim ls_current_filename As String
            lb_file_exists = True
            Dim ls_ext As String
            ls_ext = "tif"
            'now move this final temporary file to permanent storage...
            ls_last_version_no = GetLastVersionNo(iDirectory, iName)

            If ls_last_version_no = "" Then
                lb_file_exists = False
            End If


            Dim ls_splitfile() As String

            If lb_file_exists Then
                ls_splitfile = Split(iName, ".")
                ls_current_filename = ls_splitfile(0) + "V" + ls_last_version_no + "." + ls_ext
                If UCase(ioverwrite) = "TRUE" Then
                    If System.IO.File.Exists(ls_dirname + "\" + ls_current_filename) Then
                        System.IO.File.Delete(ls_dirname + "\" + ls_current_filename)
                    End If
                End If
            End If


            fs_new_file_name = ls_dirname + "\" + iName
            fs_new_file_name = GetNewFileName(fs_new_file_name)
            fs_new_file_name = UCase(fs_new_file_name)

            System.IO.File.Move(ls_destfilename, fs_new_file_name)

            'Delete the temporary files
            Dim splitfilename() As String
            Dim leftside As String
            Dim rightside As String
            Dim ls_pattern As String
            Dim filelist() As String
            Dim li_lastfile As Integer
            Dim ls_lastfile As String

            ls_dirname = System.Configuration.ConfigurationSettings.AppSettings("MAINDIR")
           
            ls_dirname = ls_dirname + iDirectory + "\TEMPFILES"
            ls_pattern = "*" + iName
            filelist = System.IO.Directory.GetFiles(ls_dirname, ls_pattern)
            Dim cntr As Integer
            For cntr = 0 To filelist.Length - 1
                Dim fi As FileInfo
                fi = New FileInfo(filelist(cntr))
                If fi.Exists Then
                    fi.Delete()
                End If
                'System.IO.File.Delete(filelist(cntr))
            Next
            xResult.Stored = True
            xResult.Message = "New image has been stored to OCW server."
            xResult.Exception.Code = 0


        Catch ex As Exception
            xResult.Stored = False
            xResult.Message = ex.Message
            xResult.Exception.Code = -1

        End Try

        Return xResult


    End Function
As long as you are doing GC.Collect thousands of times, it should actually help you in what appears to be a timing issue.

Bob
>>As long as you are doing GC.Collect thousands of times, it should actually help you in what appears to be a timing issue.<<

I hope you meant AREN'T doing GC.Collect thousands of times   ;)
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Still no joy.

Added System.GC.Collect right after xstream.close() in the StoreTiffPages function.

Before, I could run the upload process twice in a row if they were run far enough apart (like about 30-45 minutes), but adding System.GC.Collect seems to have made the problem worse.  Now it won't even run once and gives the same error.

I don't think that I am getting a clear picture here, since there aren't any comments and I didn't quite get what you are talking about.

Bob
Nevermind my last comment, it only confused the issue.

Here is some additionial info that may help:

http://www.dotnet247.com/247reference/msgs/13/66280.aspx

A System.Drawing.Image object has a tempting FromFile method that
allows you to pass in a full path to fill the object. There is a
significant downside to this simple method: It locks the image file a
lot longer than you'd ever want, prohibiting you to delete or modify
the source file out side of your app after you opened it (any such
attempts raise a "There has been a sharing violation. The source or
destination file may be in use" error).

Using the FromFile in a WinForm app will lock the file for the
lifetime of the application, even if you let the Image fall out of
scope and, worse still, in ASP.net passing a UNC to FromFile to open
an image located remotely will lock the file indefinitely (even hours
beyond the run of the WebApp, that can't be the Garbage Collector!).

I don't know exactly why this is, but I can tell you a workaround:
Don't use Image.FromFile. Instead, use a Stream object open the file,
pass it to the Image's FromStream method and close the stream when the
Image object is filled.

Dim objStream As System.IO.Stream = System.IO.File.Open([Path To
File], System.IO.FileMode.Open)
Dim MyImage as System.Drawing.Image
MyImage.FromStream(objStream)
objStream.Close()


I have tried using fromstream as the article suggests, but the problem still persists.

Next step is to involve bytearrays.  I'll let you know how it goes.



I think I was applying all the suggested fixes to the wrong function.  I was able to fix the problem by restoring an older version of the code.   Most likely one of your suggestions would have worked if I had applied it correctly, so I am awarding the points to you and giving an A grade.   You were also the only one who even tried to answer the question and I am sure if I had kept asking, you would have kept trying until the problem was solved.   Thank you for your help and your patience!