代码之家  ›  专栏  ›  技术社区  ›  Bradley Smith

IPreviewHandler引发无法捕获的异常

  •  3
  • Bradley Smith  · 技术社区  · 14 年前

    我已经导入了COM接口 IPreviewHandler 进入一个WinForms应用程序,并使用它来显示各种类型文档的预览(我在注册表中查找适当的预览处理程序的GUID,然后使用 Activator.CreateInstance(guid) 实例化特定的COM类。

    这作品 对于绝大多数文件类型(office格式、pdf、视频等)而言,这是非常棒的。不过,在我实例化了“Microsoft Windows TXT预览处理程序”之后, {1531d583-8375-4d3f-b5fb-d23bbd169f22} ,用包含普通.txt文件的流初始化它,设置预览窗口的边界,然后最终调用 DoPreview() ,我得到一个使用try…catch无法捕获的异常:

    try {
        Type comType = Type.GetTypeFromCLSID(guid);
        object handler = Activator.CreateInstance(comType);
    
        if (handler is IInitializeWithStream) {
            Stream s = File.Open(filename, FileMode.Open);
            // this just passes the System.IO.Stream as the COM type IStream
            ((IInitializeWithStream)handler).Initialize(new StreamWrapper(s), 0);
        }
        else {
            throw new NotSupportedException();
        }
    
        RECT r = new RECT();
        r.Top = 0;
        r.Left = 0;
        r.Right = hostControl.Width;
        r.Bottom = hostControl.Height;
    
        ((IPreviewHandler)handler).SetWindow(hostControl.Handle, ref r);
        ((IPreviewHandler)handler).DoPreview();    // <-- crash occurs here
    }
    catch (Exception) {
        // this will never execute
    }
    

    当我使用调试器单步执行时,Visual Studio宿主进程崩溃。如果没有调试程序,应用程序将在不触发 AppDomain.UnHandledException Application.ThreadException 事件。

    我真的不介意我不能使用这种技术预览纯文本文件(办公格式的工作预览处理程序等足以满足我的应用程序的要求),但我担心如果用户选择一个.txt文件,我的应用程序会无法控制地崩溃。有没有什么方法可以捕获这个错误并妥善处理它?更好的是,我有什么方法可以克服它并让处理程序工作吗?

    5 回复  |  直到 6 年前
        1
  •  7
  •   Hans Passant    14 年前

    我无法获取getPreviewHandlerGuid()来识别.txt文件,因此必须直接插入guid。当您使用Project+属性、调试、勾选启用非托管代码调试时,您可以看到发生了什么错误。

    现在,调试器将在出现问题时停止并显示

    `遇到状态\堆栈\缓冲区\溢出

    调用堆栈的顶部如下所示:

    kernel32.dll!_UnhandledExceptionFilter@4()  + 0x1a368 bytes 
    shell32.dll!___report_gsfailure()  + 0xc8 bytes 
    shell32.dll!CRTFPreviewHandler::_StreamInCallback()  + 0x74 bytes   
    msftedit.dll!CLightDTEngine::ReadPlainText()  + 0xed bytes  
    msftedit.dll!CLightDTEngine::LoadFromEs()  + 0x202b3 bytes  
    msftedit.dll!CTxtEdit::TxSendMessage()  + 0x1e25f bytes 
    msftedit.dll!_RichEditWndProc@16()  + 0x13d bytes   
    

    问题位于streamIncallback()函数中。它由用于显示预览(msftedit.dll)以加载文件的richtextbox调用。此回调函数中的代码有一个bug,它会破坏用于检测堆栈帧是否因缓冲区溢出而损坏的“canary”。

    这是微软为防止病毒因缓冲区溢出而自我注射而采取的反措施的一部分。VisualStudio中的/Gs编译选项用于C/C++语言。一旦检测到,CRT会迅速终止程序。发生这种情况时没有异常,因为堆栈已被破坏,所以无法安全地释放堆栈。因此,CLR无法捕获异常。

    此错误特定于TXT文件查看器。除了不使用它,你什么都做不了。将此错误报告给connect.microsoft.com可能没有用,他们会将其作为“外部”关闭。否则,这是一个微妙的提示,当您让非托管代码在程序内部运行时会发生什么;)

        2
  •  2
  •   Gee Law    6 年前

    您遇到此问题的真正原因是您正在创建预览处理程序对象 生产中的 . 正确的方法是创建它 进程外 .

    披露 下面包含我的博客/代码段的广告。

    https://github.com/GeeLaw/PreviewHost 例如。具体来说,请参见 Line 219 of PreviewHandler.cs ,你必须经过的地方 CLSCTX_LOCAL_SERVER CoCreateInstance . 正如在 one of my blog entries , Activator.CreateInstance 允许进程内服务器,这不符合预览处理程序的预期,因为它们必须在正确的代理进程中创建,如文档所示。 on MSDN .

        3
  •  1
  •   Yannick Blondeau    12 年前

    我也遇到了同样的问题,我可以通过在x64中编译而不是 AnyCPU .

    我正在Windows7(64位)上使用Visual Studio 2010,因此如果您使用的是32位操作系统,则此答案将不适用。

    在Visual Studio 2010中

    • 点击 Configurations 下拉列表
    • 选择 Configuration Manager...
    • 点击 Platform 项目旁边的单元格
    • 选择 New... 选择目标平台 x64
    • 从复制设置 任意CPU 你走吧。
        4
  •  0
  •   VinayC    14 年前

    它不太可能,但可能是这里的问题-catch(exception)只捕获异常类型的异常-尝试使用catch而不使用任何类型筛选。

    catch(Exception ex) {
       // Normal logging etc
    }
    catch
    {
       // Exception of types other than System.Exception.
    }
    
        5
  •  0
  •   Bart Prüst    10 年前

    我想我已经找到解决这个问题的办法了。问题是,您正在创建的流要么被垃圾收集器清理,要么被其他东西清理。如果使用下面的代码创建的流调用initialize方法,它应该可以工作:

    System.Runtime.InteropServices.ComTypes.IStream stream;
        byte[] fileData = System.IO.File.ReadAllBytes(filename);
        System.IntPtr hGlobal = System.Runtime.InteropServices.Marshal.AllocHGlobal(fileData.Length);
        System.Runtime.InteropServices.Marshal.Copy(fileData, 0, hGlobal, fileData.Length);
        NativeMethods.CreateStreamOnHGlobal(hGlobal, false, out stream);
        //[DllImport("ole32.dll")]
        //internal static extern int CreateStreamOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease, out IStream ppstm);
    

    i_m在Windows窗体应用程序中使用上述代码,显式设置为32位(x86)并以单线程公寓模式运行。

    信用卡归谢洛克之家( http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.interop/2010-09/msg00003.html )