一次尋找Windows Live Writer的VSPaste外掛程式丟失RTF格式資訊的經曆

來源:互聯網
上載者:User

標籤:msdn   類型   類比   不難   處理   vsp   iter   就會   檢查   

背景

我在部落格園上寫部落格是使用Windows Live Writer,代碼高亮外掛程式是使用Paste from Visual Studio(下文簡稱VSPaste)。

Windows Live Writer更進一步的資料,可參照【超詳細教程】使用Windows Live Writer 2012和Office Word 2013 發布文章到部落格園全面總結,在此處。

VSPaste更進一步的資料,可參照CnBlogs博文排版技巧。由於Windows Live Writer 2012的終止日期是2017年1月10日,並且對應的外掛程式網站也關閉了,所以目前沒有官方下載,有需要的可以聯絡我。

起因

好久沒更新過部落格了,一是懶,二是沒什麼值得分享的。恰好手上有了一點可以分享的話題,就開始興高采烈的寫部落格了。寫著寫著,發現了VSPaste複製存在丟失格式的情況,於是就來研究這個問題了。

就丟失格式的情況舉一個例子,比如,我複製的是如下代碼:

然而,使用VSPaste插入到Windows Live Writer中後,文字全都成了黑色。綠色和藍色呢?

檢查VSPaste是否出錯

由於VSPaste已經很久沒有更新,所以我的第一反應是查看VSPaste是否出錯。為了驗證判斷,我們不妨建立一個工程進行測試。

尋找入口

在建立工程之前,需要先瞭解Windows Live Writer調用VSPaste的函數入口。在必應上搜尋windows live writer plugin develop,發現有一篇名為Developing Plugins for Windows Live Writer的文章,經過瞭解後發現,外掛程式一定繼承自ContentSource或者SmartContentSource。其中ContentSource是直接插入HTML到Windows Live Writer中,而SmartContentSource功能會更豐富一些,比如可以添加後編譯。

開啟ILSpy,將VSPaste的程式集拖入其中。經過簡單查看,發現VSPaste外掛程式的入口類正是繼承自SmartContentSource。而且其中做的事情很簡單,判斷剪貼簿中是否存在RTF格式的資料,如果存在,將其轉換為HTML。

另外,部落格園官方發布的代碼著色控制項CNBlogs.CodeHighlighter確實如他們所說的,將代碼提交至伺服器處理。如:

填充測試工程

通過的分析,我們可以建立一個簡單的測試工程。在分析VSPaste入口時,發現其引用了System.Windows.Forms。所以我們不妨建立一個Windows表單應用程式來顯示轉換前的RTF和轉換後的HTML。

建立工程後先添加VSPaste的引用,接下來再添加VSPaste所必需的WindowsLive.Writer.Api。這個DLL在哪裡呢?由於是Windows Live Writer外掛程式,所以猜測是在Windows Live Writer安裝目錄下,一查,果然存在這個DLL。可是要是安裝目錄下不存在該怎麼辦呢?我比較喜歡用Everything這個軟體,可以直接輸入檔案名稱尋找,速度又快。但是使用該軟體的前提是必須要保證對應的盤是NTFS檔案系統。

完成主介面,一個主介面由兩個文字框、一個兩行的TableLayoutPanel和一個按鈕組成,如下:

測試按鈕的響應如下:

運行失敗及解決方案

我們的測試工程已經完成了,接下來我們運行一下試試。編譯成功,運行成功,接下來在VS中複製一段代碼,點擊運行試試。非常不幸,出現了這個錯誤:

這個錯誤我有經驗,多出現於P/Invoke情境。也比較好解決,在工程屬性的產生標籤頁中將產生平台改成x86即可。好了,再來嘗試,依然報錯:

不科學啊,平常都行啊,怎麼這次就出問題。再仔細對比一下,還是有區別的,這次是找到的資訊清單定義與程式集引用不匹配。點擊查看詳細資料,如:

仔細閱讀FusionLog的資訊,發現應該是WindowsLive.Writer.Api的程式集版本不一致。難道是我哪裡疏忽了?

