5

I succesfully create the ISO image, but I get 'file in use' IO errors trying to delete files in the rootFolderPath after returning from calling this Create method. Am I missing a Marshal.ReleaseComObject call?

/// <summary>
/// Create iso image from rootFolderPath and write to isoImageFilePath. Does not include the actual rootFolder itself
/// </summary>
public void Create()
{
    IFileSystemImage ifsi = new MsftFileSystemImage();
    try
    {
        ifsi.ChooseImageDefaultsForMediaType(IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK);
        ifsi.FileSystemsToCreate =
                FsiFileSystems.FsiFileSystemJoliet | FsiFileSystems.FsiFileSystemISO9660;
        ifsi.VolumeName = this.volumeName;
        ifsi.Root.AddTree(rootFolderPath, false);//use a valid folder
        //this will implement the Write method for the formatter
        IStream imagestream = ifsi.CreateResultImage().ImageStream;
        if (imagestream != null)
        {
            System.Runtime.InteropServices.ComTypes.STATSTG stat;
            imagestream.Stat(out stat, 0x01);
            IStream newStream;
            if (0 == SHCreateStreamOnFile(isoImageFilepath, 0x00001001, out newStream) && newStream != null)
            {
                IntPtr inBytes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(long)));
                IntPtr outBytes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(long)));
                try
                {
                    imagestream.CopyTo(newStream, stat.cbSize, inBytes, outBytes);
                    Marshal.ReleaseComObject(imagestream);
                    imagestream = null;
                    newStream.Commit(0);
                }
                finally
                {
                    Marshal.ReleaseComObject(newStream);
                    Marshal.FreeHGlobal(inBytes);
                    Marshal.FreeHGlobal(outBytes);
                    if (imagestream != null)
                        Marshal.ReleaseComObject(imagestream);
                }
            }
        }
    }
    finally
    {
        Marshal.ReleaseComObject(ifsi); 
    }
}
Herohtar
  • 5,347
  • 4
  • 31
  • 41
cwchilders
  • 51
  • 1
  • 3

2 Answers2

7

There is certainly a problem with locks on large files. Digging around on the net produces the following bits of the puzzle:

So, with all of this digested, I’ve got a solution: after Write, iterate the file system image root and release any stream data.

There are a couple of issues for a multisession disc – as the existing files are imported (by ImportFileSystem) they all are checked for a lock and this may take some time, and a COMException is thrown for each that was not written in the current session. With a bit of effort I’m sure the difference between the file system before and after AddTree can be cached, and only those files checked.

Anyhow... After the call to Write, we call ReleaseIFsiItems...

       {
          // Write...

          // Call to release any locks
          ReleaseIFsiItems(fileSystemImage.Root);

           // Complete tidy up...
          Marshal.FinalReleaseComObject(fileSystem);
          Marshal.FinalReleaseComObject(fileSystemImageResult);
        }

 private static void ReleaseIFsiItems(IFsiDirectoryItem rootItem)
  {
     if (rootItem == null)
     {
        return;
     }

     var enm = rootItem.GetEnumerator();
     while (enm.MoveNext())
     {
        var currentItem = enm.Current as IFsiItem;
        var fsiFileItem = currentItem as IFsiFileItem;
        if (fsiFileItem != null)
        {
           try
           {
              var stream = fsiFileItem.Data;
              var iUnknownForObject = Marshal.GetIUnknownForObject(stream);
              // Get a reference - things go badly wrong if we release a 0 ref count stream!
              var i = Marshal.AddRef(iUnknownForObject);
              // Release all references
              while (i > 0)
              {
                 i = Marshal.Release(iUnknownForObject);
              }
              Marshal.FinalReleaseComObject(stream);
           }
           catch (COMException)
           {
              // Thrown when accessing fsiFileItem.Data
           }
        }
        else
        {
           ReleaseIFsiItems(currentItem as IFsiDirectoryItem);
        }
     }
  }

I hope this works for you!

PaulS
  • 699
  • 7
  • 13
  • This worked for me when files were locked and I needed to release them after burning to a DVD in c#. – ggb667 Sep 10 '15 at 13:38
  • It is definitely associated with file size, but it isn't about "large" files -- the exact cutoff is **128 KB**; anything equal to or greater than that size will not have its handle released. The file system seems to automatically do something different with files below that size; in my project, I have a `ManagedIStream` that I use to add the files instead of `SHCreateStreamOnFile`, and `fsiFileItem.Data` will return that `ManagedIStream` object for anything above the cutoff, but below that, it returns some COM object that implements `IStream`. – Herohtar Dec 30 '19 at 16:29
-1

There is a hotfix for Windows 7 solving the problem of a handle leak in IMAPIv2. https://support.microsoft.com/en-us/kb/2878035

This problem occurs because of a handle leak in IMAPIv2 when a multisession writing session occurs for DVD-RW media.

kot-da-vinci
  • 1,152
  • 16
  • 30
  • The problem mentioned in the question happens when writing an ISO, not when burning a DVD. Also, the problem still exists in Windows 10, so that hotfix is not the answer. – Herohtar Dec 29 '19 at 07:23
  • The problem I mentioned happens while building any disc image and is actual for only Windows7 SP1 without this update. This update was included in Windows7 SP2. – kot-da-vinci Dec 30 '19 at 16:01
  • The hotfix is specifically for a problem that occurrs on Windows 7 when you *"burn data in multisession mode to a DVD-RW"*, and as stated on the hotfix page, *"this hotfix is intended to correct only the problem that is described in this article"*. The original question is about building an ISO image, and is not specific to Windows 7 (the problem exists in other versions of the OS). Therefore, the question is not about the problem you mentioned, and the hotfix does not solve the problem. – Herohtar Dec 30 '19 at 16:21