Como uso o MODI em um aplicativo Web ASP.NET?
Pergunta
Eu escrevi uma biblioteca de wrapper OCR em torno da API da Microsoft Office Document Imaging API e, em um aplicativo de console em execução localmente, ele funciona perfeitamente, a cada teste.
Infelizmente, as coisas começam a ficar mal quando tentamos integrá -lo a um serviço WCF em execução como um aplicativo Web ASP.NET, no IIS6. Tivemos problemas em tentar liberar os objetos Modi com, e havia muitos exemplos na web que nos ajudaram.
No entanto, os problemas ainda permanecem. Se eu reiniciar o IIS e fazer uma nova implantação do aplicativo da web, as primeiras tentativas de OCR funcionam muito bem. Se eu deixar por 30 minutos ou mais e depois fizer outra solicitação, recebo erros de falha do servidor como este:
O servidor lançou uma exceção. (Exceção de HRESULT: 0x80010105 (rpc_e_serverfault)): em modi.documentclass.create (string fileopen)
A partir deste ponto, cada solicitação não fará o OCR, até eu redefinir o IIS, e o ciclo começar novamente.
Executamos esse aplicativo em seu próprio pool de aplicativos e ele é executado sob uma identidade com os direitos de administrador local.
ATUALIZAÇÃO: Este problema pode ser resolvido fazendo o material do OCR fora do processo. Parece que a biblioteca Modi não joga bem com o código gerenciado, quando se trata de limpar depois de si, portanto, a geração de novos processos para cada solicitação de OCR funcionou bem na minha situação.
Aqui está a função que executa o 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();
}
}
Em seguida, instanciei uma instância de ImageReader dentro de um bloco de uso (que chamará idisposable.Dispose on Sair)
Chamar marshal.FinalReleasEComObject deve instruir o CLR a liberar os objetos COM, e, portanto, não estou sem descobrir o que estaria causando os sintomas que temos.
Pelo que vale a pena, executando esse código fora do IIS, em um aplicativo de console, tudo parece à prova de balas. Funciona sempre.
Quaisquer dicas que me ajudem a diagnosticar e resolver esse problema seriam uma imensa ajuda e eu votarei como louco! ;-)
Obrigado!
Solução
Você já pensou em hospedar a parte OCR do seu aplicativo fora do processo.
Ter um serviço pode lhe dar toneladas de flexibilidade:
- Você pode definir um ponto final simples para o seu aplicativo da Web e acessá -lo por meio de remoção ou WCF.
- Se o material é a forma de pêra e a biblioteca é toda a Dodge, você pode lançar o serviço um processo separado toda vez que precisar executar o OCR. Isso oferece segurança extrema, mas envolve uma pequena despesa extra. Eu assumiria isso OCR é muito mais caro do que aumentar um processo.
- Você pode manter uma instância em torno do objeto COM, se a memória começar a vazar, você poderá se reiniciar sem afetar o site (se tiver cuidado).
Pessoalmente, encontrei no passado o COM INTEROP + IIS = sofrimento.
Outras dicas
Modi é incrivelmente instável quando se trata de se livrar de si mesmo, especialmente correndo no IIS. Na minha experiência, descobri que, embora isso diminua tudo, a única maneira de se livrar desses erros é adicionar um GC.WaitPorpendingFinalizers () após sua chamada gc.collect (). Se você estiver interessado, escrevi um artigo sobre isso.
Você pode replicar o problema em um pequeno aplicativo de console? Talvez deixando -o dormir por 30 minutos e voltar a ele?
A melhor maneira de resolver coisas como essa é isolá -lo totalmente. Eu estaria interessado em ver como isso funciona.
Eu tive que lidar com esse erro há uma semana e, depois de testar algumas soluções dando aqui, finalmente resolvi o problema. Vou explicar aqui como fiz isso.
No meu caso, tenho um Windows Service Runing and Processing Documents de uma pasta, o problema ocorre quando há mais de 20 documentos, lançando o erro: Exceção do HRESULT: 0x80010105 (RPC_E_SERVERFAULT).
No meu código, eu chamava um método cada vez que detecto um documento na pasta, faço uma instância do documento Modi (Modi.document _document = new Modi.document ();) e processei o arquivo, e foi isso que causa o que causa o erro!!
A solução era ter apenas uma instância global do Modi.Document e processar todos os documentos, dessa maneira, tenho apenas uma execução de instância para o meu serviço o tempo todo.
Espero que isso ajude aqueles que estão enfrentando o mesmo problema.