4

我正在通过 IIS Web 应用程序合并 XPS 文件,但在合并过程中,它使文档参考文件句柄保持打开状态。在 Windows WPF 或控制台应用程序中,我不会担心句柄,因为它们会在应用程序关闭后被释放。回收应用程序池也会有效地关闭句柄。但是,由于缓存和性能,我不能仅仅为了关闭文件句柄而回收应用程序池。

合并完成后有没有办法关闭这些文件句柄?我尝试了几种不同的合并方法,但以下代码给了我最好的结果:

private DocumentPaginator CreateMergedDocument(IEnumerable<string> xpsFiles) {   
  using ( MemoryStream mergedStream = new MemoryStream() ) {
    using ( var pkg = Package.Open( mergedStream, FileMode.Create, FileAccess.ReadWrite ) ) {
      var pack = "pack://merged.xps";
      var uri = new Uri( pack, UriKind.Absolute );
      PackageStore.AddPackage( uri, pkg );
      using ( XpsDocument mergedDocument = 
          new XpsDocument( pkg, CompressionOption.Maximum, pack ) ) {
        FixedDocumentSequence seqNew = new FixedDocumentSequence();

        foreach ( string sourceDocument in xpsFiles ) {
          using ( XpsDocument xpsOld = 
              new XpsDocument( sourceDocument, FileAccess.Read ) ) {
            FixedDocumentSequence seqOld = xpsOld.GetFixedDocumentSequence();

            foreach ( DocumentReference dr in seqOld.References ) {
              DocumentReference newDocumentReference = new DocumentReference();
              newDocumentReference.Source = dr.Source;
              ( newDocumentReference as IUriContext ).BaseUri = 
              ( dr as IUriContext ).BaseUri;
              seqNew.References.Add( newDocumentReference );
            }
          }
        }

        XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter( mergedDocument );
        xpsWriter.Write( seqNew );

        PackageStore.RemovePackage( uri );

        return seqNew.DocumentPaginator;
      }
    }
  }
}

我追踪了打开xpsWriter.Write(seqNew)代码行的文件句柄。这是预期的,因为它需要加载文件以复制到新的FixedDocumentSequence. XpsDocumentWriter但是,如果 an是一次性的并清理了它的非托管资源/文件句柄,那就太好了。

在我的研究过程中,我尝试了其他一些方法。我尝试过的一种方法是将每个页面可视化写入SerializerWriterCollator. 我研究过的另一种方法是处理 FixedPage 内容并将 URI 更新为复制的图像和字体流,并将原始 XAML 写回到XmlWriter. 两者都没有很好地工作。使用页面视觉效果和SerializerWriterCollator,我在合并的 XPS 上获得了不正确的页面大小,并且它正在切断内容。使用原始XmlWriter方法时,合并的 XPS 中的一些图像会混杂在一起,并且会错误地显示一些页面内容。

4

1 回答 1

0

经过更多研究,我发现用于在 STA 线程中运行我的函数的原始代码没有正确释放文件句柄和非托管资源。我们有一个自定义线程类,该类具有调用操作和在线程上运行连接的自定义实现。操作方法完成后线程未正确退出。为了解决这个问题,我将我的代码包装在一个类似于这样的块中:

private static readonly TaskScheduler _staScheduler = new StaTaskScheduler( 1 );

[Route( "merge" )]
[HttpGet]
public async Task<HttpResponseMessage> MergeXps() {
  var paginator = await Task<DocumentPaginator>.Factory.StartNew(
    () =>
      {
        var xpsFiles = Directory.GetFiles( "C:\\Xps", "*.xps" );
        var documentPaginator = CreateMergedDocument( xpsFiles );

        return documentPaginator;
      },
    CancellationToken.None,
    TaskCreationOptions.None,
    _staScheduler );

  var response = new HttpResponseMessage();
  response.Content = new StringContent( $"Merged Document Page Count: {paginator.PageCount}" );
  response.StatusCode = HttpStatusCode.OK;
  return response;
}
于 2016-02-09T20:29:19.303 回答