開啟ILSpy,查看VSPaste的所引用的WindowsLive.Writer.Api。結果如:

再在ILSpy中查看我所安裝的Windows Live Writer 2012目錄下的WindowsLive.Writer.Api的版本資訊。結果如:

聰明如你,一定已經發現上面的不同了。沒錯,VSPaste所引用的版本是1.0.0.0,而Windows Live Writer所使用的是1.1.0.0,而且是平台是x86。這也解釋了為什麼第一次運行時提示試圖載入格式不正確的程式。

既然已經知道了問題,那解決起來就簡單了。這個時候需要用到程式集重新導向,在應用程式設定檔中指定程式集綁定,如:

運行結果

在VS中複製最前面那段代碼,運行程式,點擊測試按鈕:

運行結果如。RTF格式我瞭解不是太深入,不過這不影響接下來的操作。建立一個文字文件,將上部文字框中的文本複製到其中,並將其副檔名改為rtf。開啟該檔案,效果如:

從中可以發現,在產生HTML之前,複製出來的RTF已經不正確了。

再次運行結果

在寫字板中類比相應代碼,效果如:

在RTF中複製代碼,運行程式,點擊測試按鈕:

運行結果如。下面的HTML看起來不是很直觀,建立一個文字文件,將文字框中的文本複製到其中,並將其副檔名改為html。開啟該檔案,效果如:

從中可以發現,VSPaste並沒有出錯。

進一步尋找原因

從前面的實驗可以發現,VSPaste並沒有出錯,從VS中複製出來的代碼已經丟失了RTF格式資訊。那麼問題究竟會出現在哪裡?我以前在VS2015中使用VSPaste都沒有問題啊。這時候我有個猜想,如果VS沒有出問題,那麼很大可能就是哪個外掛程式坑爹了。

尋找問題外掛程式

在VS中選擇工具—>擴充和更新以開啟外掛程式列表,通過二分法來禁用外掛程式以查看問題是否解決(禁用後需要重啟VS)。很快,就找到了罪魁禍首,就是下面這貨:

尋找問題功能

我們找到問題外掛程式後可以就此為止了嗎?當然可以。但是對於自己來說,總是想打破砂鍋問到底。點擊中右邊的詳細資料,可以瞭解到更多的Productivity Power Tools 2015資訊。從中可以瞭解到它的各項功能,也知道了每項功能都可以進行開關。

在VS中選擇工具—>選項,並在表單左邊的樹狀控制項中選擇Productivity Power Tools。如:

在前面瞭解Productivity Power Tools的功能中,我就已經有懷疑的對象了,就是HTML Copy。嘗試將其關閉以查看問題是否解決(禁用後需要重啟VS)。經過實驗,發現果然是該項功能引發的問題。

還能不能進一步尋找問題根源?答案是可以。如果多留意一下Productivity Power Tools 2015的詳細資料,就會發現,在該頁面右上部分有一個到GitHub的連結。嗯,項目還是微軟的,不知為何為出現這種問題。

源碼調試下載代碼

從GitHub上下載Productivity Power Tools的代碼,由於其是一系列外掛程式的集合,下載後很快就找到了HTML Copy對應的項目。

尋找問題代碼

代碼不是太多,可以採取逐個檔案閱讀的方法。但是我已經知道癥狀了,就是複製出來的RTF資料不對,那麼不妨尋找代碼中使用了剪貼簿的地方。很快就找到了:

該函數只有一個引用,查看引用,可以找到:

再查看GenerateClipboardData的定義:


再繼續尋找,可以發現htmlBuilderService和rtfBuilderService都是通過MEF匯入的。


在產生html和rtf的代碼後面打一個斷點,開始調試。在新啟動並執行VS執行個體中開啟工程,複製代碼。在斷點處查看,可以發現產生的rtf已經丟失了格式資訊,而html仍然保留有格式資訊。

那麼這個rtfBuilderService究竟是何方神聖?在監看式視窗查看詳細資料:

實現自己的RtfBuilderService


前面已經知道了實現rtfBuilderService的類和所在程式集,在ILSpy中開啟該程式集並定位到類:

