通过Visual Studio向导生成Management 插件框架就不说了,网上能搜到不少资料。本篇重点是说明怎么设计一个插件安装包,适用于Management Studio 2005 到2014的版本。

先讲明这么做要面临的几个难点:

1、SSMS 2008 和 SSMS 2008 R2的安装包注册表项名称一样,但只能放一个。也就是,如果只放SSMS 2008的注册表项,SSMS 2008 R2 启动的时候会尝试读取,并报错,错误本质上是SSMS 2008 R2程序集和SSMS 2008 不同 。同理,如果只是单独放SSMS 2008 R2的注册表项,在SSMS 2008的机器上也会报错。

2、SSMS 2012 和 SSMS 2014查找插件的方法不再通过注册表了,而是通过目录上特定的文件来查找的,这个是个关键,如果你不知道该把向导生成的*.addin放哪儿,SSMS 就不能正常加载。

3、要同时兼容.NET Framework 2.0 - 4.0 并具有Windows Installer安装项目的只有Visual Studio 2010,必须采用VS 2010来编写。

下面,进入正题。

先拿最简单的SSMS 2005来说,对应于ProjkyAddin项目的ForYukon项目。

项目ForYukon中,编译后程序集文件名称是“Projky.ForYukon.dll”,插件加载时必须指定一个实现“IDTExtensibility2”的类,这在向导中就已经生成好了的,这里是“Projky.ForYukon.Connect”类。

那么,接下来就是让“Projky.ForYukon”插件项目,在SSMS 2005中正常加载,步骤如下。

同一解决方案中,添加一个名为“Projky.Setup”的安装项目,在Setup项目上右键-〉“添加”-〉“项目输出”,从下拉列表中选择“Projky.ForYukon”,然后会看到Setup项目下包含一“主输出来自Projky.ForYukon(活动)”的项。到这里还不够,选中该主输出项,右键-〉“属性”,将“Register”项设置为“vsdrpCOM”。最关键的是下一步,设置注册表项。在Setup项目上,右键-〉“视图”-〉“注册表”。经过本人多次测试,依下图的方式设置,兼容性最好。

特别说明的是,注册项位于LocalMachine根下,里面的“Addins”下项“Projky.ForYukon.Connect”是“Projky.ForYukon.dll”中实现了“IDTExtensibility2”接口的类全名。

如果只是单独需要SSMS 2008 或 2008R2中的一个插件的话,可依上一步的操作即可。我们的目标是让它们同时有效,而且加载时互不干扰。

针对SSMS 2008的插件项目名称是“Projky.ForKatmai”,SSMS 2008R2的插件项目名称是“Projky.ForKilimanjaro”,这里我们的诀窍是采用一个插件中间项目“Projky.ForSQL08Transfer”,在Setup的项目中注册表项里面只保留Transfer的注册表项。然后,在Transfer项目中同时引用“ForKatmai”和“ForKilimanjaro”项目,在Transfer插件启动过程中,根据SSMS版本,决定实例化那个一个具体的“IDTExtensibility2”实例。关键代码如下:

using System;
using Extensibility;
using EnvDTE;
using EnvDTE80; namespace Projky.ForSQL08Transfer { public class Connect : IDTExtensibility2, IDTCommandTarget {
public Connect() {
}
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) {
_addInInstance = (AddIn)addInInst;
_applicationObject = (DTE2)_addInInstance.DTE; if (_applicationObject.Version.StartsWith("")) {
_transfer = new Projky.ForKatmai.Connect();
} else {
_transfer = new Projky.ForKilimanjaro.Connect();
} _transfer.OnConnection(application, connectMode, addInInst, ref custom);
} public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) {
if (_transfer != null) {
_transfer.OnDisconnection(disconnectMode, ref custom);
}
} public void OnAddInsUpdate(ref Array custom) {
if (_transfer != null) {
_transfer.OnAddInsUpdate(ref custom);
}
} public void OnStartupComplete(ref Array custom) {
if (_transfer != null) {
_transfer.OnStartupComplete(ref custom);
}
} public void OnBeginShutdown(ref Array custom) {
if (_transfer != null) {
_transfer.OnBeginShutdown(ref custom);
}
} public void Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled) {
var target = _transfer as IDTCommandTarget;
if (target != null) {
target.Exec(CmdName, ExecuteOption, ref VariantIn, ref VariantOut, ref Handled);
}
} public void QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText) {
var target = _transfer as IDTCommandTarget;
if (target != null) {
target.QueryStatus(CmdName, NeededText, ref StatusOption, ref CommandText);
}
} DTE2 _applicationObject;
AddIn _addInInstance;
IDTExtensibility2 _transfer = null;
}
}

