PageRenderTime 29ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Releases/1.2/Source/DslPackage/CustomCode/CodeGeneration/VsMultipleFileGenerator.cs

#
C# | 309 lines | 228 code | 42 blank | 39 comment | 36 complexity | 6221974c4ddde3c48c1c02d8e02cdead MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Microsoft.VisualStudio.TextTemplating.VSHost;
  5. using Microsoft.VisualStudio.OLE.Interop;
  6. using Microsoft.VisualStudio.Shell;
  7. using System.Runtime.InteropServices;
  8. using System.IO;
  9. using System.ComponentModel;
  10. using System.Linq;
  11. using Microsoft.VisualStudio;
  12. using Microsoft.VisualStudio.Shell.Interop;
  13. /*
  14. * Based on the code found here:
  15. * http://www.codeproject.com/KB/cs/VsMultipleFileGenerator.aspx
  16. */
  17. namespace Microsoft.Samples.DslTools.ConfigurationSectionDesigner
  18. {
  19. public abstract class VsMultipleFileGenerator<IterativeElementType> : IEnumerable<IterativeElementType>, Microsoft.VisualStudio.TextTemplating.VSHost.IVsSingleFileGenerator, IObjectWithSite
  20. {
  21. #region Visual Studio Specific Fields
  22. private object site;
  23. private ServiceProvider serviceProvider = null;
  24. #endregion
  25. #region Our Fields
  26. private string bstrInputFileContents;
  27. private string wszInputFilePath;
  28. private EnvDTE.Project project;
  29. private List<string> newFileNames;
  30. #endregion
  31. [CLSCompliant(false)]
  32. protected EnvDTE.Project Project
  33. {
  34. get
  35. {
  36. return project;
  37. }
  38. }
  39. protected string InputFileContents
  40. {
  41. get
  42. {
  43. return bstrInputFileContents;
  44. }
  45. }
  46. protected string InputFilePath
  47. {
  48. get
  49. {
  50. return wszInputFilePath;
  51. }
  52. }
  53. protected object Site
  54. {
  55. get { return site; }
  56. }
  57. [CLSCompliant(false)]
  58. protected ServiceProvider SiteServiceProvider
  59. {
  60. get
  61. {
  62. if( serviceProvider == null )
  63. {
  64. Microsoft.VisualStudio.OLE.Interop.IServiceProvider oleServiceProvider = site as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
  65. serviceProvider = new ServiceProvider( oleServiceProvider );
  66. }
  67. return serviceProvider;
  68. }
  69. }
  70. private Microsoft.VisualStudio.TextTemplating.VSHost.IVsGeneratorProgress _generatorProgress;
  71. protected Microsoft.VisualStudio.TextTemplating.VSHost.IVsGeneratorProgress GeneratorProgress
  72. {
  73. get
  74. {
  75. return _generatorProgress;
  76. }
  77. }
  78. public VsMultipleFileGenerator()
  79. {
  80. EnvDTE.DTE dte = (EnvDTE.DTE)Package.GetGlobalService( typeof( EnvDTE.DTE ) );
  81. Array ary = (Array)dte.ActiveSolutionProjects;
  82. if( ary.Length > 0 )
  83. {
  84. project = (EnvDTE.Project)ary.GetValue( 0 );
  85. }
  86. newFileNames = new List<string>();
  87. }
  88. public abstract IEnumerator<IterativeElementType> GetEnumerator();
  89. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  90. {
  91. return GetEnumerator();
  92. }
  93. protected abstract string GetFileName( IterativeElementType element );
  94. public abstract byte[] GenerateContent( IterativeElementType element );
  95. public abstract string GetDefaultExtension();
  96. public abstract byte[] GenerateDefaultContent();
  97. public void Generate( string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, out IntPtr rgbOutputFileContents, out int pcbOutput, Microsoft.VisualStudio.TextTemplating.VSHost.IVsGeneratorProgress pGenerateProgress )
  98. {
  99. this.bstrInputFileContents = bstrInputFileContents;
  100. this.wszInputFilePath = wszInputFilePath;
  101. this._generatorProgress = pGenerateProgress;
  102. this.newFileNames.Clear();
  103. int iFound = 0;
  104. uint itemId = 0;
  105. EnvDTE.ProjectItem item;
  106. Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[] pdwPriority = new Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[1];
  107. // obtain a reference to the current project as an IVsProject type
  108. Microsoft.VisualStudio.Shell.Interop.IVsProject VsProject = VsHelper.ToVsProject( project );
  109. // this locates, and returns a handle to our source file, as a ProjectItem
  110. VsProject.IsDocumentInProject( InputFilePath, out iFound, pdwPriority, out itemId );
  111. // if our source file was found in the project (which it should have been)
  112. if( iFound != 0 && itemId != 0 )
  113. {
  114. Microsoft.VisualStudio.OLE.Interop.IServiceProvider oleSp = null;
  115. VsProject.GetItemContext( itemId, out oleSp );
  116. if( oleSp != null )
  117. {
  118. ServiceProvider sp = new ServiceProvider( oleSp );
  119. // convert our handle to a ProjectItem
  120. item = sp.GetService( typeof( EnvDTE.ProjectItem ) ) as EnvDTE.ProjectItem;
  121. }
  122. else
  123. throw new ApplicationException( "Unable to retrieve Visual Studio ProjectItem" );
  124. }
  125. else
  126. throw new ApplicationException( "Unable to retrieve Visual Studio ProjectItem" );
  127. // now we can start our work, iterate across all the 'elements' in our source file
  128. foreach( IterativeElementType element in this )
  129. {
  130. try
  131. {
  132. // obtain a name for this target file
  133. string fileName = GetFileName( element );
  134. // add it to the tracking cache
  135. newFileNames.Add( fileName );
  136. // fully qualify the file on the filesystem
  137. string strFile = Path.Combine( wszInputFilePath.Substring( 0, wszInputFilePath.LastIndexOf( Path.DirectorySeparatorChar ) ), fileName );
  138. FileStream fs = null;
  139. try
  140. {
  141. // generate our target file content
  142. byte[] data = GenerateContent( element );
  143. // if data is null, it means to ignore the contents of the generated file
  144. if( data == null ) continue;
  145. if( File.Exists( strFile ) )
  146. {
  147. // If the file already exists, only save the data
  148. // if the generated file is different than the
  149. // existing file.
  150. byte[] oldData = File.ReadAllBytes( strFile );
  151. bool equal = true;
  152. if( oldData.Length == data.Length )
  153. {
  154. for( int i = 0; i < oldData.Length; i++ )
  155. if( oldData[i] != data[i] )
  156. {
  157. equal = false;
  158. break;
  159. }
  160. }
  161. else
  162. equal = false;
  163. if( !equal )
  164. fs = File.Open( strFile, FileMode.Truncate );
  165. }
  166. else
  167. {
  168. // create the file
  169. fs = File.Create( strFile );
  170. }
  171. if( fs != null )
  172. {
  173. // write it out to the stream
  174. fs.Write( data, 0, data.Length );
  175. }
  176. // add the newly generated file to the solution, as a child of the source file
  177. if( item.ProjectItems.Cast<EnvDTE.ProjectItem>()
  178. .Where( pi => pi.Name == fileName ).Count() == 0 )
  179. {
  180. EnvDTE.ProjectItem itm = item.ProjectItems.AddFromFile( strFile );
  181. /*
  182. * Here you may wish to perform some addition logic
  183. * such as, setting a custom tool for the target file if it
  184. * is intented to perform its own generation process.
  185. * Or, set the target file as an 'Embedded Resource' so that
  186. * it is embedded into the final Assembly.
  187. EnvDTE.Property prop = itm.Properties.Item("CustomTool");
  188. //// set to embedded resource
  189. itm.Properties.Item("BuildAction").Value = 3;
  190. if (String.IsNullOrEmpty((string)prop.Value) || !String.Equals((string)prop.Value, typeof(AnotherCustomTool).Name))
  191. {
  192. prop.Value = typeof(AnotherCustomTool).Name;
  193. }
  194. */
  195. }
  196. }
  197. catch( Exception )
  198. {
  199. if( File.Exists( strFile ) )
  200. File.Delete( strFile );
  201. }
  202. finally
  203. {
  204. if( fs != null )
  205. fs.Close();
  206. }
  207. }
  208. catch( Exception ex )
  209. {
  210. throw ex;
  211. }
  212. }
  213. // perform some clean-up, making sure we delete any old (stale) target-files
  214. foreach( EnvDTE.ProjectItem childItem in item.ProjectItems )
  215. {
  216. if( !(childItem.Name.EndsWith( GetDefaultExtension() ) || newFileNames.Contains( childItem.Name )) )
  217. // then delete it
  218. childItem.Delete();
  219. }
  220. // generate our default content for our 'single' file
  221. byte[] defaultData = GenerateDefaultContent();
  222. if( defaultData == null )
  223. {
  224. rgbOutputFileContents = IntPtr.Zero;
  225. pcbOutput = 0;
  226. }
  227. else
  228. {
  229. // return our default data, so that Visual Studio may write it to disk.
  230. rgbOutputFileContents = Marshal.AllocCoTaskMem( defaultData.Length );
  231. Marshal.Copy( defaultData, 0, rgbOutputFileContents, defaultData.Length );
  232. pcbOutput = defaultData.Length;
  233. }
  234. }
  235. #region IObjectWithSite Members
  236. public void GetSite( ref Guid riid, out IntPtr ppvSite )
  237. {
  238. if( this.site == null )
  239. {
  240. throw new Win32Exception( -2147467259 );
  241. }
  242. IntPtr objectPointer = Marshal.GetIUnknownForObject( this.site );
  243. try
  244. {
  245. Marshal.QueryInterface( objectPointer, ref riid, out ppvSite );
  246. if( ppvSite == IntPtr.Zero )
  247. {
  248. throw new Win32Exception( -2147467262 );
  249. }
  250. }
  251. finally
  252. {
  253. if( objectPointer != IntPtr.Zero )
  254. {
  255. Marshal.Release( objectPointer );
  256. objectPointer = IntPtr.Zero;
  257. }
  258. }
  259. }
  260. public void SetSite( object pUnkSite )
  261. {
  262. this.site = pUnkSite;
  263. }
  264. #endregion
  265. }
  266. }