这半年一直在做Outlook的插件,因为不会VC++,所以想找一款基于.NET,用C#开发Outlook插件的技术方案。没想到,光技术选型这件事,就用各种技术手段验证了将近一个月,还花费了大量的精力做之后的各项开发工作。在此开个大坑,以此记录所有的技术成果,如果以后还有别的朋友要做Outlook,甚至于Office插件,都可以从这儿作为起步,因为Office插件的基础技术都是一致的,只是到了各个产品内部COM对象有特性差异。

好了,废话不多说,我们开始正文。

技术选型

如果提到要做Office插件,大家都会去网上找,当然最快得到的一个应该就是VSTO。虽然我们最后没有采用这个方案,但是我还是想在这儿稍微介绍一下它,因为它有自己非常强大的优点。

VSTO是Visual Studio Tools for Office的简称,在我看来,是和Visual Studio集成得最棒的插件开发SDK。如果你下载安装了VSTO,并且你使用Visual Studio的话,你会在创建项目的一开始就找到“Office插件”项目类型,之后有一系列的交互界面来引导你一步一步创建一个符合你要求的插件基础类型,甚至最后连界面都可以用所见即所得的方式进行设计——当然后台的代码也是完全面向对象的,你可以用最方便的方法来快速制作一个插件并进行打包部署。如果你的最终目标只是做一个为企业内部部署,并且你可以控制安装部署发布甚至面向特定Outlook版本的话,我强烈推荐你采用它,因为我一直奉行适用就是完美,不需要为了追求无意义的技术专精而采用其它方案。

但是,如果你像我一样,要做一款需要适用于各个操作系统(当然是XP以上,2000啥的咱就不提了),还需要支持所有Outlook版本(2003以上),还需要完全控制安装部署等一系列后续事宜,完全把这个插件当做一个产品为开发目标的话,VSTO这件看起来很美好的东西实际用起来不一定那么美好,原因有以下几点:

  1. 无法控制的面向对象版本——VSTO是针对固定Office版本的,如果你下载了2013版的,那很可能无法兼容2003。
  2. 无法适应多版本——在使用VSTO的时候,是无法针对各个版本来定制界面的。大家知道从Office 2010开始,菜单就采用了Ribbon样式,而2007以下还都是经典菜单栏。事实上,你无法在使用VSTO的情况下来为这些版本定制不同的界面,这非常复杂。
  3. 无法控制的发布——众所周知,你一开始用了MS的东西,后面不用都不行,简直就是一个黑洞。如果你用了VSTO作为开发框架,那打包很可能就是InstallShield(现在最新版的VS里面已经没有Installation Project了,而且InstallShield是收费的)。而这东西你完全不知道如何才能控制它的行为,让它在x86和x64上都能成功部署插件。要知道,Office插件是需要写注册表键值才能成功安装的。

好了,既然用现成的东西走不通,我们就索性找找开源免费的东西吧。幸好,我们找到了最后的技术方案(中间经历的痛苦和大量技术验证工作真是无法用文字形容),NetOffice和Wix。

基础框架

NetOffice是一套开源的,基于.NET的Office插件开发框架,大家可以访问下面的链接来下载它。

http://netoffice.codeplex.com/

NetOffice支持多个.NET版本,从2.0到4.5都有,因此已经涵盖我们现有的所有.NET版本。

我们最后的选型是3.5,这里面也是经过慎重推敲的。3.5版本的.NET Framework是从Windows 7开始支持的,对于Windows 7以上版本的操作系统,都可以不需要安装.Net Framework就可以运行。但是有一点只有是做.NET的人都知道,就是3.5安装包的尺寸非常大,难道要用户下载安装这么大的基础库包吗?我们的解决方案是,发布的时候,不带.NET Framework。那如果是XP用户怎么办?我们的安装包自动下载安装4.0版本.NET Framework。我们就用这种方法成功解决了兼容性和下载精简性的两难问题。再说了,现在微软马上就不再支持XP的持续更新了,XP的生命说实话真的进入倒计时了,我们也不要再在这样的系统上花费更多精力了。

Wix在我另外的博文里面也有介绍,是一款非常好用的打包安装框架。当然之前我的文章里面对它的介绍太为肤浅,这次,我们将它翻了一个底朝天。现在,我们已经可以做到这样的安装了。在这次的技术介绍中,我也会对Wix的高级使用开发进行详细讲解。应该可以涵盖几乎所有的打包安装Case,方便大家以后更高效得制作更优秀的安装包。下面的截图我进行了一些打码处理,因为毕竟涉及商业问题,请各位见谅。

创建工程环境

有了以上的框架技术基础,我们就可以来创建我们的第一个Outlook插件项目了,因为我现在用的是Office 2013,我们先做一个简单的Outlook 2013插件好了。其实这个插件2010也能用,因为插件UI是用Ribbon。

