PageRenderTime 53ms CodeModel.GetById 16ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 1ms

/Tools/IronStudio/IronStudio/VisualStudio/Project/Utilities.cs

http://github.com/IronLanguages/main
C# | 995 lines | 630 code | 135 blank | 230 comment | 137 complexity | c0c54c7d9859c0a393aeeb0cc582bf5b MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the Apache License, Version 2.0, please send an email to 
  8 * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 * ***************************************************************************/
 14
 15
 16using System;
 17using System.Collections.Generic;
 18using System.ComponentModel;
 19using System.Diagnostics;
 20using System.Diagnostics.CodeAnalysis;
 21using System.Drawing;
 22using System.Globalization;
 23using System.IO;
 24using System.Runtime.InteropServices;
 25using System.Security.Permissions;
 26using System.Security.Policy;
 27using System.Text;
 28using System.Text.RegularExpressions;
 29using System.Windows.Forms;
 30using Microsoft.VisualStudio;
 31using Microsoft.VisualStudio.OLE.Interop;
 32using Microsoft.VisualStudio.Shell;
 33using Microsoft.VisualStudio.Shell.Interop;
 34using Microsoft.Win32;
 35using IServiceProvider = System.IServiceProvider;
 36using MSBuild = Microsoft.Build.Evaluation;
 37using VSRegistry = Microsoft.VisualStudio.Shell.VSRegistry;
 38
 39namespace Microsoft.VisualStudio.Project
 40{
 41    public static class Utilities
 42    {
 43        private const string defaultMSBuildVersion = "4.0";
 44
 45        /// <summary>
 46        /// Look in the registry under the current hive for the path
 47        /// of MSBuild
 48        /// </summary>
 49        /// <returns></returns>
 50        [CLSCompliant(false)]
 51        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
 52        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "msbuild")]
 53        public static string GetMsBuildPath(IServiceProvider serviceProvider)
 54        {
 55            return GetMsBuildPath(serviceProvider, defaultMSBuildVersion);
 56        }
 57
 58        /// <summary>
 59        /// Search the registry for the tools path for MSBuild.
 60        /// </summary>
 61        /// <param name="serviceProvider">The service provider.</param>
 62        /// <param name="version">Msbuild version.</param>
 63        /// <returns>The msbuild tools path</returns>
 64        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
 65        public static string GetMsBuildPath(IServiceProvider serviceProvider, string version)
 66        {
 67            string msBuildPath = null;
 68            using(RegistryKey root = VSRegistry.RegistryRoot(serviceProvider, __VsLocalRegistryType.RegType_Configuration, false))
 69            {
 70                // Get the value from the registry
 71                using(RegistryKey vsKey = root.OpenSubKey("MSBuild", false))
 72                {
 73                    msBuildPath = (string)vsKey.GetValue("MSBuildBinPath", null);
 74                }
 75            }
 76            if(!string.IsNullOrEmpty(msBuildPath))
 77            {
 78                return msBuildPath;
 79            }
 80
 81            // The path to MSBuild was not found in the VisualStudio's registry hive, so try to
 82            // find it in the new MSBuild hive.
 83            string registryPath = string.Format(CultureInfo.InvariantCulture, "Software\\Microsoft\\MSBuild\\ToolsVersions\\{0}", version);
 84            using(Microsoft.Win32.RegistryKey msbuildKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryPath, false))
 85            {
 86                msBuildPath = (string)msbuildKey.GetValue("MSBuildToolsPath", null);
 87            }
 88            if(string.IsNullOrEmpty(msBuildPath))
 89            {
 90                string error = SR.GetString(SR.ErrorMsBuildRegistration, CultureInfo.CurrentUICulture);
 91                throw new FileLoadException(error);
 92            }
 93            return msBuildPath;
 94        }
 95
 96        /// <summary>
 97        /// Is Visual Studio in design mode.
 98        /// </summary>
 99        /// <param name="serviceProvider">The service provider.</param>
