From: Daniel C. \(kzu\) <dca...@us...> - 2004-10-10 15:33:54
|
Update of /cvsroot/mvp-xml/Design/v1/src/VisualStudio/GotDotNet In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11476/v1/src/VisualStudio/GotDotNet Added Files: BaseCodeGenerator.cs BaseCodeGeneratorWithSite.cs IObjectWithSite.cs IOleServiceProvider.cs IVsGeneratorProgress.cs IVsSingleFileGenerator.cs ServiceProvider.cs Log Message: Classes downloaded from GDN that allow easy development of custom tools. --- NEW FILE: ServiceProvider.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Diagnostics; using System.Runtime.InteropServices; /// <summary> /// This wraps the IOleServiceProvider interface and provides an easy COM+ way to get at /// services. /// </summary> public class ServiceProvider : IServiceProvider, IObjectWithSite { private static Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); private IOleServiceProvider serviceProvider; /// <summary> /// Creates a new ServiceProvider object and uses the given interface to resolve /// services. /// </summary> /// <param name='sp'> /// The IOleServiceProvider interface to use. /// </param> public ServiceProvider(IOleServiceProvider sp) { serviceProvider = sp; } /// <summary> /// gives this class a chance to free its references. /// </summary> public virtual void Dispose() { if (serviceProvider != null) { serviceProvider = null; } } /// <summary> /// returns true if the given HRESULT is a failure HRESULT /// </summary> /// <param name="hr">HRESULT to test</param> /// <returns>true if the HRESULT is a failure, false if not.</returns> public static bool Failed(int hr) { return(hr < 0); } /// <summary> /// Retrieves the requested service. /// </summary> /// <param name='serviceClass'> /// The class of the service to retrieve. /// </param> /// <returns> /// an instance of serviceClass or null if no /// such service exists. /// </returns> public virtual object GetService(Type serviceClass) { if (serviceClass == null) { return null; } return GetService(serviceClass.GUID, serviceClass); } /// <summary> /// Retrieves the requested service. /// </summary> /// <param name='guid'> /// The GUID of the service to retrieve. /// </param> /// <returns> /// an instance of the service or null if no /// such service exists. /// </returns> public virtual object GetService(Guid guid) { return GetService(guid, null); } /// <summary> /// Retrieves the requested service. The guid must be specified; the class is only /// used when debugging and it may be null. /// </summary> private object GetService(Guid guid, Type serviceClass) { // Valid, but wierd for caller to init us with a NULL sp // if (serviceProvider == null) { return null; } object service = null; // No valid guid on the passed in class, so there is no service for it. // if (guid.Equals(Guid.Empty)) { return null; } // We provide a couple of services of our own. // if (guid.Equals(typeof(IOleServiceProvider).GUID)) { return serviceProvider; } if (guid.Equals(typeof(IObjectWithSite).GUID)) { return (IObjectWithSite)this; } IntPtr pUnk; int hr = serviceProvider.QueryService(ref guid, ref IID_IUnknown, out pUnk); if (Succeeded(hr) && (pUnk != IntPtr.Zero)) { service = Marshal.GetObjectForIUnknown(pUnk); Marshal.Release(pUnk); } return service; } /// <summary> /// Retrieves the current site object we're using to /// resolve services. /// </summary> /// <param name='riid'> /// Must be IServiceProvider.class.GUID /// </param> /// <param name='ppvSite'> /// Outparam that will contain the site object. /// </param> /// <seealso cref='IObjectWithSite'/> void IObjectWithSite.GetSite(ref Guid riid, object[] ppvSite) { ppvSite[0] = GetService(riid); } /// <summary> /// Sets the site object we will be using to resolve services. /// </summary> /// <param name='pUnkSite'> /// The site we will use. This site will only be /// used if it also implements IOleServiceProvider. /// </param> /// <seealso cref='IObjectWithSite'/> void IObjectWithSite.SetSite(object pUnkSite) { if (pUnkSite is IOleServiceProvider) { serviceProvider = (IOleServiceProvider)pUnkSite; } } /// <summary> /// returns true if the given HRESULT is a success HRESULT /// </summary> /// <param name="hr">HRESULT to test</param> /// <returns>true if the HRESULT is a success, false if not.</returns> public static bool Succeeded(int hr) { return(hr >= 0); } } } --- NEW FILE: IOleServiceProvider.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Runtime.InteropServices; /// <remarks/> [ ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) ] public interface IOleServiceProvider { /// <remarks/> [PreserveSig] int QueryService([In]ref Guid guidService, [In]ref Guid riid, out IntPtr ppvObject); } } --- NEW FILE: IVsSingleFileGenerator.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Runtime.InteropServices; /// <remarks/> [ ComImport, Guid("3634494C-492F-4F91-8009-4541234E4E99"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) ] public interface IVsSingleFileGenerator { // // Retrieve default properties for the generator // [propget] HRESULT DefaultExtension([out,retval] BSTR* pbstrDefaultExtension); // /// <remarks/> [return: MarshalAs(UnmanagedType.BStr)] string GetDefaultExtension(); // // Generate the file // HRESULT Generate([in] LPCOLESTR wszInputFilePath, // [in] BSTR bstrInputFileContents, // [in] LPCOLESTR wszDefaultNamespace, // [out] BYTE** rgbOutputFileContents, // [out] ULONG* pcbOutput, // [in] IVsGeneratorProgress* pGenerateProgress); // /// <remarks/> void Generate( [MarshalAs(UnmanagedType.LPWStr)] string wszInputFilePath, [MarshalAs(UnmanagedType.BStr)] string bstrInputFileContents, [MarshalAs(UnmanagedType.LPWStr)] string wszDefaultNamespace, out IntPtr rgbOutputFileContents, [MarshalAs(UnmanagedType.U4)] out int pcbOutput, IVsGeneratorProgress pGenerateProgress); } } --- NEW FILE: IVsGeneratorProgress.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Runtime.InteropServices; /// <remarks/> [ ComImport, Guid("BED89B98-6EC9-43CB-B0A8-41D6E2D6669D"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) ] public interface IVsGeneratorProgress { // // Communicate errors // HRESULT GeneratorError([in] BOOL fWarning, // [in] DWORD dwLevel, // [in] BSTR bstrError, // [in] DWORD dwLine, // [in] DWORD dwColumn); // /// <remarks/> void GeneratorError( bool fWarning, [MarshalAs(UnmanagedType.U4)] int dwLevel, [MarshalAs(UnmanagedType.BStr)] string bstrError, [MarshalAs(UnmanagedType.U4)] int dwLine, [MarshalAs(UnmanagedType.U4)] int dwColumn); // // Report progress to the caller. // HRESULT Progress([in] ULONG nComplete, // Current position // [in] ULONG nTotal); // Max value // /// <remarks/> void Progress( [MarshalAs(UnmanagedType.U4)] int nComplete, [MarshalAs(UnmanagedType.U4)] int nTotal); } } --- NEW FILE: IObjectWithSite.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Runtime.InteropServices; [ ComImport, Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) ] internal interface IObjectWithSite { // // HRESULT SetSite( // [in] IUnknown * pUnkSite); // void SetSite( [MarshalAs(UnmanagedType.Interface)] object pUnkSite); // // HRESULT GetSite( // [in] REFIID riid, // [out, iid_is(riid)] void ** ppvSite); // void GetSite( [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.LPArray)] object[] ppvSite); } } --- NEW FILE: BaseCodeGeneratorWithSite.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Diagnostics; using System.Runtime.InteropServices; using EnvDTE; using VSLangProj; using Microsoft.VisualStudio.Designer.Interfaces; /// <summary> /// This class exists to be cocreated a in a preprocessor build step. /// </summary> public abstract class BaseCodeGeneratorWithSite : BaseCodeGenerator, IObjectWithSite { private const int E_FAIL = unchecked((int)0x80004005); private const int E_NOINTERFACE = unchecked((int)0x80004002); private object site = null; private CodeDomProvider codeDomProvider = null; private static Guid CodeDomInterfaceGuid = new Guid("{73E59688-C7C4-4a85-AF64-A538754784C5}"); private static Guid CodeDomServiceGuid = CodeDomInterfaceGuid; private ServiceProvider serviceProvider = null; /// <summary> /// demand-creates a CodeDomProvider /// </summary> protected virtual CodeDomProvider CodeProvider { get { if (codeDomProvider == null) { IVSMDCodeDomProvider vsmdCodeDomProvider = (IVSMDCodeDomProvider) GetService(CodeDomServiceGuid); if (vsmdCodeDomProvider != null) { codeDomProvider = (CodeDomProvider) vsmdCodeDomProvider.CodeDomProvider; } Debug.Assert(codeDomProvider != null, "Get CodeDomProvider Interface failed. GetService(QueryService(CodeDomProvider) returned Null."); } return codeDomProvider; } set { if (value == null) { throw new ArgumentNullException(); } codeDomProvider = value; } } /// <summary> /// demand-creates a ServiceProvider given an IOleServiceProvider /// </summary> private ServiceProvider SiteServiceProvider { get { if (serviceProvider == null) { IOleServiceProvider oleServiceProvider = site as IOleServiceProvider; Debug.Assert(oleServiceProvider != null, "Unable to get IOleServiceProvider from site object."); serviceProvider = new ServiceProvider(oleServiceProvider); } return serviceProvider; } } /// <summary> /// method to get a service by its GUID /// </summary> /// <param name="serviceGuid">GUID of service to retrieve</param> /// <returns>an object that implements the requested service</returns> protected object GetService(Guid serviceGuid) { return SiteServiceProvider.GetService(serviceGuid); } /// <summary> /// method to get a service by its Type /// </summary> /// <param name="serviceType">Type of service to retrieve</param> /// <returns>an object that implements the requested service</returns> protected virtual object GetService(Type serviceType) { return SiteServiceProvider.GetService(serviceType); } /// <summary> /// gets the default extension of the output file by asking the CodeDomProvider /// what its default extension is. /// </summary> /// <returns></returns> public override string GetDefaultExtension() { CodeDomProvider codeDom = CodeProvider; Debug.Assert(codeDom != null, "CodeDomProvider is NULL."); string extension = codeDom.FileExtension; if (extension != null && extension.Length > 0) { if (extension[0] != '.') { extension = "." + extension; } } return extension; } /// <summary> /// Method to get an ICodeGenerator with which this class can create code. /// </summary> /// <returns></returns> protected virtual ICodeGenerator GetCodeWriter() { CodeDomProvider codeDom = CodeProvider; if (codeDom != null) { return codeDom.CreateGenerator(); } return null; } /// <summary> /// SetSite method of IOleObjectWithSite /// </summary> /// <param name="pUnkSite">site for this object to use</param> public virtual void SetSite(object pUnkSite) { site = pUnkSite; codeDomProvider = null; serviceProvider = null; } /// <summary> /// GetSite method of IOleObjectWithSite /// </summary> /// <param name="riid">interface to get</param> /// <param name="ppvSite">array in which to stuff return value</param> public virtual void GetSite(ref Guid riid, object[] ppvSite) { if (ppvSite == null) { throw new ArgumentNullException("ppvSite"); } if (ppvSite.Length < 1) { throw new ArgumentException("ppvSite array must have at least 1 member", "ppvSite"); } if (site == null) { throw new COMException("object is not sited", E_FAIL); } IntPtr pUnknownPointer = Marshal.GetIUnknownForObject(site); IntPtr intPointer = IntPtr.Zero; Marshal.QueryInterface(pUnknownPointer, ref riid, out intPointer); if (intPointer == IntPtr.Zero) { throw new COMException("site does not support requested interface", E_NOINTERFACE); } ppvSite[0] = Marshal.GetObjectForIUnknown(intPointer); } /// <summary> /// gets a string containing the DLL names to add. /// </summary> /// <param name="DLLToAdd"></param> /// <returns></returns> private string GetDLLNames(string[] DLLToAdd) { if (DLLToAdd == null || DLLToAdd.Length == 0) { return string.Empty; } string dllNames = DLLToAdd[0]; for (int i = 1; i < DLLToAdd.Length; i++) { dllNames = dllNames + ", " + DLLToAdd[i]; } return dllNames; } /// <summary> /// adds a reference to the project for each required DLL /// </summary> /// <param name="referenceDLL"></param> protected void AddReferenceDLLToProject(string[] referenceDLL) { if (referenceDLL.Length == 0) { return; } object serviceObject = GetService(typeof(ProjectItem)); Debug.Assert(serviceObject != null, "Unable to get Project Item."); if (serviceObject == null) { string errorMessage = String.Format("Unable to add DLL to project references: {0}. Please Add them manually.", GetDLLNames(referenceDLL)); GeneratorErrorCallback(false, 1, errorMessage, 0, 0); return; } Project containingProject = ((ProjectItem) serviceObject).ContainingProject; Debug.Assert(containingProject != null, "GetService(typeof(Project)) return null."); if (containingProject == null) { string errorMessage = String.Format("Unable to add DLL to project references: {0}. Please Add them manually.", GetDLLNames(referenceDLL)); GeneratorErrorCallback(false, 1, errorMessage, 0, 0); return; } VSProject vsProj = containingProject.Object as VSProject; Debug.Assert(vsProj != null, "Unable to ADD DLL to current project. Project.Object does not implement VSProject."); if (vsProj == null) { string errorMessage = String.Format("Unable to add DLL to project references: {0}. Please Add them manually.", GetDLLNames(referenceDLL)); GeneratorErrorCallback(false, 1, errorMessage, 0, 0); return; } try { for (int i = 0; i < referenceDLL.Length; i++) { vsProj.References.Add(referenceDLL[i]); } } catch (Exception e) { Debug.Fail("**** ERROR: vsProj.References.Add() throws exception: " + e.ToString()); string errorMessage = String.Format("Unable to add DLL to project references: {0}. Please Add them manually.", GetDLLNames(referenceDLL)); GeneratorErrorCallback(false, 1, errorMessage, 0, 0); return; } } /// <summary> /// method to create an exception message given an exception /// </summary> /// <param name="e">exception caught</param> /// <returns>message to display to the user</returns> protected virtual string CreateExceptionMessage(Exception e) { string message = (e.Message != null ? e.Message : string.Empty); Exception innerException = e.InnerException; while (innerException != null) { string innerMessage = innerException.Message; if (innerMessage != null && innerMessage.Length > 0) { message = message + " " + innerMessage; } innerException = innerException.InnerException; } return message; } /// <summary> /// method to create a version comment /// </summary> /// <param name="codeNamespace"></param> protected virtual void GenerateVersionComment(System.CodeDom.CodeNamespace codeNamespace) { codeNamespace.Comments.Add(new CodeCommentStatement(string.Empty)); codeNamespace.Comments.Add(new CodeCommentStatement(String.Format("This source code was auto-generated by {0}, Version {1}.", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, System.Environment.Version.ToString()))); codeNamespace.Comments.Add(new CodeCommentStatement(string.Empty)); } } } --- NEW FILE: BaseCodeGenerator.cs --- namespace Mvp.Xml.Design.VisualStudio { using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; /// <summary> /// A managed wrapper for VS's concept of an IVsSingleFileGenerator which is /// a custom tool invoked during the build which can take any file as an input /// and provide a compilable code file as output. /// </summary> public abstract class BaseCodeGenerator : IVsSingleFileGenerator { private IVsGeneratorProgress codeGeneratorProgress; private string codeFileNameSpace = String.Empty; private string codeFilePath = String.Empty; /// <summary> /// namespace for the file. /// </summary> protected string FileNameSpace { get { return codeFileNameSpace; } } /// <summary> /// file-path for the input file. /// </summary> protected string InputFilePath { get { return codeFilePath; } } /// <summary> /// interface to the VS shell object we use to tell our /// progress while we are generating. /// </summary> internal IVsGeneratorProgress CodeGeneratorProgress { get { return codeGeneratorProgress; } } /// <summary> /// gets the default extension for this generator /// </summary> /// <returns>string with the default extension for this generator</returns> public abstract string GetDefaultExtension(); /// <summary> /// the method that does the actual work of generating code given the input /// file. /// </summary> /// <param name="inputFileName">input file name</param> /// <param name="inputFileContent">file contents as a string</param> /// <returns>the generated code file as a byte-array</returns> protected abstract byte[] GenerateCode(string inputFileName, string inputFileContent); /// <summary> /// method that will communicate an error via the shell callback mechanism. /// </summary> /// <param name="warning">true if this is a warning</param> /// <param name="level">level or severity</param> /// <param name="message">text displayed to the user</param> /// <param name="line">line number of error/warning</param> /// <param name="column">column number of error/warning</param> protected virtual void GeneratorErrorCallback(bool warning, int level, string message, int line, int column) { IVsGeneratorProgress progress = CodeGeneratorProgress; if (progress != null) { progress.GeneratorError(warning, level, message, line, column); } } /// <summary> /// main method that the VS shell calls to do the generation /// </summary> /// <param name="wszInputFilePath">path to the input file</param> /// <param name="bstrInputFileContents">contents of the input file as a string (shell handles UTF-8 to Unicode and those types of conversions)</param> /// <param name="wszDefaultNamespace">default namespace for the generated code file</param> /// <param name="rgbOutputFileContents">byte-array of output file contents</param> /// <param name="pcbOutput">count of bytes in the output byte-array</param> /// <param name="pGenerateProgress">interface to send progress updates to the shell</param> public void Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, out IntPtr rgbOutputFileContents, out int pcbOutput, IVsGeneratorProgress pGenerateProgress) { if (bstrInputFileContents == null) { throw new ArgumentNullException(bstrInputFileContents); } codeFilePath = wszInputFilePath; codeFileNameSpace = wszDefaultNamespace; codeGeneratorProgress = pGenerateProgress; byte[] bytes = GenerateCode(wszInputFilePath, bstrInputFileContents); if (bytes == null) { rgbOutputFileContents = IntPtr.Zero; pcbOutput = 0; } else { pcbOutput = bytes.Length; rgbOutputFileContents = Marshal.AllocCoTaskMem(pcbOutput); Marshal.Copy(bytes, 0, rgbOutputFileContents, pcbOutput); } } /// <summary> /// method to return a byte-array given a Stream /// </summary> /// <param name="stream">stream to convert to a byte-array</param> /// <returns>the stream's contents as a byte-array</returns> protected byte[] StreamToBytes(Stream stream) { if (stream.Length == 0) { return new byte[] { }; } long position = stream.Position; stream.Position = 0; byte[] bytes = new byte[(int)stream.Length]; stream.Read(bytes, 0, bytes.Length); stream.Position = position; return bytes; } } } |