我已将COM接口IPreviewHandler
导入到WinForms应用程序中并使用它来显示各种类型文档的预览(我在注册表中查找相应预览处理程序的GUID,然后使用{{ 1}}实例化特定的COM类。
这绝对适用于绝大多数文件类型 - Office格式,PDF,视频等 - 但是,在我实例化“Microsoft Windows TXT预览处理程序”Activator.CreateInstance(guid)
后,初始化它包含一个包含普通.txt文件的流,设置预览窗口的边界,然后最后调用{1531d583-8375-4d3f-b5fb-d23bbd169f22}
,我得到一个无法使用try ... catch:
DoPreview()
当我使用调试器时,Visual Studio Hosting Process崩溃了。如果没有调试器,应用程序会在不触发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
}
或AppDomain.UnHandledException
事件的情况下崩溃。
我真的不介意我无法使用这种技术预览纯文本文件(Office格式的工作预览处理程序等足以满足我的应用程序的要求),但是我担心我的应用程序会无法控制地崩溃应该用户选择.txt文件。有什么方法可以捕捉到这个错误并优雅地处理它?更好的是,有什么方法可以克服它并使处理程序工作吗?
答案 0 :(得分:7)
我无法让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就会很快终止程序。这种情况发生时没有异常被提升,堆栈无法安全解开,因为它已被泄露。因此,CLR无法捕获异常。
此错误特定于TXT文件查看器。除了不使用它之外,你无能为力。将此错误报告给connect.microsoft.com可能没用,他们会将其关闭为“外部”。这是一个微妙的提示,当您让非托管代码在程序中运行时会发生什么;)
答案 1 :(得分:2)
出现此问题的真正原因是您正在创建预览处理程序对象进行中。正确的方法是进程外创建它。
披露以下内容包含我的博客/代码段的广告。
有关示例,请参见https://github.com/GeeLaw/PreviewHost。具体来说,请参见Line 219 of PreviewHandler.cs,在此必须将CLSCTX_LOCAL_SERVER
传递给CoCreateInstance
。如one of my blog entries中所述,Activator.CreateInstance
允许进程内服务器,该服务器不符合预览处理程序的期望,因为必须按照on MSDN所述,在正确的代理过程中创建它们。 / p>
答案 2 :(得分:1)
我遇到了同样的问题,我能够通过在x64而不是AnyCPU
进行编译来使TXT PreviewHandler正常工作。
我在Windows 7(64位)上使用Visual Studio 2010,因此如果您使用的是32位操作系统,则此答案将不适用。
在Visual Studio 2010中
Configurations
下拉列表Configuration Manager...
Platform
单元格New...
并选择目标平台x64
AnyCPU
复制设置,然后离开。答案 3 :(得分:0)
它不太可能,但可能是这里的问题 - catch(Exception)将只捕获异常类型的异常 - 尝试使用catch w / o任何类型过滤。
catch(Exception ex) {
// Normal logging etc
}
catch
{
// Exception of types other than System.Exception.
}
答案 4 :(得分:0)
我想我找到了解决这个问题的方法。问题是你正在创建的流要么被垃圾收集器清理,要么被其他东西清理。如果使用下面代码创建的流调用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)