重点是根据SSMS版本号实例化一个“IDTExtensibility2”对象,再将接口中各项调用转发到_transfer的对应实现上。

设置后效果图:

特别说明,对于“Transfer”、“ForKatmai”、“ForKilimanjaro”项目,在Setup项目中都要添加主输出,并在各自的主输出项上右键-〉属性,设置“Register”项为“vsdrpCOM”。SSMS 2012 和 SSMS 2014也一样,就不强调了。

解决了第一个问题和第三个问题,只剩下第二个问题了,就是实现SSMS 2012 和SSMS 2014插件的加载。

这个方法是在“RedGate”插件上应用的,就是将向导生成的附带Addin结尾的文件放到特定目录。SSMS 2012放到Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\SQL Server Management Studio\11.0\Addins"目录下,SSMS 2014放到Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\SQL Server Management Studio\12.0\Addins"目录下。

然而,这不是仅仅复制这两个文件到对应目录就完成的事情。通常Addin文件内容如下:

<?xml version="1.0" encoding="gb2312" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
<HostApplication>
<Name>Microsoft SQL Server Management Studio</Name>
<Version>*</Version>
</HostApplication>
<Addin>
<FriendlyName>ProjkyAddin</FriendlyName>
<Description>ProjkyAddin for sql 2012</Description>
<Assembly>C:\Program Files (x86)\ProjkyAddin\Projky.ForDenali.dll</Assembly>
<FullClassName>Projky.ForDenali.Connect</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>

注意了,里面Assembly中是包含了路径的,如果用户在Setup包安装过程中选择了其它目录,不是默认目录,那就导致不能正常加载。

综上,需要一个定制化的安装过程。在.NET 里面就是实现System.Configuration.Install.Installer类(需要引用System.Configuration程序集),再在Setup项目中指定自定义动作,指向该类。

