Процесс Adobe Reader завершается сбоем при запуске второго экземпляра

В нашем приложении C# WinForms мы создаем PDF-файлы и запускаем Adobe Reader (или любой другой системный обработчик .pdf по умолчанию) через класс Process. Поскольку наши PDF-файлы могут быть большими (около 200 КБ), мы обрабатываем событие Exited, чтобы впоследствии очистить временный файл.

Система работает так, как требуется, когда файл открывается, а затем снова закрывается. Однако, когда открывается второй файл (перед закрытием 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 для хранения ссылок на объекты Process, чтобы они оставались в области действия, чтобы можно было успешно вызвать событие Exited.

Наше событие очистки/выхода:

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

Возможные решения:

  • Обнаружить, что файл все еще открыт в другом процессе
  • Обнаружить, что второй процесс на самом деле не был полностью завершен и что файл вместо этого открыт в первом процессе

person Reddog    schedule 21.12.2010    source источник
comment
Почему бы просто не поместить его во временный каталог и позволить системе очистить его за вас?   -  person    schedule 21.12.2010
comment
Как часто система будет его очищать? Я понял, что у меня есть 4 гигабайта в моем временном каталоге ... Есть ли какие-либо атрибуты файла, которыми вы можете пометить его, чтобы ускорить решение ОС, очищать или нет?   -  person Reddog    schedule 21.12.2010


Ответы (2)


Я только разобрался с этим пару дней назад.

Если еще нет открытых экземпляров, документ открывается в новом экземпляре напрямую.

Когда экземпляр уже открыт, я считаю, что этот экземпляр порождает новый экземпляр, с которым вы на самом деле не справляетесь. Что происходит, так это то, что управление немедленно возвращается к вашей функции, которая затем идет и удаляет файл до того, как новый экземпляр сможет прочитать файл - следовательно, его там нет.

Я «решил» это, не удаляя файлы немедленно, а отслеживая пути в списке, а затем уничтожая их все при выходе из программы (оберните каждое удаление в блоке try/catch с пустым блоком catch на случай, если файл тем временем исчез).

person Jon Seigel    schedule 21.12.2010
comment
Хмммм, удивительная часть заключалась в том, что неудачный вызов удаления (который, как вы сказали, вы испытали) фактически заставил Reader не работать с файлом, но, что еще более необычно, не тогда, когда был подключен отладчик. - person Reddog; 21.12.2010
comment
@Reddog: Ну, вызов удаления не дал сбоев; на самом деле это удалось, поэтому Adobe сообщила об ошибке. - person Jon Seigel; 21.12.2010

Я бы предложил следующий подход:

  1. Создайте файлы во временном каталоге пользователя (Путь.ПолучитьВременныйПуть). Вы можете создать под ней какую-нибудь подпапку.
  2. Попытаться удалить файлы только после выхода из последнего экземпляра процесса (т. е. вам нужно подсчитать количество процессов, которые вы запустили, при выходе уменьшить счетчик, а когда он станет равным нулю, попытаться удалить (все) файлы, которые открыты до сих пор )
  3. Попробуйте очистить созданную подпапку (во временном каталоге) при запуске и завершении приложения. Вы даже можете попытаться выполнить периодическую очистку с помощью таймера.
person VinayC    schedule 21.12.2010
comment
Мне нравится идея с подпапкой! Это, по крайней мере, еще больше их разделит и упростит метод очистки. Я до сих пор не понимаю, почему эта глупая штука отлично работает, когда подключен отладчик, а не иначе. - person Reddog; 21.12.2010
comment
@Reddog, известно, что приложения меняют поведение под отладчиком. Несколько причин, которые я знаю: а) точка останова в отладчике приостанавливает все потоки приложения, б) некоторые оптимизации пропускаются для отладки - person VinayC; 21.12.2010
comment
Если вы выполняете код пошагово, вы, вероятно, даете Adobe Reader достаточно времени, чтобы открыть файл, прежде чем он будет удален/удален. - person Scott; 27.05.2014