在工程中建立一個類,類名不能為RtfBuilderService,將ILSpy中的所有代碼複製出來放到該類中。將該類的匯出類型設為對應的類名,同時在匯入IRtfBuilderService的地方改為匯入對應的類。如下:

而更改類名的原因是為了避免MEF匯入匯出失效。如果失效會出現以下情況:

分析RtfBuilderService代碼

在我們實現的RtfBuilderService內部代碼中尋找GenerateRtf方法,發現其使用了如下方法:


再查看RtfBuilder內部的GenerateRtf方法


先查看GenerateBody方法,發現其主要是通過分析TextRunProperties的屬性來產生rtf的:


而文字屬性來源於GetClassificationSpans:

調試RtfBuilderService

在GenerateBody方法擷取TextRunProperties後面打一個斷點,開始調試。在新啟動並執行VS執行個體中開啟工程,複製代碼。在斷點處查看文字屬性的顏色,發現只進入一次斷點,且文本前景色彩為白色,背景色為黑色。

再次複製代碼,在監看式視窗處查看current的屬性資訊。其只有的ClassificationType和Span屬性。在Span屬性上可以看出它的內容彷彿是我們複製的代碼,嘗試看是否有擷取文本的方法,一查,還真有:

對照監看式視窗的各項值,可以發現,此時就已經丟失了所有的格式資訊。由於我們的ClassificationType為空白,所以始終返回的是預設文字屬性。

在GetClassificationSpans第一行打一個斷點,進行單步調試,可以發現一些資訊。調用該方法的cancel參數為空白,而GetClassificationSpans返回的列表中無條目,所以總會調用ClassificationType參數為空白的NullableClassificationSpan的建構函式。接下來根據呼叫堆疊一層一層的往上查看,發現在最開始在GenerateClipboardData方法調用GenerateRtf時就已經決定了cancel為空白。

這可怎麼辦,線索又斷了,真的是無路可走了嗎?不,我們還有一條路!這個工程不是有產生HTML的代碼麼,它不是沒丟失格式的嘛,去參考一下唄。

參考產生HTML的代碼

經過一番尋找,發現了在產生HTML代碼中與RtfBuilderService中GetClassificationSpans方法功能類似的代碼,連名字都一樣。

查看該代碼所調用的GetClassificationSpansSync方法:


發現這次調用GetAllClassificationSpans帶了一個CancellationToken的參數,而產生WaitContext的IWaitIndicator來源於MEF匯入。

怎麼樣,是不是山重水複疑無路,柳暗花明又一村?

完善RtfBuilderService

依照HTM產生部分依樣畫葫蘆,如,紅色部分表示新增代碼:

經過調試測試,發現產生的RTF已經包含正確的格式資訊了。

好了,問題已經解決了,我們到此為止了嗎?是否還可以做些其它什嗎?

另一種解決方案

讓我們再次回到RtfBuilderService,為什麼它會有那麼多的重載?


查看實現的介面,發現除了實現IRtfBuilderService外,還實現了IRtfBuilderService2。兩個介面的定義對比:


不難發現,IRtfBuilderService2是在IRtfBuilderService每個方法後面加上了一個CancellationToken重載。

所以,我們可以不用新增加類,直接將匯入的IRtfBuilderService類型改為IRtfBuilderService2,同時在產生rtf的地方傳入CancelToken以調用對應的介面方法。

注意事項

微軟建議的在調試外掛程式時需要Productivity Power Tools卸載了。我在研究問題時是卸載了的,但是在寫這邊部落格時沒有卸載,貌似也沒什麼問題。

另外,因為我是先研究的問題,後寫的部落格,可能有些細節忘記了或者沒有寫上,有心研究的話可以聯絡我。

結語

因為事情比較多,斷斷續續這麼久,終於把這篇部落格寫完了。之所以寫這麼多,主要是想分享下我解決問題的過程和思路。作為程式員,我個人覺得還是有一些探索精神好一些,很多時候,路不是想象的那麼難走。

最後,我給微軟提了個issue……

一次尋找Windows Live Writer的VSPaste外掛程式丟失RTF格式資訊的經曆

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.