2010-08-30 114 views
3

我已經將COM接口IPreviewHandler導入到WinForms應用程序中,並使用它來顯示各種類型文檔的預覽(我在註冊表中查找相應預覽處理程序的GUID,然後使用Activator.CreateInstance(guid)實例的具體COM類IPreviewHandler引發無法捕捉的異常

這工作奇妙的絕大多數文件類型 - 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.UnHandledExceptionApplication.ThreadException事件。

我並不介意使用這種技術無法預覽純文本文件(對於Office格式等的工作預覽處理程序對於我的應用程序的要求已足夠),但是我擔心應用程序崩潰失控應該用戶選擇一個.txt文件。有什麼辦法可以捕捉到這個錯誤並且優雅地處理它嗎?更好的是,有什麼方法可以克服它並讓處理程序起作用?

+0

沒有類型庫。你是如何「導入」接口聲明的? – 2010-08-30 20:28:48

+0

@Hans Passant:使用'[ComImport]'和'[Guid]'屬性進行手動聲明。看到我的博客上的源代碼:http://www.brad-smith.info/blog/archives/79 – 2010-08-30 23:21:02

回答

6

我無法使GetPreviewHandlerGUID()識別.txt文件,而必須直接注入GUID。當您使用Project + Properties,Debug時,可以看到出現了什麼問題,勾選啓用非託管代碼調試。

調試器現在將在問題和顯示器

`STATUS_STACK_BUFFER_OVERRUN遇到

調用堆棧的頂部看起來像這樣停止:

[email protected]() + 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 
[email protected]() + 0x13d bytes 

的問題所在在StreamInCallback()函數中。它由RichTextBox調用,用於顯示預覽(msftedit.dll)來加載文件。這個回調函數中的代碼有一個bug,它破壞了用來檢測堆棧幀由於緩衝區溢出而損壞的'canary'。

這是微軟用來防止病毒通過緩衝區溢出注入自身的對策措施的一部分。 Visual Studio中用於C/C++語言的/ GS編譯選項。一旦檢測到,CRT就會很快終止程序。發生這種情況時沒有例外,堆棧無法安全地展開,因爲它已被泄露。因此,CLR無法捕捉到例外。

此錯誤特定於TXT文件查看器。除了不使用它之外,你無能爲力。向connect.microsoft.com報告此錯誤可能沒有用,他們會將其作爲「外部」關閉。這是一個微妙的提示,當你讓非託管代碼在你的程序中運行時會發生什麼;)

+0

謝謝,您的回答肯定會對此有所瞭解。我發現Windows TXT預覽處理程序實際上與Windows 7上的.txt擴展名沒有關聯,僅在Vista上。也許MS意識到允許第三方應用程序託管這個多功能處理程序是一個壞主意?我可能只是在我的應用程序中將CLSID列入黑名單。 – 2010-08-31 02:11:51

+0

多好的答案。布拉德利史密斯,你是否爲txt文件編寫自己的處理程序?或者你如何解決這個問題? – SwissCoder 2011-07-18 11:52:00

+0

不可捕捉的異常似乎也被拋出* .reg * .bat和* .vbs文件。 – 2015-01-23 13:55:26

0

它非常不可能,但可以在這裏問題 - catch(Exception)將只捕獲異常類型的異常 - 嘗試使用catch不帶任何類型的過濾。

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

毫不意外,這是行不通的。我也非常肯定,按照定義,CLR不能捕獲除System.Exception之外的任何東西。如果發生一些低級異常,'Marshal'應該拋出一個'COMException'或一個'Win32Exception'。 – 2010-08-30 08:33:57

+0

我同意 - 應該有COMException。僅供參考,我曾在J Ritcher書中讀過CLR沒有要求異常對象是System.Exception類型,但語言(C#,Vb.NET)不支持它。 針對您的問題的另一個長鏡頭 - 如果您使用的是.NET4,請檢查您是否遇到了損壞的狀態異常 - 因爲它們需要使用HandleProcessCorruptedStateExceptions屬性標記您的代碼。 – VinayC 2010-08-30 08:47:57

+0

不幸的是,仍然生活在.NET 2.0的土地上。可能嘗試在一個針對.NET 4的獨立項目中嘗試... – 2010-08-30 09:01:18

1

我有同樣的問題,我能通過編譯x64而不是AnyCPU來獲得TXT PreviewHandler。

我在Windows 7(64位)上使用Visual Studio 2010,因此如果您在32位操作系統中,此答案將不適用。

在Visual Studio 2010

  • 點擊Configurations下拉列表
  • 選擇Configuration Manager...
  • 單擊旁邊的項目Platform細胞
  • 選擇New...,並選擇目標平臺x64
  • AnyCPU複製設置並離開你。
0

我想我已經找到了解決這個問題的方法。問題是你正在創建的流是由垃圾收集器或其他東西清理。如果您使用的代碼創建的流調用初始化方法如下它應該工作:

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)的和單線程運行公寓模式。

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