我们先用“管理员身份”运行Visual Studio。因为我们开发的是一个COM类型的类库,而这个类库需要向注册表注入一些键值,VS需要管理员身份才能自动完成这些操作。你问怎么用管理员身份运行?在图标上右键点击,展开的菜单里面就有。

然后我们创建一个“类库”项目,我们起个项目名称叫“TestOutlookAddin”,记得.NET的版本是3.5。随后我们添加NetOffice相应3.5版本的以下dll文件引用:

extensibility.dll

NetOffice.dll

OfficeApi.dll

OutlookApi.dll

我们再调整解决方案的编译环境,必须要有x86和x64两种。因为插件是对Office版本敏感的,x64的Office只能加载x64编译得到的插件dll,同样的,x86版本Office也只能加载x86版本dll。

我们还要对项目进行属性设置。

在“应用程序”标签页中,点击“程序集信息…”,在打开的对话框中勾选“使程序集COM可见”。

在“生成”标签页中,勾选“为COM互操作注册”。

在“调试”标签页中,将“启动操作”勾选“启动外部程序”,并且填入你机器上的Outlook.exe启动路径,在我的机器上,路径如下:

C:\Program Files\Microsoft Office\Office15\OUTLOOK.EXE

修改完这个以后,我们先进入C:\Program Files\Microsoft Office\Office15\,也就是Outlook.exe的所在文件夹,看看有没有outlook.exe.config文件。如果没有这个文件,请创建这个文件,并在文件里面写入以下XML

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v1.0.3705" />
<supportedRuntime version="v1.1.4322" />
<supportedRuntime version="v2.0.50727" />
</startup>
</configuration>

这个文件必须和Outlook.exe在同一个文件夹中。如果没有这个文件,我们将无法进入调试状态。我想没有人能在不调试的情况下编写一整个插件吧?

编写样例代码

我们把环境的装备工作做好了,下面就来编写最简单的插件。

我们先在项目中创建一个RibbonUI.xml,并把它的生成操作定义为“嵌入的资源”,内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<customUI onLoad="LoadAction" xmlns="http://schemas.microsoft.com/office/2006/01/customui" >
<ribbon>
<tabs>
<tab id="RibbonAddinSampleTabCS35" label="插件标签">
<group id="group1" label="分组名">
<button id="customButton1" size="large" label="按钮"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>

对于Outlook 2007以上的版本来说,这个文件就是定义新增的菜单栏界面的。

然后再在项目中创建一个名为COMEntry的类,代码如下:

using System.Runtime.InteropServices;
using NetOffice.OutlookApi.Tools;
using NetOffice.Tools; namespace TestOutlookAddin
{
[COMAddin("Test Addin For Outlook", "", ), CustomUI("TestOutlookAddin.RibbonUI.xml"), RegistryLocation(RegistrySaveLocation.CurrentUser)]
[Guid("AFE67651-951D-4A42-8CAB-E9BF7E219DDF"), ProgId("TestAddinForOutlook")]
public class COMEntry : COMAddin
{
}
}

其中COMAddin特性类声明了一系列需要加载和插件初始化的信息,它的参数有“插件在Outlook插件列表中的显示名称”,“插件在列表中的描述”,“启动类型(3代表跟随Outlook启动而自动启动)”。

CustomUI表示了刚才我们定义的RibbonUI.xml作为嵌入的资源的资源访问路径,这点对于有一定经验的.NET开发人员一定不陌生。

RegistryLocation是定义了插件在注册表中注册到哪个根键值里面去。我们知道现在Windows安装文件都有“仅为我”和“所有人”安装选项,这个特性也是为了区分它的。不过这个仅仅是在调试状态下,因为发布的时候这个键值还是会由我们的安装包来自行控制,所以不需要在这个地方做过多纠结。

好了,我们在VS按下F5调试查看一下效果吧,如下图

我们得到了一个新的标签,标签内部有个新的分组和空白的“按钮”,鼠标移到按钮上,就会自动出现下面的弹出说明框。

在此,需要先进行一个声明,下方的这个弹出说明框是可以自定义标题和内容的,但是最下方的插件名称超链接和“详细信息”是无法去掉的。微软官方的解释是:为了区分这个到底是一个第三方插件还是Outlook自身自带的按钮。因为他们担心一些质量很差的插件影响了Outlook自身的行为,用户还要怪罪到微软头上。因此他们就用这种方法来明显区别插件和自身控件了。

现在我们已经得到了最初始版本的插件,当然现在什么事情都不能做。在下一篇中,我们将谈谈如何定位插件的部署错误,以及对插件进行更多的自定义操作。

