如何在 ASP.Net Web 应用程序中使用 MODI?

Posted

技术标签:

【中文标题】如何在 ASP.Net Web 应用程序中使用 MODI?【英文标题】:How do I use MODI in an ASP.Net Web Application? 【发布时间】:2010-11-23 14:10:09 【问题描述】:

我已经围绕 Microsoft Office Document Imaging COM API 编写了一个 OCR 包装库,并且在本地运行的控制台应用程序中,它可以完美运行,每次测试。

遗憾的是,当我们尝试将它与在 IIS6 下作为 ASP.Net Web 应用程序运行的 WCF 服务集成时,事情开始变得糟糕。我们在尝试释放 MODI COM 对象时遇到了问题,网上有很多例子对我们有帮助。

但是,问题仍然存在。如果我重新启动 IIS,并重新部署 Web 应用程序,前几次 OCR 尝试效果很好。如果我将其放置 30 分钟左右,然后再执行另一个请求,则会收到如下服务器故障错误:

服务器抛出异常。 (来自 HRESULT 的异常:0x80010105 (RPC_E_SERVERFAULT)):在 MODI.DocumentClass.Create(String FileOpen)

从现在开始,每个请求都将无法进行 OCR,直到我重置 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();
    

然后我在 using 块中实例化 ImageReader 的实例(在退出时将调用 IDisposable.Dispose)

调用 Marshal.FinalReleaseComObject 应该指示 CLR 释放 COM 对象,所以我不知道是什么导致了我们的症状。

对于它的价值,在 IIS 之外运行此代码,比如控制台应用程序,一切似乎都是防弹的。每次都有效。

任何可以帮助我诊断和解决此问题的提示都将是一个巨大的帮助,我会疯狂地投票! ;-)

谢谢!

【问题讨论】:

感谢您奖励我,很高兴它对您有所帮助! 【参考方案1】:

您是否考虑过在进程外托管应用的 OCR 部分。

拥有一项服务可以为您提供大量的灵活性:

    您可以为您的 Web 应用程序定义一个简单的端点,并通过远程处理或 WCF 访问它。 如果东西是梨形并且图书馆都是躲避的,你可以让服务在每次你需要执行 OCR 时启动一个单独的进程。这为您提供了极大的安全性,但需要少量的额外费用。我会假设 OCR 比启动流程要昂贵得多。 您可以在 COM 对象周围保留一个实例,如果内存开始泄漏,您可以在不影响网站的情况下重新启动自己(如果您小心的话)。

我个人在过去发现 COM 互操作 + IIS = grief。

【讨论】:

嗨,Sam,是的,这是我这周刚刚尝试过的。我使用 WCF(和 NetTCPBinding)将 OCR 内容放入单独托管的 Windows 服务中。当我在 IIS 下运行它时,我仍然有非常相似的症状。根据 Silky 的提示,我尝试了一个长时间运行的控制台应用程序(而不是我编写的短期运行版本),并设法在 10 分钟内复制了该问题。但是,无论结果是什么问题,我都会为您的答案 +1,因为完全出于您概述的原因,在流程之外执行此操作更有意义。谢谢。 哦,另外,我没想过要启动一个新流程,这也是个好主意。事实上,这可能只是一个很好的解决方法,因为我可以捕获 COM 中断异常,并启动一个新进程……太棒了,我现在很兴奋。我会试试看,然后报告。再次感谢。 这很奏效。我编写了一个 Windows 服务,托管一个 WCF 服务,该服务启动一个新进程,为每个请求包装 OCR 内容。是的,这听起来很昂贵,但正如您所指出的,与进行实际 OCR 本身的成本相比,它微不足道。正确关闭进程会清除“古怪”的 MODI 互操作内容,并且一切正常。感谢 Sam 和所有相关人员。非常感谢。【参考方案2】:

在摆脱自身时,MODI 非常不稳定,尤其是在 IIS 中运行。根据我的经验,我发现虽然它会减慢一切,但摆脱这些错误的唯一方法是在 GC.Collect() 调用之后添加 GC.WaitForPendingFinalizers() 。如果你有兴趣,我写了一个article 关于这个。

【讨论】:

优秀的文章,感谢您在此处引用。我会尽快考虑实施此建议,并告知结果。 非常感谢。希望它可以帮助您解决当前的情况。 遗憾的是我的问题仍然存在。我会用新的源代码更新我原来的帖子,看看社区的想法。感谢您的尝试! MODI 让我很头疼。首先是版本问题,然后是在网络驱动器上注释传真时的文件损坏问题。 “Wonky”是个好词! ;)【参考方案3】:

您能否在一个小型控制台应用程序中复制该问题?也许让它睡 30 分钟然后再回来?

解决此类问题的最佳方法是将其完全隔离。我很想看看它是如何工作的。

【讨论】:

这真是个好主意,谢谢 Silky。我的控制台测试应用程序永远不会失败,但我也从未让它闲置。它只是运行测试并退出,可能正确地丢弃了对 COM 对象的引用。我将修改控制台应用程序,并让您知道结果如何。干杯 我唯一真正的猜测是,您正在以某种方式运行某些不兼容的 exe 的另一个版本(例如,在同一个应用程序池中运行两个不同的 .net 版本)并且以某种方式损坏了 dll。这将解释它在重新启动后工作的原因。 虽然我还没有完全解决这个问题,但您的评论非常有帮助。我设法在一个长时间运行的控制台应用程序中复制了这个问题,所以我现在可以消除 IIS 作为问题的原因。再次感谢您的提示。 我想知道是否有某种方法可以添加一些跟踪来观察垃圾收集器正在做什么(就像另一个帖子暗示的那样)。也许它是 COM 需要的过期对象。现在只是疯狂地猜测,但如果有启用详细 GC 输出的选项,也许尝试启用它。我认为您尝试在其他机器上进行复制并获得相同的结果? 等一下。我刚刚注意到您有一个尝试删除某些内容的处置。你一直这样吗?或者它是新的?我认为这是新的,但我不确定这是一个好主意......【参考方案4】:

一周前我不得不处理这个错误,在测试了这里给出的一些解决方案之后,我终于解决了这个问题。我将在这里解释我是如何做到的。

在我的情况下,我有一个 Windows 服务正在运行和处理文件夹中的文档,当有超过 20 个文档时会出现问题,并抛出错误:HRESULT 异常:0x80010105 (RPC_E_SERVERFAULT)。

在我的代码中,每次我在文件夹中检测到一个文档时,我都会调用一个方法,我创建一个 MODI 文档实例 (MODI.Document _document = new MODI.Document();) 并处理该文件,然后是什么导致了错误!

解决方案是只拥有一个 MODI.Document 的全局实例,并通过它处理所有文档,这样我就只有一个实例始终为我的服务运行。

希望对遇到同样问题的人有所帮助。

【讨论】:

以上是关于如何在 ASP.Net Web 应用程序中使用 MODI?的主要内容,如果未能解决你的问题,请参考以下文章

如何在asp.net的Web应用程序中录制音频? [复制]

如何在使用 JWT 的 asp.net 核心 web 应用和 web api 中使用谷歌身份验证

如何在 asp.net Web 应用程序中使用 jquery ajax 使用 Web 服务

如何在 ASP.Net Web 应用程序中使用 MODI?

如何在 ASP.NET Web 窗体环境中使用 DLL?

如何在 ASP.NET Web API SelfHost 应用程序中使用 CORS?