#region Copyright 2009-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using System.Reflection; using System.Diagnostics; using System.IO; using System.Text; namespace CSharpTest.Net.Utils { /// /// Utility class for obtaining information about the currently running /// process and AppDomain /// partial class ProcessInfo { /// Returns the string '[Unknown]' const string UNKNOWN = "[Unknown]"; #region Process Data /// Returns the current process id public readonly Int32 ProcessId = 0; /// Returns the current process name without an extension public readonly string ProcessName = UNKNOWN; /// Returns the file path to the exe for this process public readonly string ProcessFile = UNKNOWN; #endregion #region AppDomain Data /// Returns the current AppDomain's friendly name public readonly string AppDomainName = UNKNOWN; /// Returns the entry-point assembly or the highest stack assembly public readonly Assembly EntryAssembly = typeof(ProcessInfo).Assembly; /// Returns the product version of the entry assembly public readonly Version ProductVersion= new Version(); /// Returns the product name of the entry assembly public readonly string ProductName= UNKNOWN; /// Returns the company name of the entry assembly public readonly string CompanyName= UNKNOWN; #endregion #region Misc /// Returns true if a debugger is attached to the process public readonly bool IsDebugging = false; #endregion #region Derived Paths /// /// Returns the HKCU or HKLM path for this software application based /// on the process that is running: Software\{CompanyName}\{ProductName} /// public readonly string RegistrySoftwarePath = UNKNOWN; /// /// Returns the roaming user profile path for the currently running software /// application: {SpecialFolder.ApplicationData}\{CompanyName}\{ProductName} /// public readonly string ApplicationData = UNKNOWN; /// /// Returns the non-roaming user profile path for the currently running software /// application: {SpecialFolder.LocalApplicationData}\{CompanyName}\{ProductName} /// public readonly string LocalApplicationData = UNKNOWN; /// /// Returns a default log file name derived as: /// {SpecialFolder.LocalApplicationData}\{CompanyName}\{ProductName}\{AppDomainName}.txt /// public readonly string DefaultLogFile = UNKNOWN; #endregion /// /// This is some ugly code, the intent is to be able to answer the above questions in /// a wide array of environments. I admit now this may fail eventually. /// public ProcessInfo() { try { EntryAssembly = FindEntryAssembly(); AssemblyName entryAsmName = EntryAssembly.GetName(); ProcessName = entryAsmName.Name; ProcessFile = EntryAssembly.Location; ProductVersion = entryAsmName.Version; // read product name from attributes object[] attrs = EntryAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true); if (attrs.Length > 0) ProductName = ((AssemblyProductAttribute)attrs[0]).Product; // read company name from attributes attrs = EntryAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); if (attrs.Length > 0) CompanyName = ((AssemblyCompanyAttribute)attrs[0]).Company; } catch { } try { //this can fail when not fully-trusted System.Diagnostics.Process thisProcess = System.Diagnostics.Process.GetCurrentProcess(); ProcessId = thisProcess.Id; ProcessModule module = thisProcess.MainModule; if (!String.IsNullOrEmpty(module.ModuleName)) ProcessName = thisProcess.MainModule.ModuleName; if (!String.IsNullOrEmpty(module.FileName)) ProcessFile = thisProcess.MainModule.FileName; if (module.FileVersionInfo != null) { FileVersionInfo fver = module.FileVersionInfo; ProductVersion = new Version(fver.ProductMajorPart, fver.ProductMinorPart, fver.ProductBuildPart, fver.ProductPrivatePart); if(!String.IsNullOrEmpty(fver.CompanyName)) CompanyName = fver.CompanyName; if (!String.IsNullOrEmpty(fver.ProductName)) ProductName = fver.ProductName; } } catch { } try { IsDebugging = System.Diagnostics.Debugger.IsAttached; } catch { } try { AppDomainName = AppDomain.CurrentDomain.FriendlyName; } catch { } if (String.IsNullOrEmpty(AppDomainName)) AppDomainName = ProcessName; //Before we go further, we are going to make sure that the following fields are safe //for use in file system api calls by removing the following characters: /\:*?'"<>| ProcessName = SafeName(ProcessName); AppDomainName = SafeName(AppDomainName); ProductName = SafeName(ProductName); CompanyName = SafeName(CompanyName); RegistrySoftwarePath = String.Format(@"Software\{0}\{1}", CompanyName, ProductName); try { ApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); ApplicationData = Path.Combine(Path.Combine(ApplicationData, CompanyName), ProductName); } catch { try { ApplicationData = Path.Combine(Path.Combine(Path.GetTempPath(), CompanyName), ProductName); } catch { ApplicationData = null; } //you have no access to files } try { LocalApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); LocalApplicationData = Path.Combine(Path.Combine(LocalApplicationData, CompanyName), ProductName); DefaultLogFile = Path.Combine(LocalApplicationData, AppDomainName + ".txt"); } catch { try { LocalApplicationData = Path.Combine(Path.Combine(Path.GetTempPath(), CompanyName), ProductName); DefaultLogFile = Path.Combine(LocalApplicationData, AppDomainName + ".txt"); } catch { DefaultLogFile = LocalApplicationData = null; } //you have no access to files } } /// /// Copy from StringUtils /// private static string SafeName(string name) { StringBuilder sbName = new StringBuilder(); foreach (char ch in name) { if (ch >= ' ' && ch != '/' && ch != '\\' && ch != ':' && ch != '*' && ch != '?' && ch != '\'' && ch != '"' && ch != '<' && ch != '>' && ch != '|' && !Char.IsControl(ch)) sbName.Append(ch); } return sbName.ToString(); } private static Assembly FindEntryAssembly() { Assembly asm = Assembly.GetEntryAssembly(); if (asm != null) return asm; // Find the first non-Microsoft assembly on the stack try { StackTrace trace = new StackTrace(false); int count = trace.FrameCount; while (count > 0) { StackFrame frame = trace.GetFrame(count - 1);//top of the call stack asm = frame.GetMethod().ReflectedType.Assembly; //first non-Microsoft assembly will have to do... object[] attrs = asm.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); if (attrs.Length > 0 && ((AssemblyCompanyAttribute)attrs[0]).Company.IndexOf("Microsoft", StringComparison.OrdinalIgnoreCase) >= 0) count--; else break; } if(asm != null) return asm; if (null != (asm = Assembly.GetCallingAssembly())) return asm; } catch { } return typeof(ProcessInfo).Assembly;//all else fails... } } }