VsSingleFileGenerator插件创建,安装和使用

    相信很多用过VS朋友都会发现一个问题,就是当编写一个XML或一个资源文件的时候VS会为该文件同时生成个类文件;其实我们也可以针对某种文件来实现自定义的生成效果,这种插件就是VS提供给相关文件的自定义工具功能(Custom Tools);下面将为大家介绍如何构造一个VsSingleFileGenerator插件,制作安装发布和在VS中使用.

编写插件

首先要知道的编写这样一个插件需要什么,其实这东西在Google一搜应该就有不少资料和相关sample,直接拿下来看下基本知道原来.编写这样一个插件所需要用到的是一个接口IVsSingleFileGenerator,对于这个接口的详细介绍可以通过MSDN了解.下面看一下实现这接口后的对象代码是怎样的

View Code
  1 /// <summary>
2 ///
3 /// </summary>
4 [Guid("F9135C1D-E958-485b-95B9-0233E63930ED")]
5 public class InterfaceToModel : IVsSingleFileGenerator, VSOLE::IObjectWithSite
6 {
7 private CodeDomProvider codeProvider;
8
9 private string codeFileNameSpace;
10 private string codeFilePath;
11
12 private object site;
13
14 private IVsGeneratorProgress codeGeneratorProgress;
15
16 public CodeDomProvider CodeProvider
17 {
18 get
19 {
20 if (this.codeProvider == null)
21 {
22 codeProvider = CodeDomProvider.CreateProvider("C#");
23 }
24
25 return this.codeProvider;
26 }
27
28 set
29 {
30 if (value == null)
31 {
32 throw new ArgumentNullException();
33 }
34
35 this.codeProvider = value;
36 }
37 }
38
39 #region IVsSingleFileGenerator Members
40
41 public int DefaultExtension(out string ext)
42 {
43 string defExt;
44 ext = string.Empty;
45
46 defExt = this.CodeProvider.FileExtension;
47
48 if (((defExt != null) && (defExt.Length > 0)) && (defExt[0] != '.'))
49 {
50 defExt = "." + defExt;
51 }
52
53 if (!string.IsNullOrEmpty(defExt))
54 {
55 ext = ".Model" + defExt;
56 }
57
58 return 0;
59 }
60
61 public int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] pbstrOutputFileContents, out uint pbstrOutputFileContentSize, IVsGeneratorProgress pGenerateProgress)
62 {
63 if (bstrInputFileContents == null)
64 {
65 throw new ArgumentNullException(bstrInputFileContents);
66 }
67
68 this.codeFilePath = wszInputFilePath;
69 this.codeFileNameSpace = wszDefaultNamespace;
70 this.codeGeneratorProgress = pGenerateProgress;
71
72 byte[] generatedStuff = this.GenerateCode(wszInputFilePath, bstrInputFileContents);
73
74 if (generatedStuff == null)
75 {
76 pbstrOutputFileContents[0] = IntPtr.Zero;
77 pbstrOutputFileContentSize = 0;
78 }
79 else
80 {
81 pbstrOutputFileContents[0] = Marshal.AllocCoTaskMem(generatedStuff.Length);
82 Marshal.Copy(generatedStuff, 0, pbstrOutputFileContents[0], generatedStuff.Length);
83 pbstrOutputFileContentSize = (uint)generatedStuff.Length;
84 }
85 return 0;
86 }
87 #endregion
88
89 #region IObjectWithSite Members
90
91 public void GetSite(ref Guid riid, out IntPtr ppvSite)
92 {
93 if (this.site == null)
94 {
95 throw new Win32Exception(-2147467259);
96 }
97
98 IntPtr objectPointer = Marshal.GetIUnknownForObject(this.site);
99
100 try
101 {
102 Marshal.QueryInterface(objectPointer, ref riid, out ppvSite);
103 if (ppvSite == IntPtr.Zero)
104 {
105 throw new Win32Exception(-2147467262);
106 }
107 }
108 finally
109 {
110 if (objectPointer != IntPtr.Zero)
111 {
112 Marshal.Release(objectPointer);
113 objectPointer = IntPtr.Zero;
114 }
115 }
116 }
117
118 public void SetSite(object pUnkSite)
119 {
120 this.site = pUnkSite;
121 this.codeProvider = null;
122 }
123
124 #endregion
125
126 #region Private Methods
127 protected byte[] GenerateCode(string inputFileName, string inputFileContent)
128 {
129 using (System.IO.FileStream stream =System.IO.File.Open(inputFileName, FileMode.Open, FileAccess.Read))
130 {
131 Smark.Data.InterfaceToModelGenerator.GenerateCode gc
132 = new GenerateCode();
133 return StreamToBytes(gc.Builder(stream));
134 }
135
136 }
137
138 protected byte[] StreamToBytes(Stream stream)
139 {
140 if (stream.Length == 0)
141 {
142 return new byte[0];
143 }
144
145 long pos = stream.Position;
146
147 stream.Position = 0;
148
149 byte[] buffer = new byte[(int)stream.Length];
150 stream.Read(buffer, 0, buffer.Length);
151
152 stream.Position = pos;
153 return buffer;
154 }
155
156 private void ThrowOnFailure(int hr)
157 {
158 if ((hr < 0))
159 {
160 Marshal.ThrowExceptionForHR(hr);
161 }
162 }
163 #endregion
164 }

