zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    UML实践
    “学士之路”系统设计
    团队采访
    《软件需求规格说明书》 ---学士之路
    奇怪的bug,不懂Atom在添加markdown-themeable-pdf,在配置好phantomjs的情况下报错
    团队项目-“学士之路”展示
    学习Mybatis的两个必须的jar包分享
    Spring MVC controller的方法返回值
    Spring的Controller映射规则
    servlet基础学习总结
  • 原文地址:https://www.cnblogs.com/smark/p/2210432.html
Copyright © 2011-2022 走看看