IMAPI2 MsftFileSystemImage in .NET not releasing files when creating ISO

只谈情不闲聊 提交于 2019-12-01 21:46:55

问题


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?

thanks,

cw

/// <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); 
    }
}

回答1:


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

  • http://social.msdn.microsoft.com/Forums/en-US/windowsopticalplatform/thread/5ae4a173-ccb2-4c10-8fd5-c6e59a9c0ac9

    The first answer from MS acknowledges a problem with large files. (NB setting “Staging = false” does not work for me.)

  • http://www.codeproject.com/KB/miscctrl/ISOImage.aspx?msg=2532334

    Uses a stream to add files to be burned.

  • http://social.msdn.microsoft.com/Forums/en-AU/windowsopticalplatform/thread/124017ea-79c5-45e2-b62e-589b3b4505af

    Discusses an AddRef/Release imbalance.

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!




回答2:


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.



来源:https://stackoverflow.com/questions/7897119/imapi2-msftfilesystemimage-in-net-not-releasing-files-when-creating-iso

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!