启动第二个实例时Adobe Reader进程失败

问题描述:

在我们的C#WinForms应用程序中,我们通过Process类生成PDF文件并启动Adobe Reader(或任何默认系统.pdf处理程序)。由于我们的PDF文件可能较大(大约200K),因此我们会处理Exited事件,然后清理临时文件。启动第二个实例时Adobe Reader进程失败

当打开文件然后再次关闭文件时,系统按需要工作。但是,当第二个文件打开时(在关闭Adobe Reader之前),第二个进程立即退出(因为Reader现在正在使用它的MDI权限),并且在我们的Exited处理程序中,我们的File.Delete调用应该失败,因为它被现在加入的Adobe进程锁定。但是,在Reader中,我们改为:

打开此文档时出错。无法找到该文件。

不寻常的是,如果我在删除文件之前放置了一个调试器断点,并允许它尝试(并失败)删除,那么系统的行为与预期相同!

我确信该文件存在并且相当肯定在启动该过程之前关闭了该文件的所有句柄/文件流。

我们用下面的代码启动:

// Open the file for viewing/printing (if the default program supports it) 
var pdfProcess = new Process(); 
pdfProcess.StartInfo.FileName = tempFileName; 
if (pdfProcess.StartInfo.Verbs.Contains("open", StringComparer.InvariantCultureIgnoreCase)) 
{ 
    var verb = pdfProcess.StartInfo.Verbs.First(v => v.Equals("open", StringComparison.InvariantCultureIgnoreCase)); 
    pdfProcess.StartInfo.Verb = verb; 
} 
pdfProcess.StartInfo.Arguments = "/N"; // Specifies a new window will be used! (But not definitely...) 
pdfProcess.SynchronizingObject = this; 
pdfProcess.EnableRaisingEvents = true; 
pdfProcess.Exited += new EventHandler(pdfProcess_Exited); 

_pdfProcessDictionary.Add(pdfProcess, tempFileName); 

pdfProcess.Start(); 

注:我们使用的是_pdfProcessDictionary,让他们留在范围,使已退出事件可以成功地提升到存储过程对象的引用。

我们清理/退出事件是:

void pdfProcess_Exited(object sender, EventArgs e) 
{ 
    Debug.Assert(!InvokeRequired); 
    var p = sender as Process; 
    try 
    { 
     if (_pdfProcessDictionary.ContainsKey(p)) 
     { 
      var tempFileName = _pdfProcessDictionary[p]; 
      if (File.Exists(tempFileName)) // How else can I check if I can delete it!!?? 
      { 
       // NOTE: Will fail if the Adobe Reader application instance has been re-used! 
       File.Delete(tempFileName); 
       _pdfProcessDictionary.Remove(p); 
      } 

      CleanOtherFiles(); // This function will clean up files for any other previously exited processes in our dictionary 
     } 
    } 
    catch (IOException ex) 
    { 
     // Just swallow it up, we will deal with trying to delete it at another point 
    } 
} 

可能的解决方案:

  • 检测该文件仍然在另一个进程中打开
  • 检测到第二个进程还没有真正得到完全退出,并在第一个过程中打开文件
+0

为什么不把它放到临时目录中让系统为你清理它? – Will 2010-12-21 05:18:22

+0

系统多久清理一次?我意识到我在临时目录中有4个演出...有没有可以标记的文件属性来加速操作系统决定是否清理? – Reddog 2010-12-21 07:33:18

只是两天前处理了这个。

当已经没有实例打开时,文档直接在新实例中打开。

当有一个实例已经打开,我相信这个实例产生一个新的实例,你实际上没有得到一个句柄。会发生什么情况是控制立即返回到您的函数,然后在新实例有机会读取文件之前删除文件 - 因此它似乎不在那里。

我通过不立即删除文件来解决这个问题,而是在列表中跟踪路径,然后在程序退出时对它们进行核对(将try/catch中的每个删除用一个空的catch块以防文件在此期间消失)。

+0

嗯,令人惊讶的部分是,失败的删除调用(正如您所说的那样)实际上导致Reader不处理该文件 - 但更加不寻常的是,不是在调试器被连接时。 – Reddog 2010-12-21 07:35:40

+0

@Reddog:那么,删除电话没有失败;事实上,它成功了,这就是Adobe报告错误的原因。 – 2010-12-21 17:06:11

我会建议以下方法:

  1. 在用户的临时目录(Path.GetTempPath)中创建文件。你可以在它下面创建一些子文件夹。
  2. 尝试仅在退出进程的最后一个实例时删除文件(例如,您需要计算退出时启动的进程数,递减计数以及计数为零时尝试删除(全部)打开目前为止)
  3. 尝试清理已创建的子文件夹(在临时目录下),同时开始和结束应用程序。你甚至可以尝试定期清理。
+0

我喜欢子文件夹的想法!这至少可以进一步隔离它们并简化清理方法。我仍然很惊讶,为什么当调试器被连接时,这个愚蠢的东西工作得很好,而不是其他的。 – Reddog 2010-12-21 07:34:04

+0

@Reddog,已知应用程序会在调试器下改变行为。我知道的几个原因 - a)调试器中的断点暂停所有应用程序线程,b)跳过某些优化以用于调试 – VinayC 2010-12-21 08:17:27

+0

如果您正在逐步完成代码,那么您可能需要足够的时间在Adobe Reader上打开文件它被处置/删除 – Scott 2014-05-27 06:19:31