100        /// <returns>true if visual studio is in design mode</returns>
101        public static bool IsVisualStudioInDesignMode(IServiceProvider site)
102        {
103            if (site == null)
104            {
105                throw new ArgumentNullException("site");
106            }
107
108            IVsMonitorSelection selectionMonitor = site.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection;
109            uint cookie = 0;
110            int active = 0;
111            Guid designContext = VSConstants.UICONTEXT_DesignMode;
112            ErrorHandler.ThrowOnFailure(selectionMonitor.GetCmdUIContextCookie(ref designContext, out cookie));
113            ErrorHandler.ThrowOnFailure(selectionMonitor.IsCmdUIContextActive(cookie, out active));
114            return active != 0;
115        }
116
117        /// <include file='doc\VsShellUtilities.uex' path='docs/doc[@for="Utilities.IsInAutomationFunction"]/*' />
118        /// <devdoc>
119        /// Is an extensibility object executing an automation function.
120        /// </devdoc>
121        /// <param name="serviceProvider">The service provider.</param>
122        /// <returns>true if the extensiblity object is executing an automation function.</returns>
123        public static bool IsInAutomationFunction(IServiceProvider serviceProvider)
124        {
125            if(serviceProvider == null)
126            {
127                throw new ArgumentNullException("serviceProvider");
128            }
129
130            IVsExtensibility3 extensibility = serviceProvider.GetService(typeof(EnvDTE.IVsExtensibility)) as IVsExtensibility3;
131
132            if(extensibility == null)
133            {
134                throw new InvalidOperationException();
135            }
136            int inAutomation = 0;
137            ErrorHandler.ThrowOnFailure(extensibility.IsInAutomationFunction(out inAutomation));
138            return inAutomation != 0;
139        }
140
141        /// <summary>
142        /// Creates a semicolon delinited list of strings. This can be used to provide the properties for VSHPROPID_CfgPropertyPagesCLSIDList, VSHPROPID_PropertyPagesCLSIDList, VSHPROPID_PriorityPropertyPagesCLSIDList
143        /// </summary>
144        /// <param name="guids">An array of Guids.</param>
145        /// <returns>A semicolon delimited string, or null</returns>
146        [CLSCompliant(false)]
147        public static string CreateSemicolonDelimitedListOfStringFromGuids(Guid[] guids)
148        {
149            if(guids == null || guids.Length == 0)
150            {
151                return null;
152            }
153
154            // Create a StringBuilder with a pre-allocated buffer big enough for the
155            // final string. 39 is the length of a GUID in the "B" form plus the final ';'
156            StringBuilder stringList = new StringBuilder(39 * guids.Length);
157            for(int i = 0; i < guids.Length; i++)
158            {
159                stringList.Append(guids[i].ToString("B"));
160                stringList.Append(";");
161            }
162
163            return stringList.ToString().TrimEnd(';');
164        }
165
166        private static char[] curlyBraces = new char[] { '{', '}' };
167        /// <summary>
168        /// Take list of guids as a single string and generate an array of Guids from it
169        /// </summary>
170        /// <param name="guidList">Semi-colon separated list of Guids</param>
171        /// <returns>Array of Guids</returns>
172        [CLSCompliant(false)]
173        public static Guid[] GuidsArrayFromSemicolonDelimitedStringOfGuids(string guidList)
174        {
175            if(guidList == null)
176            {
177                return null;
178            }
179
180            List<Guid> guids = new List<Guid>();
181            string[] guidsStrings = guidList.Split(';');
182            foreach(string guid in guidsStrings)
183            {
184                if(!String.IsNullOrEmpty(guid))
185                    guids.Add(new Guid(guid.Trim(curlyBraces)));
186            }
187
188            return guids.ToArray();
189        }
190
191        /// <summary>
192        /// Validates a file path by validating all file parts. If the 
193        /// the file name is invalid it throws an exception if the project is in automation. Otherwise it shows a dialog box with the error message.
194        /// </summary>
195        /// <param name="serviceProvider">The service provider</param>
196        /// <param name="filePath">A full path to a file name</param>
197        /// <exception cref="InvalidOperationException">In case of failure an InvalidOperationException is thrown.</exception>
198        public static void ValidateFileName(IServiceProvider serviceProvider, string filePath)
199        {
200            string errorMessage = String.Empty;
201            if(String.IsNullOrEmpty(filePath))
202            {
203                errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture);
204            }
205            else if(filePath.Length > NativeMethods.MAX_PATH)
206            {
207                errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), filePath);
208            }
209            else if(ContainsInvalidFileNameChars(filePath))
210            {
211                errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture);
212            }
213
214            if(errorMessage.Length == 0)
215            {
216                string fileName = Path.GetFileName(filePath);
217                if(String.IsNullOrEmpty(fileName) || IsFileNameInvalid(fileName))
218                {
219                    errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture);
220                }
221                else
222                {
223                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
224
225                    // If there is no filename or it starts with a leading dot issue an error message and quit.
226                    if(String.IsNullOrEmpty(fileNameWithoutExtension) || fileNameWithoutExtension[0] == '.')
227                    {
228                        errorMessage = SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture);
229                    }
230                }
231            }
232
233            if(errorMessage.Length > 0)
234            {
235                // If it is not called from an automation method show a dialog box.
236                if(!Utilities.IsInAutomationFunction(serviceProvider))
237                {
238                    string title = null;
239                    OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
240                    OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
241                    OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
242                    VsShellUtilities.ShowMessageBox(serviceProvider, title, errorMessage, icon, buttons, defaultButton);
243                }
244                else
245                {
246                    throw new InvalidOperationException(errorMessage);
247                }
248            }
249
250        }
251
252        /// <summary>
253        /// Creates a CALPOLESTR from a list of strings 
254        /// It is the responsability of the caller to release this memory.
255        /// </summary>
256        /// <param name="guids"></param>
257        /// <returns>A CALPOLESTR that was created from the the list of strings.</returns>
258        [CLSCompliant(false)]
259        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CALPOLESTR")]
260        public static CALPOLESTR CreateCALPOLESTR(IList<string> strings)
261        {
262            CALPOLESTR calpolStr = new CALPOLESTR();
263
264            if(strings != null)
265            {
266                // Demand unmanaged permissions in order to access unmanaged memory.
267                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
268
269                calpolStr.cElems = (uint)strings.Count;
270
271                int size = Marshal.SizeOf(typeof(IntPtr));
272
273                calpolStr.pElems = Marshal.AllocCoTaskMem(strings.Count * size);
274
275                IntPtr ptr = calpolStr.pElems;
276
277                foreach(string aString in strings)
278                {
279                    IntPtr tempPtr = Marshal.StringToCoTaskMemUni(aString);
280                    Marshal.WriteIntPtr(ptr, tempPtr);
281                    ptr = new IntPtr(ptr.ToInt64() + size);
282                }
283            }
284
285            return calpolStr;
286        }
287
288        /// <summary>
289        /// Creates a CADWORD from a list of tagVsSccFilesFlags. Memory is allocated for the elems. 
290        /// It is the responsability of the caller to release this memory.
291        /// </summary>
292        /// <param name="guids"></param>
293        /// <returns>A CADWORD created from the list of tagVsSccFilesFlags.</returns>
294        [CLSCompliant(false)]
295        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CADWORD")]
296        public static CADWORD CreateCADWORD(IList<tagVsSccFilesFlags> flags)
297        {
298            CADWORD cadWord = new CADWORD();
299
300            if(flags != null)
301            {
302                // Demand unmanaged permissions in order to access unmanaged memory.
303                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
304
305                cadWord.cElems = (uint)flags.Count;
306
307                int size = Marshal.SizeOf(typeof(UInt32));
308
309                cadWord.pElems = Marshal.AllocCoTaskMem(flags.Count * size);
310
311                IntPtr ptr = cadWord.pElems;
312
313                foreach(tagVsSccFilesFlags flag in flags)
314                {
315                    Marshal.WriteInt32(ptr, (int)flag);
316                    ptr = new IntPtr(ptr.ToInt64() + size);
317                }
318            }
319
320            return cadWord;
321        }
322
323        /// <summary>
324        /// Splits a bitmap from a Stream into an ImageList
325        /// </summary>
326        /// <param name="imageStream">A Stream representing a Bitmap</param>
327        /// <returns>An ImageList object representing the images from the given stream</returns>
328        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
329        public static ImageList GetImageList(Stream imageStream)
330        {
331            ImageList ilist = new ImageList();
332
333            if(imageStream == null)
334            {
335                return ilist;
336            }
337            ilist.ColorDepth = ColorDepth.Depth24Bit;
338            ilist.ImageSize = new Size(16, 16);
339            Bitmap bitmap = new Bitmap(imageStream);
340            ilist.Images.AddStrip(bitmap);
341            ilist.TransparentColor = Color.Magenta;
342            return ilist;
343        }
344
345        /// <summary>
346        /// Splits a bitmap from a pointer to an ImageList
347        /// </summary>
348        /// <param name="imageListAsPointer">A pointer to a bitmap of images to split</param>
349        /// <returns>An ImageList object representing the images from the given stream</returns>
350        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
351        public static ImageList GetImageList(object imageListAsPointer)
352        {
353            ImageList images = null;
354
355            IntPtr intPtr = new IntPtr((int)imageListAsPointer);
356            HandleRef hImageList = new HandleRef(null, intPtr);
357            int count = UnsafeNativeMethods.ImageList_GetImageCount(hImageList);
358
359            if(count > 0)
360            {
361                // Create a bitmap big enough to hold all the images
362                Bitmap b = new Bitmap(16 * count, 16);
363                Graphics g = Graphics.FromImage(b);
364
365                // Loop through and extract each image from the imagelist into our own bitmap
366                IntPtr hDC = IntPtr.Zero;
367                try
368                {
369                    hDC = g.GetHdc();
370                    HandleRef handleRefDC = new HandleRef(null, hDC);
371                    for(int i = 0; i < count; i++)
372                    {
373                        UnsafeNativeMethods.ImageList_Draw(hImageList, i, handleRefDC, i * 16, 0, NativeMethods.ILD_NORMAL);
374                    }
375                }
376                finally
377                {
378                    if(g != null && hDC != IntPtr.Zero)
379                    {
380                        g.ReleaseHdc(hDC);
381                    }
382                }
383
384                // Create a new imagelist based on our stolen images
385                images = new ImageList();
386                images.ColorDepth = ColorDepth.Depth24Bit;
387                images.ImageSize = new Size(16, 16);
388                images.Images.AddStrip(b);
389            }
390            return images;
391        }
392
393        /// <summary>
394        /// Gets the active configuration name.
395        /// </summary>
396        /// <param name="automationObject">The automation object.</param>
397        /// <returns>The name of the active configuartion.</returns>		
398        internal static string GetActiveConfigurationName(EnvDTE.Project automationObject)
399        {
400            if(automationObject == null)
401            {
402                throw new ArgumentNullException("automationObject");
403            }
404
405            string currentConfigName = string.Empty;
406            if(automationObject.ConfigurationManager != null)
407            {
408                EnvDTE.Configuration activeConfig = automationObject.ConfigurationManager.ActiveConfiguration;
409                if(activeConfig != null)
410                {
411                    currentConfigName = activeConfig.ConfigurationName;
412                }
413            }
414            return currentConfigName;
415
416        }
417
418
419        /// <summary>
420        /// Verifies that two objects represent the same instance of a COM object.
421        /// This essentially compares the IUnkown pointers of the 2 objects.
422        /// This is needed in scenario where aggregation is involved.
423        /// </summary>
424        /// <param name="obj1">Can be an object, interface or IntPtr</param>
425        /// <param name="obj2">Can be an object, interface or IntPtr</param>
426        /// <returns>True if the 2 items represent the same thing</returns>
427        [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")]
428        public static bool IsSameComObject(object obj1, object obj2)
429        {
430            bool isSame = false;
431            IntPtr unknown1 = IntPtr.Zero;
432            IntPtr unknown2 = IntPtr.Zero;
433            try
434            {
435                // If we have 2 null, then they are not COM objects and as such "it's not the same COM object"
436                if(obj1 != null && obj2 != null)
437                {
438                    unknown1 = QueryInterfaceIUnknown(obj1);
439                    unknown2 = QueryInterfaceIUnknown(obj2);
440
441                    isSame = IntPtr.Equals(unknown1, unknown2);
442                }
443            }
444            finally
445            {
446                if(unknown1 != IntPtr.Zero)
447                {
448                    Marshal.Release(unknown1);
449                }
450
451                if(unknown2 != IntPtr.Zero)
452                {
453                    Marshal.Release(unknown2);
454                }
455
456            }
457
458            return isSame;
459        }
460
461        /// <summary>
462        /// Retrieve the IUnknown for the managed or COM object passed in.
463        /// </summary>
464        /// <param name="objToQuery">Managed or COM object.</param>
465        /// <returns>Pointer to the IUnknown interface of the object.</returns>
466        internal static IntPtr QueryInterfaceIUnknown(object objToQuery)
467        {
468            bool releaseIt = false;
469            IntPtr unknown = IntPtr.Zero;
470            IntPtr result;
471            try
472            {
473                if(objToQuery is IntPtr)
474                {
475                    unknown = (IntPtr)objToQuery;
476                }
477                else
478                {
479                    // This is a managed object (or RCW)
480                    unknown = Marshal.GetIUnknownForObject(objToQuery);
481                    releaseIt = true;
482                }
483
484                // We might already have an IUnknown, but if this is an aggregated
485                // object, it may not be THE IUnknown until we QI for it.				
486                Guid IID_IUnknown = VSConstants.IID_IUnknown;
487                ErrorHandler.ThrowOnFailure(Marshal.QueryInterface(unknown, ref IID_IUnknown, out result));
488            }
489            finally
490            {
491                if(releaseIt && unknown != IntPtr.Zero)
492                {
493                    Marshal.Release(unknown);
494                }
495
496            }
497
498            return result;
499        }
500
501        /// <summary>
502        /// Returns true if thename that can represent a path, absolut or relative, or a file name contains invalid filename characters.
503        /// </summary>
504        /// <param name="name">File name</param>
505        /// <returns>true if file name is invalid</returns>
506        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0",
507            Justification="The name is validated.")]
508        public static bool ContainsInvalidFileNameChars(string name)
509        {
510            if(String.IsNullOrEmpty(name))
511            {
512                return true;
513            }
514
515            try
516            {
517                if(Path.IsPathRooted(name) && !name.StartsWith(@"\\", StringComparison.Ordinal))
518                {
519                    string root = Path.GetPathRoot(name);
520                    name = name.Substring(root.Length);
521                }
522            }
523            // The Path methods used by ContainsInvalidFileNameChars return argument exception if the filePath contains invalid characters.
524            catch(ArgumentException)
525            {
526                return true;
527            }
528
529            Microsoft.VisualStudio.Shell.Url uri = new Microsoft.VisualStudio.Shell.Url(name);
530
531            // This might be confusing bur Url.IsFile means that the uri represented by the name is either absolut or relative.
532            if(uri.IsFile)
533            {
534                string[] segments = uri.Segments;
535                if(segments != null && segments.Length > 0)
536                {
537                    foreach(string segment in segments)
538                    {
539                        if(IsFilePartInValid(segment))
540                        {
541                            return true;
542                        }
543                    }
544
545                    // Now the last segment should be specially taken care, since that cannot be all dots or spaces.
546                    string lastSegment = segments[segments.Length - 1];
547                    string filePart = Path.GetFileNameWithoutExtension(lastSegment);
548                    if(IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart))
549                    {
550                        return true;
551                    }
552                }
553            }
554            else
555            {
556                // The assumption here is that we got a file name.
557                string filePart = Path.GetFileNameWithoutExtension(name);
558                if(IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart))
559                {
560                    return true;
561                }
562
563
564                return IsFilePartInValid(name);
565            }
566
567            return false;
568        }
569
570        /// Cehcks if a file name is valid.
571        /// </devdoc>
572        /// <param name="fileName">The name of the file</param>
573        /// <returns>True if the file is valid.</returns>
574        public static bool IsFileNameInvalid(string fileName)
575        {
576            if(String.IsNullOrEmpty(fileName))
577            {
578                return true;
579            }
580
581            if(IsFileNameAllGivenCharacter('.', fileName) || IsFileNameAllGivenCharacter(' ', fileName))
582            {
583                return true;
584            }
585
586
587            return IsFilePartInValid(fileName);
588
589        }
590
591        /// <summary>
592        /// Helper method to call a converter explicitely to convert to an enum type
593        /// </summary>
594        /// <typeparam name="T">THe enum to convert to</typeparam>
595        /// <typeparam name="V">The converter that will be created</typeparam>
596        /// <param name="value">The enum value to be converted to</param>
597        /// <param name="typeToConvert">The type to convert</param>
598        /// <param name="culture">The culture to use to read the localized strings</param>
599        /// <returns></returns>
600        [CLSCompliant(false)]
601        public static object ConvertToType<T>(T value, Type typeToConvert, CultureInfo culture)
602            where T : struct
603        {
604            EnumConverter converter = GetEnumConverter<T>();
605            if(converter == null)
606            {
607                return null;
608            }
609            if(converter.CanConvertTo(typeToConvert))
610            {
611                return converter.ConvertTo(null, culture, value, typeToConvert);
612            }
613
614            return null;
615        }
616
617        /// <summary>
618        /// Helper method for converting from a string to an enum using a converter.
619        /// </summary>
620        /// <typeparam name="T"></typeparam>
621        /// <param name="value"></param>
622        /// <param name="culture">The culture to use to read the localized strings</param>
623        /// <returns></returns>
624        [CLSCompliant(false)]
625        public static Nullable<T> ConvertFromType<T>(string value, CultureInfo culture)
626            where T : struct
627        {
628            Nullable<T> returnValue = new Nullable<T>();
629
630            returnValue = returnValue.GetValueOrDefault();
631
632            if(value == null)
633            {
634                return returnValue;
635            }
636
637            EnumConverter converter = GetEnumConverter<T>();
638            if(converter == null)
639            {
640                return returnValue;
641            }
642
643            if(converter.CanConvertFrom(value.GetType()))
644            {
645                object converted = converter.ConvertFrom(null, culture, value);
646
647                if(converted != null && (converted is T))
648                {
649                    returnValue = (T)converted;
650                }
651            }
652
653            return returnValue;
654        }
655
656
657        /// <summary>
658        /// Sets a string value from an enum
659        /// </summary>
660        /// <typeparam name="T">The enum type</typeparam>
661        /// <param name="enumValue">The value of teh enum.</param>
662        /// <returns></returns>
663        [CLSCompliant(false)]
664        public static string SetStringValueFromConvertedEnum<T>(T enumValue, CultureInfo culture)
665            where T : struct
666        {
667            string convertToType = ConvertToType<T>(enumValue, typeof(string), culture) as string;
668
669            if(convertToType == null)
670            {
671                return String.Empty;
672            }
673
674            return convertToType;
675        }
676
677
678        /// <summary>
679        /// Initializes the in memory project. Sets BuildEnabled on the project to true.
680        /// </summary>
681        /// <param name="engine">The build engine to use to create a build project.</param>
682        /// <param name="fullProjectPath">The full path of the project.</param>
683        /// <returns>A loaded msbuild project.</returns>
684        internal static MSBuild.Project InitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath)
685        {
686            if(String.IsNullOrEmpty(fullProjectPath))
687            {
688                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fullProjectPath");
689            }
690
691            // Call GetFullPath to expand any relative path passed into this method.
692            fullProjectPath = Path.GetFullPath(fullProjectPath);
693
694
695            // Check if the project already has been loaded with the fullProjectPath. If yes return the build project associated to it.
696            List<MSBuild.Project> loadedProject = new List<MSBuild.Project>(buildEngine.GetLoadedProjects(fullProjectPath));
697            MSBuild.Project buildProject = loadedProject != null && loadedProject.Count > 0 && loadedProject[0] != null ? loadedProject[0] : null;
698
699            if(buildProject == null)
700            {
701               buildProject = buildEngine.LoadProject(fullProjectPath);
702            }
703
704            return buildProject;
705        }
706
707        /// <summary>
708        /// Loads a project file for the file. If the build project exists and it was loaded with a different file then it is unloaded first. 
709        /// </summary>
710        /// <param name="engine">The build engine to use to create a build project.</param>
711        /// <param name="fullProjectPath">The full path of the project.</param>
712        /// <param name="exitingBuildProject">An Existing build project that will be reloaded.</param>
713        /// <returns>A loaded msbuild project.</returns>
714        internal static MSBuild.Project ReinitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath, MSBuild.Project exitingBuildProject)
715        {
716            // If we have a build project that has been loaded with another file unload it.
717            try
718            {
719                if(exitingBuildProject != null && exitingBuildProject.ProjectCollection != null && !NativeMethods.IsSamePath(exitingBuildProject.FullPath, fullProjectPath))
720                {
721                    buildEngine.UnloadProject(exitingBuildProject);
722                }
723            }
724            // We  catch Invalid operation exception because if the project was unloaded while we touch the ParentEngine the msbuild API throws. 
725            // Is there a way to figure out that a project was unloaded?
726            catch(InvalidOperationException)
727            {
728            }
729
730            return Utilities.InitializeMsBuildProject(buildEngine, fullProjectPath);
731        }
732
733        /// <summary>
734        /// Initialize the build engine. Sets the build enabled property to true. The engine is initialzed if the passed in engine is null or does not have its bin path set.
735        /// </summary>
736        /// <param name="engine">An instance of MSBuild.ProjectCollection build engine, that will be checked if initialized.</param>
737        /// <param name="engine">The service provider.</param>
738        /// <returns>The buildengine to use.</returns>
739        internal static MSBuild.ProjectCollection InitializeMsBuildEngine(MSBuild.ProjectCollection existingEngine, IServiceProvider serviceProvider)
740        {
741            if(serviceProvider == null)
742            {
743                throw new ArgumentNullException("serviceProvider");
744            }
745
746            if(existingEngine == null)
747            {
748                MSBuild.ProjectCollection buildEngine = MSBuild.ProjectCollection.GlobalProjectCollection;
749                return buildEngine;
750            }
751
752            return existingEngine;
753        }
754
755        /// <summary>
756        /// Gets an instance of an EnumConverter for enums that have PropertyPageTypeConverter attribute
757        /// </summary>
758        /// <typeparam name="T">The type to search for the PropertyPageTypeConverter attribute.</typeparam>
759        /// <returns>An instance of an enum converter, or null if none found.</returns>
760        private static EnumConverter GetEnumConverter<T>()
761            where T : struct
762        {
763            object[] attributes = typeof(T).GetCustomAttributes(typeof(PropertyPageTypeConverterAttribute), true);
764
765            // There should be only one PropertyPageTypeConverterAttribute defined on T
766            if(attributes != null && attributes.Length == 1)
767            {
768
769                Debug.Assert(attributes[0] is PropertyPageTypeConverterAttribute, "The returned attribute must be an attribute is PropertyPageTypeConverterAttribute");
770                PropertyPageTypeConverterAttribute converterAttribute = (PropertyPageTypeConverterAttribute)attributes[0];
771
772                if(converterAttribute.ConverterType.IsSubclassOf(typeof(EnumConverter)))
773                {
774                    return Activator.CreateInstance(converterAttribute.ConverterType) as EnumConverter;
775                }
776            }
777
778            return null;
779        }
780
781        /// <summary>>
782        /// Checks if the file name is all the given character.
783        /// </summary>
784        private static bool IsFileNameAllGivenCharacter(char c, string fileName)
785        {
786            // A valid file name cannot be all "c" .
787            int charFound = 0;
788            for(charFound = 0; charFound < fileName.Length && fileName[charFound] == c; ++charFound) ;
789            if(charFound >= fileName.Length)
790            {
791                return true;
792            }
793
794            return false;
795        }
796
797        /// <summary>
798        /// Checks whether a file part contains valid characters. The file part can be any part of a non rooted path.
799        /// </summary>
800        /// <param name="filePart"></param>
801        /// <returns></returns>
802        private static bool IsFilePartInValid(string filePart)
803        {
804            if(String.IsNullOrEmpty(filePart))
805            {
806                return true;
807            }
808            String reservedName = "(\\b(nul|con|aux|prn)\\b)|(\\b((com|lpt)[0-9])\\b)";
809            String invalidChars = @"([/?:&\\*<>|#%" + '\"' + "])";
810            String regexToUseForFileName = reservedName + "|" + invalidChars;
811            String fileNameToVerify = filePart;
812
813            // Define a regular expression that covers all characters that are not in the safe character sets.
814            // It is compiled for performance.
815
816            // The filePart might still be a file and extension. If it is like that then we must check them separately, since different rules apply
817            string extension = String.Empty;
818            try
819            {
820                extension = Path.GetExtension(filePart);
821            }
822            // We catch the ArgumentException because we want this method to return true if the filename is not valid. FilePart could be for example #¤&%"¤&"% and that would throw ArgumentException on GetExtension
823            catch(ArgumentException)
824            {
825                return true;
826            }
827
828            if(!String.IsNullOrEmpty(extension))
829            {
830                // Check the extension first
831                String regexToUseForExtension = invalidChars;
832                Regex unsafeCharactersRegex = new Regex(regexToUseForExtension, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
833                bool isMatch = unsafeCharactersRegex.IsMatch(extension);
834                if(isMatch)
835                {
836                    return isMatch;
837                }
838
839                // We want to verify here everything but the extension.
840                // We cannot use GetFileNameWithoutExtension because it might be that for example (..\\filename.txt) is passed in asnd that should fail, since that is not a valid filename.
841                fileNameToVerify = filePart.Substring(0, filePart.Length - extension.Length);
842
843                if(String.IsNullOrEmpty(fileNameToVerify))
844                {
845                    return true;
846                }
847            }
848
849            // We verify CLOCK$ outside the regex since for some reason the regex is not matching the clock\\$ added.
850            if(String.Compare(fileNameToVerify, "CLOCK$", StringComparison.OrdinalIgnoreCase) == 0)
851            {
852                return true;
853            }
854
855            Regex unsafeFileNameCharactersRegex = new Regex(regexToUseForFileName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
856            return unsafeFileNameCharactersRegex.IsMatch(fileNameToVerify);
857        }
858
859        /// <summary>
860        /// Copy a directory recursively to the specified non-existing directory
861        /// </summary>
862        /// <param name="source">Directory to copy from</param>
863        /// <param name="target">Directory to copy to</param>
864        public static void RecursivelyCopyDirectory(string source, string target)
865        {
866            // Make sure it doesn't already exist
867            if(Directory.Exists(target))
868                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture), target));
869
870            Directory.CreateDirectory(target);
871            DirectoryInfo directory = new DirectoryInfo(source);
872
873            // Copy files
874            foreach(FileInfo file in directory.GetFiles())
875            {
876                file.CopyTo(Path.Combine(target, file.Name));
877            }
878
879            // Now recurse to child directories
880            foreach(DirectoryInfo child in directory.GetDirectories())
881            {
882                RecursivelyCopyDirectory(child.FullName, Path.Combine(target, child.Name));
883            }
884        }
885
886        /// <summary>
887        /// Canonicalizes a file name, including:
888        ///  - determines the full path to the file
889        ///  - casts to upper case
890        /// Canonicalizing a file name makes it possible to compare file names using simple simple string comparison.
891        /// 
892        /// Note: this method does not handle shared drives and UNC drives.
893        /// </summary>
894        /// <param name="anyFileName">A file name, which can be relative/absolute and contain lower-case/upper-case characters.</param>
895        /// <returns>Canonicalized file name.</returns>
896        internal static string CanonicalizeFileName(string anyFileName)
897        {
898            // Get absolute path
899            // Note: this will not handle UNC paths
900            FileInfo fileInfo = new FileInfo(anyFileName);
901            string fullPath = fileInfo.FullName;
902
903            // Cast to upper-case
904            fullPath = fullPath.ToUpper(CultureInfo.CurrentCulture);
905
906            return fullPath;
907        }
908
909
910        /// <summary>
911        /// Determines if a file is a template.
912        /// </summary>
913        /// <param name="fileName">The file to check whether it is a template file</param>
914        /// <returns>true if the file is a template file</returns>
915        internal static bool IsTemplateFile(string fileName)
916        {
917            if(String.IsNullOrEmpty(fileName))
918            {
919                return false;
920            }
921
922            string extension = Path.GetExtension(fileName);
923            return (String.Compare(extension, ".vstemplate", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(extension, ".vsz", StringComparison.OrdinalIgnoreCase) == 0);
924        }
925
926        /// <summary>
927        /// Retrives the configuration and the platform using the IVsSolutionBuildManager2 interface.
928        /// </summary>
929        /// <param name="serviceProvider">A service provider.</param>
930        /// <param name="hierarchy">The hierrachy whose configuration is requested.</param>
931        /// <param name="configuration">The name of the active configuration.</param>
932        /// <param name="platform">The name of the platform.</param>
933        /// <returns>true if successfull.</returns>
934        internal static bool TryGetActiveConfigurationAndPlatform(System.IServiceProvider serviceProvider, IVsHierarchy hierarchy, out string configuration, out string platform)
935        {
936            if(serviceProvider == null)
937            {
938                throw new ArgumentNullException("serviceProvider");
939            }
940
941            if(hierarchy == null)
942            {
943                throw new ArgumentNullException("hierarchy");
944            }
945
946            configuration = String.Empty;
947            platform = String.Empty;
948
949            IVsSolutionBuildManager2 solutionBuildManager = serviceProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2;
950
951            if(solutionBuildManager == null)
952            {
953                return false;
954            }
955
956            IVsProjectCfg[] activeConfigs = new IVsProjectCfg[1];
957            ErrorHandler.ThrowOnFailure(solutionBuildManager.FindActiveProjectCfg(IntPtr.Zero, IntPtr.Zero, hierarchy, activeConfigs));
958
959            IVsProjectCfg activeCfg = activeConfigs[0];
960
961            // Can it be that the activeCfg is null?
962            System.Diagnostics.Debug.Assert(activeCfg != null, "Cannot find the active configuration");
963
964            string canonicalName;
965            ErrorHandler.ThrowOnFailure(activeCfg.get_CanonicalName(out canonicalName));
966
967            return ProjectConfig.TrySplitConfigurationCanonicalName(canonicalName, out configuration, out platform);
968        }
969
970        /// <summary>
971        /// Determines whether the shell is in command line mode.
972        /// </summary>
973        /// <param name="serviceProvider">A reference to a Service Provider.</param>
974        /// <returns>true if the shell is in command line mode. false otherwise.</returns>
975        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
976        internal static bool IsShellInCommandLineMode(System.IServiceProvider serviceProvider)
977        {
978            if(serviceProvider == null)
979            {
980                throw new ArgumentNullException("serviceProvider");
981            }
982
983            IVsShell shell = serviceProvider.GetService(typeof(SVsShell)) as IVsShell;
984            if(shell == null)
985            {
986                throw new InvalidOperationException();
987            }
988
989            object isInCommandLineModeAsObject;
990            ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out isInCommandLineModeAsObject));
991
992            return ((bool)isInCommandLineModeAsObject);
993        }
994    }
995}