c# - 合并 XPS 文件将打开的文件句柄留给文档引用

标签 c# merge xps xpsdocument

我正在通过 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 是一次性的并清理其非托管资源/文件句柄,那就太好了。

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

最佳答案

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

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

关于c# - 合并 XPS 文件将打开的文件句柄留给文档引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35229012/

相关文章:

c# - SQL 主键生成

svn - TortoiseSVN - 合并 - 新文件夹导致树冲突

perl - 如何在 Perl 中组合哈希值?

git - 如何将更改 merge 到单个文件,而不是 merge 提交?

vb.net - 打印到 XPS,无需“另存为”对话框

c# - C#中的Excel进程

c# - 在 MonoDevelop 中更改默认列宽/显示列标尺?

c# - PrintDialog/XPS Document Writer 中忽略的纸张大小

printing - 如何在 Windows 8 中打印 xps 文件

c# - 异步 TCP 服务器 - 消息框架建议