如何使用.NET开发全版本支持的Outlook插件产品(一)——准备工作的更多相关文章

  1. 如何使用.NET开发全版本支持的Outlook插件产品(三)——全面控制

    插件项目所有代码都已经上传至 https://github.com/VanPan/TestOutlookAdding 进阶基础--COM查看 首先,对于Outlook对象模型,MSDN早就有非常详细的 ...

  2. 如何使用.NET开发全版本支持的Outlook插件产品(二)——完善插件

    插件项目所有代码都已经上传至 https://github.com/VanPan/TestOutlookAdding 勿在浮砂筑高台--定位错误 在介绍后面的插件开发技术之前,让我们先来看看已经达到的 ...

  3. 如何使用.NET开发全版本支持的Outlook插件产品(四)——进阶探讨

    插件项目所有代码都已经上传至 https://github.com/VanPan/TestOutlookAdding 如何定制Ribbon在不同界面的显示 实际使用过程中出现的问题 这个问题的来自十分 ...

  4. mac 下基于firebreath 开发多浏览器支持的浏览器插件

    mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...

  5. iOS:iOS开发非常全的三方库、插件等等

    iOS开发非常全的三方库.插件等等 github排名:https://github.com/trending, github搜索:https://github.com/search. 此文章转自git ...

  6. 偏执的iOS逆向研究员:收集全版本的macOS iOS+越狱+内核调试

    Intro 虽然“只有偏执狂才能够生存”这句话已经被假药停给毁了,但是作为一只有逼格的高大上的iOS逆向分析研究员,难道如果有现成的macOS/iOS全版本镜像可以下载并且无限“漫游”,难道你就不想来 ...

  7. 新成员!Visual Studio Code --跨平台的开发工具(支持OSX, Linux 和 Windows)

    原文出处:新成员!Visual Studio Code --跨平台的开发工具(支持OSX, Linux 和 Windows) 这是我的文章备份  http://www.dotblogs.com.tw/ ...

  8. [EXP]IIS全版本提权工具

    工具: iislpe.exe 编译: .net 3.5 全版本IIS提权工具,支持IIS应用池用户/网络服务用户/本地服务用户 原理:       通过NTLM重放将权限提升至SYSTEM权限,详情参 ...

  9. Windows全版本KMS激活脚本

    搭建了个KMS服务器,制作了个批处理激活脚本,所有代码可以看到,让你再也不用担心系统会被有些激活工具强改主页,留有后门的风险了. 本脚本可以激活Windows全版本,安全.绿色. 1.首先你的系统必须 ...

随机推荐

  1. 安装centos时候自动安装vm tool,导致无法继续安装centos的解决办法

    我原先安装centos 的时候装的是CD版的,也是到这一步就卡住了,然后我在"虚拟机->取消安装vmare tool" 点击“取消安装vmare tool”,然后他就可以进行 ...

  2. JAVA 1.5 运算符

    1. 关系运算符:大于(>).小于(<).等于(==).不等于(!=).大于等于(>=).小于等于(<=),关系运算的结果是个boolean值.2. 逻辑运算符:重点讲解两个, ...

  3. 《C++ Primer》学习笔记【第二部分 C++标准库】

    第8章 IO库 IO对象不能复制,即1.IO对象不能存储在vector或其他容器中   2.如果需要传递或返回IO对象,必须传递或返回指向该对象的指针或引用. 一般情况下,如果要传递IO对象以便对它进 ...

  4. 02.JavaScript 面向对象精要--函数

    在JavaScript中,函数其实也是对象.是函数不同于其他对象的特点是:函数存在一个被称为[[Call]]的内部属性.[[Call]]属性是函数独有的,表明该对象可以被执行.ECMAScript 定 ...

  5. LogConfigruration

    import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import ...

  6. offsetwidth/clientwidth的区别

    clientWidth是对象看到的宽度(不含边线,即border)scrollWidth是对象实际内容的宽度(若无padding,那就是边框之间距离,如有padding,就是左padding和右pad ...

  7. [sqoop1.99.7] sqoop实例——数据ETL

    一.创建一个mysql的link MySQL链接使用的是JDBC,必须有对应的驱动文件jar,还得有对应的访问权限,请确保能在server端访问MySQL.确保mysql的jar包已经导入到${SQO ...

  8. BIND的进阶二:视图,日志,转发,子域的授权

    实验分为4部分组成: 1:DNS的转发   2:DNS日志     3:子域的授权 4:智能DNS的简单配置根据网段来分配不同的ip地址 一:DNS的转发: 转发方式有两种:only (直接把客户端请 ...

  9. html5对密码加密

    今天找了几个关于对html5的密码加密的方法,仅供参考 1.base64加密:在页面中引入base64.js文件,调用方法为: <html> <head> <meta c ...

  10. php的clone 浅拷贝

    总所周知 php 的 clone方法 拷贝一个对象 而且还是所谓的浅拷贝 一时迷茫 今天终于整明白了 <?php class a { pulic $data; function __constr ...