ProjkyAddin中的实现代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Text; namespace Projky.InstallHelper {
[RunInstaller(true)]
public partial class SetupAddinFile : System.Configuration.Install.Installer {
#region Installer public SetupAddinFile() {
InitializeComponent();
} protected override void OnAfterUninstall(IDictionary savedState) {
RemoveDenaliAddinFile();
RemoveHekatonAddinFile();
base.OnAfterUninstall(savedState);
} public override void Rollback(IDictionary savedState) {
RemoveDenaliAddinFile();
RemoveHekatonAddinFile();
base.Rollback(savedState);
} public override void Install(IDictionary stateSaver) {
base.Install(stateSaver);
SetupDenaliAddinFile();
SetupHekatonAddinFile();
CreateProjkyAddinFolder();
CreateScriptFoldersTextFile();
} #endregion void CreateScriptFoldersTextFile() {
string filePath = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), "scriptfolders.txt");
if (File.Exists(filePath) == false) {
File.WriteAllText(filePath,
@"// put you script folder here,single folder single row. example as:
C:\Users\signal\Documents\ProjkyTwo", Encoding.UTF8);
}
} void CreateProjkyAddinFolder() {
string projkyFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ProjkyAddin";
if (Directory.Exists(projkyFolder) == false) {
Directory.CreateDirectory(projkyFolder);
} for (int i = ; i < ; i++){
string filePath = Path.Combine(projkyFolder, String.Format("{0}.sql", i));
string content = String.Format("SELECT {0};", i);
if (File.Exists(filePath) == false) {
File.WriteAllText(filePath, content, Encoding.UTF8);
}
}
} void SetupDenaliAddinFile() {
string fileContent;
string denaliFilePath; fileContent = @"<?xml version=""1.0"" encoding=""{0}"" standalone=""no""?>
<Extensibility xmlns=""http://schemas.microsoft.com/AutomationExtensibility"">
<HostApplication>
<Name>Microsoft SQL Server Management Studio</Name>
<Version>*</Version>
</HostApplication>
<Addin>
<FriendlyName>ProjkyAddin</FriendlyName>
<Description>ProjkyAddin for sql 2012</Description>
<Assembly>{1}\Projky.ForDenali.dll</Assembly>
<FullClassName>Projky.ForDenali.Connect</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>"; fileContent = string.Format(fileContent, Encoding.Default.BodyName, Path.GetDirectoryName(this.GetType().Assembly.Location));
denaliFilePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\SQL Server Management Studio\11.0\Addins";
Directory.CreateDirectory(denaliFilePath); using (StreamWriter sw = new StreamWriter(denaliFilePath + "\\Projky.ForDenali.AddIn", false)) {
sw.Write(fileContent);
}
} void RemoveDenaliAddinFile() {
string denaliFilePath; denaliFilePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) +
@"\Microsoft\SQL Server Management Studio\11.0\Addins" +
"\\Projky.ForDenali.AddIn";
if (File.Exists(denaliFilePath)) {
File.Delete(denaliFilePath);
}
} void SetupHekatonAddinFile() {
string fileContent;
string hekatonFilePath; fileContent = @"<?xml version=""1.0"" encoding=""{0}"" standalone=""no""?>
<Extensibility xmlns=""http://schemas.microsoft.com/AutomationExtensibility"">
<HostApplication>
<Name>Microsoft SQL Server Management Studio</Name>
<Version>*</Version>
</HostApplication>
<Addin>
<FriendlyName>ProjkyAddin</FriendlyName>
<Description>ProjkyAddin for sql 2014</Description>
<Assembly>{1}\Projky.ForHekaton.dll</Assembly>
<FullClassName>Projky.ForHekaton.Connect</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>"; fileContent = string.Format(fileContent, Encoding.Default.BodyName, Path.GetDirectoryName(this.GetType().Assembly.Location));
hekatonFilePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\Microsoft\SQL Server Management Studio\12.0\Addins";
Directory.CreateDirectory(hekatonFilePath); using (StreamWriter sw = new StreamWriter(hekatonFilePath + "\\Projky.ForHekaton.AddIn", false)) {
sw.Write(fileContent);
}
} void RemoveHekatonAddinFile() {
string hekatonFilePath; hekatonFilePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) +
@"\Microsoft\SQL Server Management Studio\12.0\Addins" +
"\\Projky.ForHekaton.AddIn";
if (File.Exists(hekatonFilePath)) {
File.Delete(hekatonFilePath);
}
}
}
}

该代码是放在“Projky.InstallHelper”项目中的。

添加自定义操作,需要在Setup项目上右键-〉“视图”-〉“自定义操作”,将里面的安装、提交、回滚、卸载,都添加上“InstallHelper”的主输出。效果如下图:

不过呢,假如将来SSMS 插件需要引用.NET 4.5程序集,VS 2010是不能用,留待以后解决。

ProjkyAddin示例代码参见http://www.cnblogs.com/ProJKY/p/ProjkyAddin.html

