IPreviewHandler抛出无法捕获的exception

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

这适用于绝大多数文件类型 – Office格式,PDF,video等 – 但是,在我实例化“Microsoft Windows TXT预览处理程序” {1531d583-8375-4d3f-b5fb-d23bbd169f22} ,使用流初始化它包含一个普通的.txt文件,设置预览窗口的边界,然后最后调用DoPreview() ,我得到一个无法使用try … catch捕获的exception:

 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 Hosting Process崩溃了。 如果没有调试器,应用程序将崩溃而不会触发AppDomain.UnHandledExceptionApplication.ThreadException事件。

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

我无法让GetPreviewHandlerGUID()识别.txt文件并且必须直接注入GUID。 使用Project + Properties,Debug,tick启用非托管代码调试时,您可以看到出了什么问题。

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

遇到`STATUS_STACK_BUFFER_OVERRUN

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

 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()函数中。 它由RichTextBox调用,用于显示加载文件的预览(msftedit.dll)。 此回调函数中的代码有一个错误,它会破坏用于检测堆栈帧因缓冲区溢出而损坏的“canary”。

这是微软采取的防止病毒通过缓冲区溢出注入自身的措施的一部分。 Visual Studio中用于C / C ++语言的/ GS编译选项。 一旦检测到,CRT就会非常迅速地终止程序。 这种情况发生时没有exception提升,堆栈无法安全解除,因为它已被泄露。 因此,CLR无法捕获exception。

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

我有同样的问题,我能够通过在x64而不是AnyCPU编译来使TXT PreviewHandler工作。

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

在Visual Studio 2010中

  • 单击Configurations下拉列表
  • 选择Configuration Manager...
  • 单击项目旁边的“ Platform单元格
  • 选择New...并选择目标平台x64
  • AnyCPU复制设置,然后离开。

它不太可能,但可能是这里的问题 – catch(Exception)将只捕获Exception类型的exception – 尝试使用catch w / o任何类型过滤。

 catch(Exception ex) { // Normal logging etc } catch { // Exception of types other than System.Exception. } 

我想我找到了解决这个问题的方法。 问题是你正在创建的流要么被垃圾收集器清理,要么被其他东西清理掉。 如果使用下面代码创建的流调用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); 

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

归功于Sherlock Homes( http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.interop/2010-09/msg00003.html