其实整个类的实现都比较简单,主要有两个方法是需要我们去关注分别是:

 public int DefaultExtension(out string ext)

这个方法主要是描述生成文件的默认后缀扩展名

public int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] pbstrOutputFileContents, out uint pbstrOutputFileContentSize, IVsGeneratorProgress pGenerateProgress)

 这个方法比重要,主要是获取当前文件路径,名称空间,输出文件内容等相关信息;并根据根据这些信息按实际需要生成相关文件内容输出给相关参数.

byte[] generatedStuff = this.GenerateCode(wszInputFilePath, bstrInputFileContents);

if (generatedStuff == null)
{
pbstrOutputFileContents[0] = IntPtr.Zero;
pbstrOutputFileContentSize = 0;
}
else
{
pbstrOutputFileContents[0] = Marshal.AllocCoTaskMem(generatedStuff.Length);
Marshal.Copy(generatedStuff, 0, pbstrOutputFileContents[0], generatedStuff.Length);
pbstrOutputFileContentSize = (uint)generatedStuff.Length;
}
return 0;
 从上面代码可以看到,Generate方法内部所做的事情就是根据需要生成一个文件内容generatedStuff,并根据generatedStuff数据情况进行参数更新,告诉VS如何处理.

     大概看一下this.GenerateCode方法做得是什么:

protected byte[] GenerateCode(string inputFileName, string inputFileContent)
{
using (System.IO.FileStream stream =System.IO.File.Open(inputFileName, FileMode.Open, FileAccess.Read))
{
Smark.Data.InterfaceToModelGenerator.GenerateCode gc
= new GenerateCode();
return StreamToBytes(gc.Builder(stream));
}

}
public System.IO.Stream Builder(System.IO.Stream stream)
{
System.IO.StringWriter sw = new System.IO.StringWriter();
Console.SetOut(sw);
Console.SetError(sw);
CSharpParser p = new CSharpParser(null, stream, null);
System.IO.Stream result = new System.IO.MemoryStream();
System.IO.StreamWriter writer = new System.IO.StreamWriter(result, Encoding.UTF8);
int errno = p.parse();
if (errno > 0)
{
writer.Write(sw.ToString());
}
CodeCompileUnit unit = p.Builder.CurrCompileUnit;

if (unit != null)
{
try
{
Microsoft.CSharp.CSharpCodeProvider csp = new Microsoft.CSharp.CSharpCodeProvider();
foreach (string assembly in unit.ReferencedAssemblies)
{
mEntityUnit.ReferencedAssemblies.Add(assembly);
}

foreach (CodeNamespace cn in unit.Namespaces)
{
BuilderNameSpace(cn);
}
csp.GenerateCodeFromCompileUnit(mEntityUnit, writer, new CodeGeneratorOptions());
writer.Flush();
}
catch (Exception e_)
{
writer.WriteLine(e_.Message + e_.StackTrace);
}
}
return result;

}

其实就是生成文件内容并返回相关流数据

 制作安装

     插件使用必须要把相关类信息注册到win注册表中让vs可以识别,还要把类生成的程序集部署到全局程序集中,这样做的前提就是要给程序集签名并发为相关类生成uid标识(对于这些详细信息可以通过MSDN了解).以上工作其实可以手动去做自行导入注册表和copy文件到全局目录中;这种做法确实比较山寨,我们可以创建一个安装项目,生成相关安装程序自动地完成这些事情.

 首先把需要的DLL添加到GAC中 

然后针对不同VS需要的版本构造相关的注册信息,也可以通过现有的注册项文件导入

以上工作都做好后,直接编译生成安装文件包就OK了.

使用插件

 其实插件使用也很简单,只需要在文件属性的自定义工具处理,填写插件制定的名称就行了

插件源码

http://smark.codeplex.com/

访问Beetlex的Github
原文地址:https://www.cnblogs.com/smark/p/2210432.html