Management Studio 插件生成安装包要点(以ProjkyAddin为例)的更多相关文章

  1. 开源一款私藏Management Studio插件,ProjkyAddin,送给所有使用SQLServer的园友们

    ProjkyAddin 是一款Management Studio 插件,安装包才500多kb,兼容SSMS 2005.SSMS 2008.SSMS 2008 R2.SSMS 2012.SSMS 201 ...

  2. Qt5.4生成安装包过程

    所需工具: 1.  HM NIS Edit 2.  windeployqt.exe 第一个工具需要自己去网上下载,第二个工具可以在qt安装目录下找到:D:\qtopengl\5.4\mingw491_ ...

  3. VS2010生成安装包制作步骤

    VS2010生成安装包制作步骤   在VS2010中文旗舰版本中生成winForm安装包,可以复制你电脑中的开发环境,避免你忘记了一下配置然后在别的机器上运行不起来.也省去了Framwork的安装. ...

  4. VS2010生成安装包制作步骤 (转)

    阅读目录 VS2010生成安装包制作步骤 回到目录 VS2010生成安装包制作步骤   在VS2010中文旗舰版本中生成winForm安装包,可以复制你电脑中的开发环境,避免你忘记了一下配置然后在别的 ...

  5. UWP项目生成安装包远程安装在树莓派上

    原文: UWP项目生成安装包远程安装在树莓派上 哎,好纠结啊!如果这个名字写的太长,会显得太繁琐,如果写的短又好像说不清楚,我这语言表达水平实在是令人担忧啊!不过应该能够明白啥意思吧!因为对这个感兴趣 ...

  6. VISUAL STUDIO 2008 WINDOWS FORM项目发布生成安装包详解(转)

    转自:http://www.cnblogs.com/killerofyang/archive/2012/05/31/2529193.html Visual Studio 2008 Windows Fo ...

  7. VS2010生成安装包

    项目的第一个版本出来了,要做个安装包,之前没有做过,网上看看贴,写了一个,总结下,根据本项目的需要,没有写的太复杂,可能还不是很完善,仅作参考. 首先在打开 VS2010    >   文件 & ...

  8. Flink打包生成安装包缺少jar包

    官方默认打包生成的安装包的flink-release-1.7.0\flink-dist\target\flink-1.7.0-bin\flink-1.7.0\lib下缺少jar flink-dist项 ...

  9. cxfreeze打包python程序的方法说明(生成安装包,实现桌面快捷方式、删除快捷方式)

    一.cxfreeze基础 1.cxfreeze功能 python代码文件转exe方法有三种,分别是cx_freeze,py2exe,PyInstaller,这三种方式各有千秋,本人只用过py2exe和 ...

随机推荐

  1. invokespecial与invokevirtual指令的区别

    package com.test19; class Father { public void publicMethod() { privateMethod(); // this是Son对象,调用Fat ...

  2. Chapter 3 Phenomenon——6

    A low oath made me aware that someone was with me, and the voice was impossible not to recognize. 某人 ...

  3. kubernetes ingress到pod的数据流

    假设现在有一个ingress暴露的服务 example.com.cn,查看一下流量是怎么传输到后端的 使用kubectl get ingress可以查看到如下内容,example.com.cn对应的i ...

  4. lucene源码分析(3)facet实例

    简单的facet实例 public class SimpleFacetsExample { private final Directory indexDir = new RAMDirectory(); ...

  5. js加载事件和js函数定义

    一  dom文档树加载完之后执行一个函数 在Dom加载完成后执行函数,下面这三个的作用是一样的,window.onload 是JavaScript的,window.onload是在dom文档树加载完和 ...

  6. 双11抢券,写一个自动打开页面的html,仅仅是设定时间打开抢券的页面

    <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  7. [javaSE] 单例设计模式

    四人帮设计了23中设计模式 单例设计模式:解决一个类在内存中只存在一个对象 构造函数私有化 在类中创建一个本类对象 提供一个方法可以获取该对象 class Single{ private static ...

  8. java利器------反射机制

    java反射的概念:java的反射机制是指在运行状态下,对于一个类来说,可以得到这个类的所有方法和属性.对于一个对象来说,可以调用这个对象的人和方法和属性. 反射机制首先会拿到该类的字节码文件(Cla ...

  9. Session和Cookie的区别与联系

    一. 概念理解 你可能有留意到当你浏览网页时,会有一些推送消息,大多数是你最近留意过的同类东西,比如你想买桌子,上淘宝搜了一下,结果连着几天会有各种各样的桌子的链接.这是因为 你浏览某个网页的时候,W ...

  10. scala 编程思想—学习笔记

    方法 方法是打包在某个名字下的小程序.在使用方法时,也就是调用方法时就会执行 这个小程序.方法将一组活动组合起来并赋予一个名字,这就是组织程序的最基本方式. scala 中方法的基本形式为 def m ...