|
Mon
Jan 25 2010 |
Find Conflicting Assembly References |
|
A small test to help resolve the compiler warning:
Point it to the build folder of your project and it will look for referenced assemblies with the same short name but different full names. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using NUnit.Framework; namespace MyProject { [TestFixture] public class UtilityTest { [Test] public void FindConflictingReferences() { var assemblies = GetAllAssemblies(@"E:\dev\myapp\myproject\bin\debug"); var references = GetReferencesFromAllAssemblies(assemblies); var groupsOfConflicts = FindReferencesWithTheSameShortNameButDiffererntFullNames(references); foreach (var group in groupsOfConflicts) { Console.Out.WriteLine("Possible conflicts for {0}:", group.Key); foreach (var reference in group) { Console.Out.WriteLine("{0} references {1}", reference.Assembly.Name.PadRight(25), reference.ReferencedAssembly.FullName); } } } private IEnumerable<IGrouping<string, Reference>> FindReferencesWithTheSameShortNameButDiffererntFullNames(List<Reference> references) { return from reference in references group reference by reference.ReferencedAssembly.Name into referenceGroup where referenceGroup.ToList().Select(reference => reference.ReferencedAssembly.FullName).Distinct().Count() > 1 select referenceGroup; } private List<Reference> GetReferencesFromAllAssemblies(List<Assembly> assemblies) { var references = new List<Reference>(); foreach (var assembly in assemblies) { foreach (var referencedAssembly in assembly.GetReferencedAssemblies()) { references.Add(new Reference { Assembly = assembly.GetName(), ReferencedAssembly = referencedAssembly }); } } return references; } private List<Assembly> GetAllAssemblies(string path) { var files = new List<FileInfo>(); var directoryToSearch = new DirectoryInfo(path); files.AddRange(directoryToSearch.GetFiles("*.dll", SearchOption.AllDirectories)); files.AddRange(directoryToSearch.GetFiles("*.exe", SearchOption.AllDirectories)); return files.ConvertAll(file => Assembly.LoadFile(file.FullName)); } private class Reference { public AssemblyName Assembly { get; set; } public AssemblyName ReferencedAssembly { get; set; } } } } |
|
Mon
Apr 6 2009 |
Resharper live templates for NUnit |
aae Assert.AreEqual aane Assert.AreNotEqual aif Assert.IsFalse ait Assert.IsTrue ain Assert.IsNull ainn Assert.IsNotNull setup NUnit [Setup] method teardown NUnit [TearDown] method test NUnit [Test] method |
|
Sun
Aug 31 2008 |
Build ASP.NET WebService from the Command-line |
|
This NAnt target will build the project into a new folder with a (single) precompiled DLL. All source files and other non-deployment files are removed. <target name="publish.webservice"> <exec basedir="." program="${msbuild.exe}" commandline=" ${web.project.file} /nologo /p:OutDir=${publish.webservice.dir}bin /p:WebProjectOutputDir=${publish.webservice.dir}" workingdir="." failonerror="true" /> </target> |
|
Mon
Jun 25 2007 |
HttpContext.Cache vs. HttpContext.Items |
|
HttpContext.Cache is simply a pointer the HttpRuntime.Cache also known as the application cache. This can be verified by opening System.Web with Reflector. The MSDN documentation for this member is misleading: “Gets the Cache object for the current HTTP request”. Information stored here is global to the application domain, not a specific request. To store per-request information use HttpContext.Items. |
|
Fri
Apr 20 2007 |
Impersonate User |
/// <summary> /// Impersonate a user within the current thread. /// This class will automatically revert the user to the original user /// identity when this class is disposed. Use this class with the /// C# using or a try/catch block to ensure the user is reverted /// after an exception. /// /// Suggested usage: /// using (new ImpersonateHelper("domain", "joe", "password")) /// { /// ...code to run as joe /// } /// ...code to run as orignial user /// /// Note, this implementation is vulnerable to "exception filter" attacks /// as described below. Evaluate it against the risk of other attacks /// such as the credential store used to impersonate. /// http://blogs.msdn.com/shawnfa/archive/2005/03/22/400749.aspx /// </summary> public class ImpersonationHelper : IDisposable { IntPtr m_tokenHandle = new IntPtr(0); WindowsImpersonationContext m_impersonatedUser; #region Win32 API Declarations const int LOGON32_PROVIDER_DEFAULT = 0; const int LOGON32_LOGON_INTERACTIVE = 2; //This parameter causes LogonUser to create a primary token. [DllImport("advapi32.dll", SetLastError=true)] public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] public extern static bool CloseHandle(IntPtr handle); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); #endregion /// <summary> /// Constructor. Impersonates the requested user. Impersonation lasts until /// the instance is disposed. /// </summary> public ImpersonationHelper(string domain, string user, string password) { // Call LogonUser to obtain a handle to an access token. bool returnValue = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref m_tokenHandle); if (false == returnValue) { int ret = Marshal.GetLastWin32Error(); throw new System.ComponentModel.Win32Exception(ret); } // Impersonate m_impersonatedUser = new WindowsIdentity(m_tokenHandle).Impersonate(); } #region IDisposable Pattern /// <summary> /// Revert to original user and cleanup. /// </summary> protected virtual void Dispose(bool disposing) { if (disposing) { // Revert to original user identity if (m_impersonatedUser != null) m_impersonatedUser.Undo(); } // Free the tokens. if (m_tokenHandle != IntPtr.Zero) CloseHandle(m_tokenHandle); } /// <summary> /// Explicit dispose. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Destructor /// </summary> ~ImpersonationHelper() { Dispose(false); } #endregion } |
|
Tue
Apr 17 2007 |
Log4Net + ASP.Net |
|
In web applications, Log4Net’s ThreadContext properties (typically items like the current user) should be set immediately before each logging call. Normally these properties are set in Global.Session_Start(). However, log4net internally stores these properties in thread local storage. Using Reflector to disassemble log4net.dll (v1.2.10), start with log4net.ThreadContext which leads to ThreadContextProperties.get_Item() and then to ThreadContextProperties.GetProperties(). The disassembly for this method: internal PropertiesDictionary GetProperties(bool create) { PropertiesDictionary data; data = (PropertiesDictionary) Thread.GetData(s_threadLocalSlot); if ((data == null) && create) { data = new PropertiesDictionary(); Thread.SetData(s_threadLocalSlot, data); } return data; } The call to Thread.GetData() is the problem. ASP.Net, when under load, may switch threads when processing a single request (see ThreadStatic, CallContext and HttpContext in ASP.Net). You may initialize log4net with the current user on one thread, then attempt to log on a different thread. Log4net.LogicalThreadContext() suffers a similar problem as it uses CallContext to store its data rather than HttpContext. For reliable logging information, set these ThreadContext properties each time you log an entry. The additional processing time will be negligable in most cases. Further, as a general rule of thumb, use HttpContext to store request-specific information in ASP.Net applications. 10 minutes after finishing this post I come across this post, also from piers7: |
|
Sun
Mar 25 2007 |
Formatting File Sizes |
|
In Windows Explorer file sizes are displayed in two different formats. In the Properties page, file sizes are displayed in bytes, KB, MB, GB or TB depending on the size. The StrFormatByteSize() Win32 API can reproduce this format. The second format is used under the “Size” column when viewing a folder. File sizes are displayed in whole KB with thousands separators. I have not found a API call to reproduce this format. The StrFormatKBSize() function is close but differs (it displays two decimal places). Here are two pure C# functions to reproduce these formats: /// <summary> /// Converts a number value into a string that represents the number /// expressed in whole kilobytes. This is a format similar to the /// Windows Explorer "Size" column. /// </summary> public static string FileSizeToStringKB(long fileSize) { return string.Format("{0:n0} KB", Math.Ceiling((double)fileSize / 1024)); } /// <summary> /// Converts a numeric value into a string that represents the number /// expressed as a size value in bytes, kilobytes, megabytes, gigabytes, /// or terabytes depending on the size. Output is identical to /// StrFormatByteSize() in shlwapi.dll. This is a format similar to /// the Windows Explorer file Properties page. For example: /// 532 -> 532 bytes /// 1240 -> 1.21 KB /// 235606 -> 230 KB /// 5400016 -> 5.14 MB /// </summary> /// <remarks> /// It was surprisingly difficult to emulate the StrFormatByteSize() function /// due to a few quirks. First, the function only displays three digits: /// - displays 2 decimal places for values under 10 (e.g. 2.12 KB) /// - displays 1 decimal place for values under 100 (e.g. 88.2 KB) /// - displays 0 decimal places for values under 1000 (e.g. 532 KB) /// - jumps to the next unit of measure for values over 1000 (e.g. 0.97 MB) /// The second quirk: insiginificant digits are truncated rather than /// rounded. The original function likely uses integer math. /// This implementation was tested to 100 TB. /// </remarks> public static string FileSizeToString(long fileSize) { double value; string unit; if (fileSize < 1024) { return string.Format("{0} bytes", fileSize); } else { value = fileSize; value = value / 1024; unit = "KB"; if (value >= 1000) { value = Math.Floor( value ); value = value / 1024; unit = "MB"; } if (value >= 1000) { value = Math.Floor( value ); value = value / 1024; unit = "GB"; } if (value >= 1000) { value = Math.Floor( value ); value = value / 1024; unit = "TB"; } if (value < 10) { value = Math.Floor( value * 100 ) / 100; return string.Format("{0:f2} {1}", value, unit); } else if (value < 100) { value = Math.Floor( value * 10) / 10; return string.Format("{0:f1} {1}", value, unit); } else { value = Math.Floor( value * 1) / 1; return string.Format("{0:f0} {1}", value, unit); } } } |
|
Fri
Jan 19 2007 |
CLR Debugging |
|
To get started with debugging the CLR see Tess Ferrandez’s post on getting .NET memory dumps and Associating Windbg with .dmp files. See her other posts for mining these dumps for useful information. To debug ASP.Net worker process crashes, create a batch file named debug_crash_iis5.bat in windbg folder with: To dump (non-crashing) exceptions from ASP.Net, create a batch file named debug_monitor_iis5.bat with: and another file named TrackCLR.cfg with: <ADPLUS> <SETTINGS> <RUNMODE>CRASH</RUNMODE> </SETTINGS> <PRECOMMANDS> <CMD>!load clr10\sos</CMD> </PRECOMMANDS> <EXCEPTIONS> <OPTION>NoDumpOnFirstChance</OPTION> <OPTION>NoDumpOnSecondChance</OPTION> <CONFIG><!-- This is for the CLR exception --> <CODE>clr</CODE> <ACTIONS1>Log</ACTIONS1> <CUSTOMACTIONS1>!cen;!clrstack;.dump /ma /u c:\exceptiondump.dmp;gn </CUSTOMACTIONS1> <RETURNACTION1>GN</RETURNACTION1> </CONFIG> </EXCEPTIONS> </ADPLUS> After opening a .dmp file with windbg, the most useful commands are: |
|
Thu
Jan 11 2007 |
Catching Unhandled ASP.Net Exceptions |
|
Exceptions that occur off the worker process thread are silently swallowed by ASP.NET 1.1. Use the method described in MS article 911816 to create an HttpModule to catch the exceptions. The follow is adapted for ASP.NET 1.1 without requiring registration in the GAC and a strong name. It also logs to the EventLog as well as sending an email as Add the following code to UnhandledExceptionModule.cs: using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Web; namespace WebMonitor { public class UnhandledExceptionModule: IHttpModule { static int _unhandledExceptionCount = 0; static string _sourceName = null; static object _initLock = new object(); static bool _initialized = false; static string _mailServer = "mymailserver"; static string _mailTo = "abc@xyz.com"; public void Init(HttpApplication app) { // Do this one time for each AppDomain. if (!_initialized) { lock (_initLock) { if (!_initialized) { _sourceName = string.Format(CultureInfo.InvariantCulture, "ASP.NET {0}.{1}.{2}.0", System.Environment.Version.Major, System.Environment.Version.Minor, System.Environment.Version.Build); if (!EventLog.SourceExists(_sourceName)) { throw new Exception(String.Format(CultureInfo.InvariantCulture, "There is no EventLog source named '{0}'.", _sourceName)); } AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException); _initialized = true; LogMessage("UnhandledExceptionModule started", "", EventLogEntryType.Information); } } } } public void Dispose() { } void OnUnhandledException(object o, UnhandledExceptionEventArgs e) { // Let this occur one time for each AppDomain. if (Interlocked.Exchange(ref _unhandledExceptionCount, 1) != 0) return; StringBuilder message = new StringBuilder("\r\n\r\nUnhandledException logged by UnhandledExceptionModule.dll:\r\n\r\nappId="); string appId = (string) AppDomain.CurrentDomain.GetData(".appId"); if (appId != null) { message.Append(appId); } Exception currentException = null; for (currentException = (Exception)e.ExceptionObject; currentException != null; currentException = currentException.InnerException) { message.AppendFormat("\r\n\r\ntype={0}\r\n\r\nmessage={1}\r\n\r\nstack=\r\n{2}\r\n\r\n", currentException.GetType().FullName, currentException.Message, currentException.StackTrace); } LogMessage("UnhandledExceptionModule exception", message.ToString(), EventLogEntryType.Error); } void LogMessage(string subject, string message, EventLogEntryType severity) { try { EventLog Log = new EventLog(); Log.Source = _sourceName; Log.WriteEntry(subject + "\n" + message, severity); } catch {} try { System.Web.Mail.MailMessage msg = new System.Web.Mail.MailMessage(); msg.Body = message; msg.To = _mailTo; msg.Subject = severity.ToString() + ": " + subject; msg.From = _mailTo; System.Web.Mail.SmtpMail.SmtpServer = _mailServer; System.Web.Mail.SmtpMail.Send(msg); } catch {} } } } Open a Visual Studio Command Prompt and run: csc /t:library /r:system.web.dll,system.dll UnhandledExceptionModule.cs Place the DLL in your bin folder Add following to your web.config: <system.web> <httpModules> <add type="WebMonitor.UnhandledExceptionModule, UnhandledExceptionModule" name="UnhandledExceptionModule"/> </httpModules> </system.web> Here is sample code to trigger an unhandled exception using System.Threading; Timer t; private void btnTest_Click(object sender, System.EventArgs e) { t = new Timer(new TimerCallback(t_Elapsed), null, 250, Timeout.Infinite); } private void t_Elapsed(object state) { throw new Exception("My unhandled exception"); } |