2009-08-28 12 views
10

He escrito una biblioteca de contenedor de OCR alrededor de la API COM de Microsoft Office Document Imaging, y en una aplicación de consola que se ejecuta localmente, funciona perfectamente, con cada prueba.¿Cómo uso MODI en una aplicación web ASP.Net?

Lamentablemente, las cosas empiezan a ir mal cuando intentamos integrarlo con un servicio WCF que se ejecuta como una aplicación web ASP.Net, bajo IIS6. Tuvimos problemas para liberar los objetos COM de MODI y hubo muchos ejemplos en la web que nos ayudaron.

Sin embargo, aún persisten los problemas. Si reinicio IIS y realizo una nueva implementación de la aplicación web, los primeros pocos intentos de OCR funcionan bien. Si lo dejo durante 30 minutos más o menos, y luego hacer otra solicitud, recibo errores falla en el servidor como este:

The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT)): at MODI.DocumentClass.Create(String FileOpen)

A partir de ahora, cada solicitud dejará de hacer el OCR, hasta que restablecer IIS, y el ciclo comienza de nuevo.

Ejecutamos esta aplicación en su propio grupo de aplicaciones y se ejecuta con una identidad con derechos de administrador local.

ACTUALIZACIÓN: Este problema se puede resolver haciendo que el material de OCR esté fuera de proceso. Parece que la biblioteca MODI no funciona bien con el código administrado, cuando se trata de limpiar por sí mismo, por lo que generar nuevos procesos para cada solicitud de OCR funcionó bien en mi situación.

Aquí es la función que realiza el 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(); 
    } 
} 

entonces se crea una instancia de ImageReader dentro de un bloque usando (que se llame a la salida IDisposable.Dispose)

Calling Marshal.FinalReleaseComObject debería instruir al CLR para que libere los objetos COM, por lo que no sé qué causaría los síntomas que tenemos.

Por lo que vale la pena, ejecutar este código fuera de IIS, por ejemplo, una aplicación de consola, todo parece a prueba de balas. Funciona todo el tiempo.

¡Cualquier consejo que me ayude a diagnosticar y resolver este problema sería una gran ayuda y me volvería loco! ;-)

Gracias!

+0

Gracias por concederme la recompensa, ¡me alegro de que te haya ayudado! –

Respuesta

4

¿Ha pensado en alojar la parte de OCR de su aplicación fuera de proceso.

Tener un servicio que puede dar un montón de flexibilidad:

  1. Usted puede definir un punto final simple para su aplicación web, y acceder a ella a través de la interacción remota o WCF.
  2. Si las cosas tienen forma de pera y la biblioteca es totalmente esquiva, puede hacer que el servicio inicie un proceso por separado cada vez que necesite realizar OCR. Esto le brinda seguridad extrema, pero implica un pequeño gasto adicional. Supongo que OCR es MUCHO más costoso que girar un proceso.
  3. Puede mantener una instancia alrededor del objeto COM; si la memoria comienza a perder, puede reiniciarse sin afectar el sitio web (si tiene cuidado).

Personalmente he encontrado en el pasado la interoperabilidad COM + IIS = duelo.

+0

Hola Sam, sí, esto es algo que intenté esta semana. Puse el material de OCR en un servicio de Windows alojado por separado utilizando WCF (y NetTCPBinding). Todavía tenía síntomas muy similares a cuando lo ejecutaba bajo IIS. Basándome en una pista de Silky, probé una aplicación de consola de larga duración (a diferencia de la versión de ejecución corta que había escrito), y logré replicar el problema en 10 minutos. Sin embargo, independientemente de lo que sea el problema, voy a +1 a su respuesta, porque tiene mucho más sentido hacer esto fuera de proceso, por exactamente las razones que ha descrito. Gracias. –

+0

Ah, además, no pensé en hacer un nuevo proceso, eso también es una excelente idea. De hecho, eso podría ser una buena solución, porque puedo atrapar la excepción COM Interupt y activar un nuevo proceso ... Genial, estoy emocionado ahora. Lo probaré e informaré. gracias de nuevo. –

+0

Esto funcionó como un regalo. Escribí un servicio de Windows, alojando un servicio WCF que hace girar un nuevo proceso envolviendo las cosas de OCR para cada solicitud. Sí, eso suena caro, pero como usted señaló, es insignificante al lado del costo de hacer el propio OCR. Cerrar el proceso limpia correctamente las cosas de interoperabilidad MODI 'wonky', y todo funciona como debería. Gracias Sam y todos los involucrados. Muy apreciado. –

1

MODI es increíblemente inseguro cuando se trata de deshacerse de sí mismo, especialmente cuando se ejecuta en IIS. En mi experiencia, descubrí que, aunque ralentiza todo, la única forma de deshacerse de estos errores es agregar GC.WaitForPendingFinalizers() después de la llamada GC.Collect(). Si estás interesado, escribí un article sobre esto.

+0

Excelente artículo, gracias por hacer referencia aquí. Voy a ver cómo implementar esta sugerencia pronto y asesorar sobre el resultado. –

+0

Muchas gracias. Espero que te ayude con tu situación actual. –

+0

Lamentablemente, mi problema aún persiste. Actualizaré mi publicación original con el nuevo código fuente y veré qué piensa la comunidad. ¡Gracias por intentarlo de todos modos! –

1

¿Se puede replicar el problema en una aplicación de consola pequeña? Tal vez dejarlo dormir durante 30 minutos y volver a él?

La mejor manera de resolver este tipo de cosas es aislarlo por completo. Me interesaría ver cómo funciona eso.

+0

Esa es una muy buena idea, gracias Silky. La aplicación de prueba de mi consola nunca falla, pero tampoco la tengo inactiva. Simplemente ejecuta las pruebas y sale, presumiblemente arrojando referencias a los objetos COM. Voy a modificar la aplicación de la consola y te haré saber cómo van las cosas. Cheers –

+0

Mi única conjetura real es que de alguna manera está ejecutando otra versión de algún exe incompatible (por ejemplo, ejecutando dos versiones diferentes de .NET en el mismo grupo de aplicaciones) y eso está de alguna manera corrompiendo el dll. Esto explicaría el motivo por el que funciona después de un reinicio. –

+0

Aunque aún no he resuelto esto, su comentario fue increíblemente útil. Pude replicar el problema en una aplicación de consola de larga ejecución, por lo que ahora puedo eliminar IIS como la causa del problema. Gracias de nuevo por la pista. –

1

Tuve que lidiar con este error hace una semana, y después de probar algunas soluciones aquí, finalmente resolví el problema. Explicaré aquí cómo lo hice.

En mi caso tengo un servicio de Windows ejecutando y procesando documentos desde una carpeta, el problema ocurre cuando hay más de 20 documentos, arrojando el error: Excepción de HRESULT: 0x80010105 (RPC_E_SERVERFAULT).

En mi código estaba llamando a un método cada vez que detecto un documento en la carpeta, hago una instancia del documento MODI (MODI.Document _document = new MODI.Document();) y proceso el archivo, y eso fue lo que causa el error !!

La solución consistía en tener solo una instancia global de MODI.Document y procesar todos los documentos, de esta forma solo tengo una instancia ejecutándose para mi servicio en todo momento.

Espero que ayude a aquellos que enfrentan el mismo problema.

Cuestiones relacionadas