Как использовать MODI в веб-приложении ASP.Net?

Я написал библиотеку обложек OCR вокруг API-интерфейса Microsoft Office Document Imaging COM API, а в приложении консоли, работающем локально, он работает безупречно с каждым тестом.

К сожалению, все начинается плохо, когда мы пытаемся интегрировать его с сервисом WCF, работающим как веб-приложение ASP.Net, в рамках IIS6. У нас были проблемы с попыткой освободить MODI COM-объекты, и было много примеров в Интернете, которые помогли нам.

Однако проблемы все еще остаются. Если я перезапущу IIS и сделаю новое развертывание веб-приложения, первые несколько попыток OCR отлично работают. Если я оставлю это на 30 минут или около того, а затем сделаю еще один запрос, я получаю ошибки с ошибками сервера, например:

Сервер исключил исключение. (Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT)): в MODI.DocumentClass.Create(String FileOpen)

С этого момента каждый запрос не сможет выполнить OCR, пока я reset IIS, и цикл не начнется снова.

Мы запускаем это приложение в своем собственном пуле приложений, и оно выполняется под идентификатором с правами локального администратора.

ОБНОВЛЕНИЕ: эта проблема может быть решена путем удаления OCR из процесса. Похоже, что библиотека MODI не очень хорошо работает с управляемым кодом, когда дело доходит до очистки после себя, поэтому нереста новых процессов для каждого запроса OCR хорошо работала в моей ситуации.

Вот функция, которая выполняет OCR:

    public class ImageReader : IDisposable
{
    private MODI.Document _document;
    private MODI.Images _images;
    private MODI.Image _image;
    private MODI.Layout _layout;
    private ManualResetEvent _completedOCR = new ManualResetEvent(false);

    // SNIP - Code removed for clarity

    private string PerformMODI(string fileName)
    {
        _document = new MODI.Document();
        _document.OnOCRProgress += new MODI._IDocumentEvents_OnOCRProgressEventHandler(_document_OnOCRProgress);
        _document.Create(fileName);

        _document.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, true, true);
        _completedOCR.WaitOne(5000);
        _document.Save();
        _images = _document.Images;
        _image = (MODI.Image)_images[0];
        _layout = _image.Layout;
        string text = _layout.Text;
         _document.Close(false);
        return text;
    }

    void _document_OnOCRProgress(int Progress, ref bool Cancel)
    {
        if (Progress == 100)
        {
            _completedOCR.Set();
        }
    }
    private static void SetComObjectToNull(params object[] objects)
    {
        for (int i = 0; i < objects.Length; i++)
        {
            object o = objects[i];
            if (o != null)
            {
                Marshal.FinalReleaseComObject(o);
                o = null;
            }
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Dispose()
    {
        SetComObjectToNull(_layout, _image, _images, _document);
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Затем я создаю экземпляр ImageReader внутри используемого блока (который вызовет IDisposable.Dispose при выходе)

Вызов Marshal.FinalReleaseComObject должен дать указание CLR освободить COM-объекты, и поэтому я не понимаю, что может вызвать симптомы, которые у нас есть.

За что стоит, запустив этот код за пределами IIS, скажем, в консольном приложении, все кажется пуленепробиваемым. Он работает каждый раз.

Любые подсказки, которые помогут мне диагностировать и решить эту проблему, будут огромной помощью, и я буду взят как сумасшедший!; -)

Спасибо!

Ответ 1

Думали ли вы о размещении части OCR вашего приложения вне процесса.

Обслуживание может дать вам массу гибкости:

  • Вы можете определить простую конечную точку для своего веб-приложения и получить доступ к ней через удаленный доступ или WCF.
  • Если материал представляет собой грушевидную форму, и библиотека полностью уверена, вы можете запустить службу отдельного процесса каждый раз, когда вам нужно выполнить OCR. Это дает вам максимальную безопасность, но требует небольших дополнительных расходов. Я бы предположил, что OCR намного дороже, чем разворачивание процесса.
  • Вы можете сохранить экземпляр объекта COM, если память начинает протекать, вы можете перезапустить себя, не затрагивая веб-сайт (если вы будете осторожны).

Лично я нашел в прошлом COM interop + IIS = grief.

Ответ 2

MODI невероятно выигрышный, когда дело доходит до избавления от себя, особенно в IIS. По моему опыту, я обнаружил, что, хотя он все замедляет, единственный способ избавиться от этих ошибок - добавить GC.WaitForPendingFinalizers() после вызова GC.Collect(). Если вам интересно, я написал статью об этом.

Ответ 3

Можете ли вы воспроизвести проблему в небольшом консольном приложении? Возможно, оставив его спать в течение 30 минут и вернуться к нему?

Лучший способ решить такие вещи - полностью изолировать его. Мне было бы интересно посмотреть, как это работает.

Ответ 4

Мне пришлось иметь дело с этой ошибкой неделю назад, и после тестирования некоторых решений, предоставляемых здесь, я, наконец, решил проблему. Я объясню, как я это сделал.

В моем случае у меня есть служба Windows, запускающая и обрабатывающая документы из папки, проблема возникает, когда имеется более 20 документов, бросая ошибку: Исключение из HRESULT: 0x80010105 (RPC_E_SERVERFAULT).

В моем коде я вызывал метод каждый раз, когда я обнаруживал документ в папке, я делаю экземпляр документа MODI (MODI.Document _document = new MODI.Document();) и обрабатываю файл, и это было причиной ошибки!

Решение состояло в том, чтобы иметь только один глобальный экземпляр MODI.Document и обрабатывать все документы с ним, таким образом, у меня есть только один экземпляр экземпляра для моей службы все время.

Я надеюсь, что это поможет тем, кто сталкивается с одной и той же проблемой.