`
pyjz34pyjz
  • 浏览: 12473 次
社区版块
存档分类
最新评论

Windows Live Writer插件开发经验

 
阅读更多

Windows Live Writer插件开发经验
2011年04月02日
  经常写博客的朋友也许经常使用Windows Live Writer,尤其对于有些程序员来说,时常辗转于CSDN、Windows Live、cnBlogs等众多社区,使用WLW对博客进行管理和更新会方便很多。我最近在写一些技术类博客文章的时候,时常需要插入代码,目前也有许多第三方的代码格式化插件,如"Insert Code"、"Code Snippet"等,这些插件能让代码插入后输出的HTML中产生语法高亮、行号、隔行阴影等特效,但是与CSDN本身的"插入代码"功能相比,这些插件输出的内容中会包含大量的样式,当代码量较大时,会造成文章大小急剧地膨胀。
  如果要解决内联样式造成的HTML膨胀问题,可以采用CSS+JavaScript的解决方案,如著名的的"Syntax Highlighter"项目,但是这需要博客系统和平台的支持,Wordpress与CSDN都是采用Syntax Hightlighter。因此,我在研究了几个WLW的插件和Syntax Hightlighter的接口后,决定自己开发一个利用CSDN本身的"插入代码"功能的插件。 微软已经在其MSDN上发布了Windows Live Writer的SDK,其中关于插件编写的部分可以参看:Windows Live Writer Plugin API和这篇博客http://www.cnblogs.com/yaoshiyou/archive/2009/11/2 8/1612746.html。
  WLW的插件可以分为三类: 发布通知插件:这类插件主要用于在博客文章发布前后调用,可以用来对内容的XHTML进行检查或者在某些条件下取消发布。 
  内容插件:这类插件用于让用户插入某种形式的内容,并对内容进行格式化处理,例如某些插件可以让用户选择插入本地的一张图片,然后生成缩略图上传并最终返回链接地址。 
  页眉页脚插件:这类插件主要用于自动在发布文章首尾添加一些内容,如签名、链接等等。 
  本次需求就是开发第二类插件,让用户输入源程序代码,生成符合CSDN样式的HTML文本。 WLW的插件开发的大致步骤如下: 创建一个新的.Net类库项目(framework 1.1以上)。 
  在解决方案资源管理器中右键单击该项目,选择"添加引用",然后选择"浏览"的选项,导入对WindowsLive.Writer.Api.dll的引用,此文件位于Window Live Writer执行文件所在文件夹下,如:C:\Program Files\Windows Live\Writer\。 
  创建一个类,继承自WindowsLive.Writer.Api.ContentSource(如果创建其他类型插件,可能继承自其他类,可以参看文档)。 
  对这个类应用WindowsLive.Writer.Api.WriterPluginAttribute属性,以指明插件的名称、图标及唯一ID等信息。 
  ContentSource有3个虚方法:CreateContent()、CreateContentFromLiveClipboard()、CreateContentFromUrl(),根据源内容的输入方式的不同,后续的步骤有一定的不同: 
  对话框输入:如果希望点击插件后,弹出一个对话框来让用户输入内容;这时需要为这个类应用WindowsLive.Writer.Api.InsertableContentSourceAttr ibute属性,然后重载 
  剪切板输入:这种模式是先将内容复制到剪切板中(准确的说应该是Live Clipboard,微软为Web开发一种数据交换技术,采用XML描述),然后点击插件,插件则直接读取剪切板中的内容作为自己的源;这时需要为这个类应用WindowsLive.Writer.Api.LiveClipboardContentSourceAttribute属性,然后重载CreateContentFromLiveClipboard()方法。 
  URL输入:根据WindowsLiveWriterApplication.BlogThisLink中的URL或者粘贴或拖入到编辑器中的URL作为数据源输入;这时需要为这个类应用WindowsLive.Writer.UrlContentSourceAttribute属性,然后重载CreateContentFromUrl()方法。 
  当然,有时候插件可能不需要任何输入源,只需当点击插件按钮后输出信息即可(如个人签名、当前日期等),则可以以上任选一种方法,在重载函数中忽略输入的内容即可。 
  重载方法后,编译成dll,然后将其放置到Window Live Writer安装目录下的Plugins目录下,重启WLW,即可以看见新的插件了,也可以在项目的生成后事件添加如下语句,自动复制到插件目录下进行调试: XCOPY /D /Y /R "$(TargetPath)" "C:\Program Files\Windows Live\Writer\Plugins\",调试时只能先启动WLW,然后手动附加到进程。 为了详解以上步骤中的几个难点,下面我列出一个最简单的插件示例。  1:Imports WindowsLive.Writer.Api 2:Imports System.Windows.Forms 3:Imports System.Text 4:   5: Windows Live Writer Plugin for CSDN", _ 8: ImagePath:="blog.bmp", _ 9: PublisherUrl:="http://blog.csdn.net/icefireelf")> _ 10:  _ 11:PublicClass PluginAdapter 12:Inherits ContentSource 13:PublicOverridesFunction CreateContent(ByVal dialogOwner As IWin32Window, ByRef content AsString) As System.Windows.Forms.DialogResult 14:Dim ret = MessageBox.Show("是否插入一条问候?", "问候语插件", MessageBoxButtons.OKCancel) 15:If (ret = DialogResult.OK) Then 16: content = "hello" 17:EndIf 18:Return ret 19:EndFunction 20:End Class
  WriterPlugin有2个必填属性: Id:即上面代码中的构造的第一个参数,此参数用于唯一标识一个插件,必须用GUID,使用项目属性中程序集的GUID即可。 
  Name:即上面代码中的构造的第二个参数,此参数即WLW的插件栏中显示的名称,建议不要太长。 
  此外还有几个可选属性: Description:字符串属性,此参数即WLW的插件详细信息中显示的内容,简单描述插件功能。 
  ImagePath: 图标路径,此参数决定WLW参见栏名称前的图标,如果不填则没有图标。图标须采用16*16的位图或PNG图,且必须作为嵌入的资源(将图标添加到工程中,在解决方案资源管理器中右键点击图标,选择"属性",然后将属性框中的"生成操作"设为"嵌入的资源"即可)。注意:如果图标放置在工程目录下,则直接填图标名称即可;如果放置在工程目录的子级目录下时,使用"."作为目录分隔符,例如:将图标放置在工程目录下的image\目录中,图标路径应写为"image.blog.bmp",而非"image\blog.bmp"。
  
  PublisherUrl:发表者的URL,如果设置了此属性,在WLW的插件信息中点击详细信息的文字链接时,会打开此URL。 
  
  HasEditableOptions: 详细信息里面是否有"选项"按钮(如下图所示),此参数默认为False,即不带"选项"按钮,当其设置为True时,则必须为本类重载EditOptions()方法,在该方法中,可以启动一个用于配置插件的窗体,获取参数,然后保存在配置文件或本类对象的成员中,共后续访问使用。 
  
  此属性本用于定义插件在WLW的"Insert"菜单和"Insert"快捷面板的名称,但新版的WLW已经去除了插入菜单与侧边栏,因此应用此属性只是为了保持向前兼容。 由上面的代码可以看出,每个内容插件都对应一个ContentSource派生类的对象。那么这个对象是如何维护的呢?对于每个Writer的进程而言,所有ContentSource实例都是单例的,即当插件对象创建后,以后每次点击插件,使用的都是同一个对象。所以,如果该对象拥有任何实例成员,则其在整个进程的上下文中都是有效的,可以用来存储一些关于插件的全局参数(例如Options属性)。
  ContentSource类继承自WriterPlugin类,因此也继承了WriterPlugin.Options属性与WriterPlugin.Initialize()方法。 WriterPlugin.Options是一个键值对集合对象,一般用于保存与查询插件的参数记录(如最后一次的配置)。 
  WriterPlugin.Initialize()方法在本对象初始化时调用,可以在子类中可以重载此方法,添加自己的初始化行为,但一定不要忘记住在重载方法中调用基类的Initialize()实现。 
  ContentContent方法是本插件的入口,在样例代码中,我们实现了一个小功能:每次点击插件,弹出一个对话框,询问用户是否插入问候语,如果用户点击确认,则输出"hello"的字符串,否则不输出。此接口的形式如下: public virtual DialogResult CreateContent( IWin32Window dialogOwner, ref string content ); 本方法有两个参数: dialogOwner:对话框的拥有者。 
  content:从ref可以看出这个参数是做为输出用,即最后输出编辑区的内容。 
  如果输出了内容,则返回DialogResult.OK,否则,返回DialogResult.Cancel。 注意:前面提到过,由于ContentSource对象是单例的,所以在实现本方法,不应该使用任何实例成员来保存临时变量,应做到可重入。 Syntax Highlight支持与两种元素,下面为两个例子: 由上可以看出,与的name属性必须为"code" ,而class属性里可以指定代码的语言类型,是否显示行号、是否显示控制条、是否折叠、其实行号等属性,具体如下: 语言项 目前支持的语言项有cpp、c-sharp、css、java、javascript、vb、sql、ruby、delphi、python、php、xhtml
  nogutter  如果添加此属性,将不显示行号。 
  nocontrols  如果添加此属性,将不会在代码块顶部显示控制器(包含折叠、打印、复制等命令)。 
  collapse  如果添加此属性,将默认折叠代码。 
  firstline[value]  如果添加此属性,行号从value开始计数,默认值是 1。 
  showcolumns  如果添加此属性,将在第一行显示列号。 
  其中代码内容需要用HtmlEncode进行编码,替换掉''等escape符号。如果查看过CSDN的"插入代码"输出的HTML源码,可以发现其采用的就是方案,而且到目前为止还存在bug,因为其没有对插入c++代码进行Html编码,所以当代码中有掉''时,会导致插入代码错误。 在了解了Syntax Highlighter的输出格式后,就可以设置ContentSource的输出方式了,我的设计是当点击插件后,弹出以下对话框:
  
  在其中选择好语言种类和各类配置后,在文本框中输入代码,点击插入后,就按照Syntax Highlighter的格式输出HTML文本,核心代码如下:  1:Imports WindowsLive.Writer.Api 2:Imports System.Windows.Forms 3:Imports System.Text 4:   5: Windows Live Writer Plugin for CSDN", _ 8: ImagePath:="blog.bmp", _ 9: PublisherUrl:="http://blog.csdn.net/icefireelf")> _ 10:  _ 11:PublicClass PluginAdapter 12:Inherits ContentSource 13:   14:PrivateSub SaveSettings(ByVal cf As CodeForm) 15: Options.SetBoolean("CSDN_EnableFolding", cf.EnableFolding) 16: Options.SetBoolean("CSDN_EnableLineNum", cf.EnableLineNum) 17: Options.SetBoolean("CSDN_EnableToolBar", cf.EnableToolBar) 18: Options.SetInt("CSDN_SelectedIndex", cf.SelectedIndex) 19:EndSub 20:   21:PrivateSub LoadSettings(ByVal cf As CodeForm) 22: cf.EnableFolding = Options.GetBoolean("CSDN_EnableFolding", False) 23: cf.EnableLineNum = Options.GetBoolean("CSDN_EnableLineNum", True) 24: cf.EnableToolBar = Options.GetBoolean("CSDN_EnableToolBar", True) 25:Dim index AsInteger = Options.GetInt("CSDN_SelectedIndex", -1) 26:If (index >= 0) Then 27: cf.SelectedIndex = index 28:EndIf 29:EndSub 30:   31:PublicFunction HtmlEncode(ByVal content AsString) AsString 32:Dim ret AsString = HtmlServices.HtmlEncode(content) 33:Return ret 34:EndFunction 35:   36:PublicOverridesFunction CreateContent(ByVal dialogOwner As IWin32Window, ByRef content AsString) As System.Windows.Forms.DialogResult 37:Try 38: Using cf AsNew CodeForm 39: LoadSettings(cf) 40:If (cf.ShowDialog(dialogOwner) = DialogResult.No OrElseString.IsNullOrEmpty(cf.GetCode)) Then 41:Return DialogResult.No 42:EndIf 43: SaveSettings(cf) 44: content = " 1) Then 54: content &= ":firstline[" & first.ToString & "]" 55:EndIf 56:EndIf 57:   58:If (Not cf.EnableToolBar) Then 59: content &= ":nocontrols" 60:EndIf 61: content &= """>" & HtmlEncode(cf.GetCode()) & "" 62:Return DialogResult.OK 63:End Using 64:Catch ex As Exception 65: MessageBox.Show(ex.Message) 66:Return DialogResult.No 67:EndTry 68:EndFunction 69:End Class
  通过代码可以看出,我选择了用标签,而没有采用CSDN的方案,这是因为Windows Live Writer目前存在一个Bug:会在编辑模式下自动将中的回车都删除,这就会导致所有代码被缩成一行(在CSDN网页上写好的代码用WLW下载下来后千万不要上传,因为它会修改的格式)。
  目前此工程的所有源代码已经上传至 http://download.csdn.net/source/3156349 ,有兴趣的朋友可以下载参考,或者直接编译后将生成的dll放入WLW的插件目录直接使用。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics