Monday, November 29, 2010

Installing .NET Nuke

I read DotNetNuke supports OpenID out of the box and I wanted to try it out.  This post covers the installation of DNN.  A future post will cover configuring OpenID.

Environment:
  • Windows 7 64 bit
  • IIS 7.5
  • SQL Server 2008 R2

Downloaded DotNetNuke Community Edition version 5.05.01

Listened to the DotNetNuke Installation Webinar while doing the following:

Right click properties on the zip file. Uncheck Unblock.

Unzipped the community edition.

read .\Documentation\Readme.txt

Open SQL Server Management Studio connect to .\

Created a new database on my local machine named DotNetNukeTest.  Set initial size to 20 MB.

Expand the security node.  Right click Logins 
Click 'New Login...'
Select SQL Server Authentication

login Name: DotNetNuke
password:IloveGravityWorks
Uncheck Enforce password policy
select User Mapping map to DotNetNukeTest and set role membership to db_owner

Moved the folder to c:\www\

Opened Internet Information Services Manager

Expanded the Sites node.

Right click Sites, Add Site
  Site Name: DotNetNukeTest
  Change Application Pool back to DefaultAppPool
  Physical path: C:\www\DotNetNuke_Community_05.05.01_Install

  Port: 8081

right click C:\www\DotNetNuke_Community_05.05.01_Install -> Properties
select Security tab
click Edit
Click Add
Ensure From this location: is set to your computer name
Object name --> IIS AppPool\DefaultAppPool
OK
Grant the DefaultAppPool user modify permissions
OK
OK

Browse to http://localhost:8081 in Chrome

Select Custom install (I always like doing this for new software)

Next through File Permissions Summary.

Select 'SQL Server 2005/2008 Database' instead of SQL Server 2005/2008 (Express File).
Server: "."
Database: "DotNetNukeTest"

Uncheck 'Integrated Security"
User ID: "DotNetNuke"
Password:'IloveGravityWorks'

I fix my SQL Server authentication and then click Next

change the password, and email fields.  I leave SMTP alone.

on the Install Optional Modules I select all modules:
Announcements (04.00.03), Blog (04.00.00), Documents (04.01.00), Events (05.01.04), FAQs (04.04.00), Feedback (05.00.02), FormAndList (05.01.03), Forum (04.05.03), Help (03.00.02), HTML (Community (05.04.03), IFrame (04.04.00), Links (04.00.01), Map (01.00.09), Media (03.03.00), Messaging (01.01.00), NewsFeeds (04.00.01), Reports (05.01.00), Repository (03.01.15), Store (02.01.36), Survey (04.60.00), Taxonomy (01.01.00), Telerik (05.05.00), UsersOnline (05.01.00), Wiki (04.02.00), XML (04.03.05)
Next
Next through Skins and Containers
Next through Language Packs
Next through Authentication Systems:
Next through Providers
For Portal Administrator set password, email address, and Portal Title.
Click 'Start building your new Site!'

Monday, November 22, 2010

Blacklisted Oh No! - More reliable email

I've had email issues for about 1.5 years.  Often times when sending email to a new contact my email doesn't arrive in their inbox because it gets put in their spam filter or doesn't even reach their inbox at all.

At a party this summer I spoke with some network guys who suggested googling email black list and checking to see if my domain was black listed.

The first hit returned mxtoolbox, and a search for my domain did show I was listed once :(
http://www.mxtoolbox.com/SuperTool.aspx?action=blacklist:davidsilvasmith.com

Clicking on the entry I was taken to the listing website which had a button to whitelist myself.

198.173.106.90 was found in NOMOREFUNN!
no-more-funn.moensted.dk -> 127.0.0.7
31/03/02 importet from fiveten after SPAM


Checking back a day later, my domain was no longer listed, and now 2 months later, my emails seem to be getting through to recipients.




Monday, November 15, 2010

Troubleshooting a failed login with correct user name and password

After setting up a new Database in SQL Server 2008 R2 and configuring DotNetNuke (DNN), an ASP.NET application, to connect to the new database I got received this message:
Connection Error(s):
Index #: 0
Source: .Net SqlClient Data Provider
Class: 14
Number: 18456
Message: Sql login failed

I open up SQL Server Management Studio and reset the user password.  Try again from DNN, same error.

I try connecting via SQL Server Management Studio.  Login failed.

I look in event Viewer and see
Login failed for user 'dotnetnuke'. Reason: An attempt to login using SQL authentication failed. Server is configured for Windows authentication only. [CLIENT: <local machine>]

I open up up SQL Server Management Studio and connect to '.'

I right click the SQL Server 10.50.1600 node and

  1. Select properties
  2. Select Security Page
  3. Under Server authentication change it to 'SQL Server and Windows Authentication mode'


Right click the SQL Server 10.50.1600 node again and select 'Restart' from the sub menu.

Back to the application and login is successful.

Monday, October 11, 2010

Access Denied on Adding a Database

Today I ran into a problem where I couldn't run a create script to create a local database in SQL Server Management Studio 2008 running on 64 bit Windows 7 Professional.  This is the error I received:

Msg 5123, Level 16, State 1, Line 1
CREATE FILE encountered operating system error 5(Access is denied.) while attempting to open or create the physical file 'c:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA\xxx.mdf'.
Msg 1802, Level 16, State 4, Line 1
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.

This is how it was fixed on my machine:

  1. Fire up SysInternals Process Monitor
  2. Open the 'Filter' menu
  3. Click 'Filter...'
  4. From the first dropdown select 'Path' in the second dropdown select 'contains'.
  5. For the value enter '\data'
  6. Click the 'OK' button to add the filter.
  7. Retry the operation
  8. Open up one of the NAME NOT FOUND entries with a Detail of Access Denied.
  9. Click the Process tab
  10. Note the process is sqlsvr.exe and the user is NT Authority\Network Service
  11. Navigate to the Data directory in Windows Explorer
  12. Click the Security tab
  13. Click the 'Edit' Button
  14. On the Permissions for 'Data' click the 'Add' button.
  15. For the object name enter 'Network Service'
  16. Click 'OK'
  17. Disregard any errors regarding not being able to set the permissions on files.
  18. Retry the operation in Sql Server Management Studio and it should work!

Monday, October 4, 2010

Unable to rename database in SQL Server Management Studio 2008

Today I wanted to rename a SQL Server 2008 database so I would have a rollback if my create from script went bad.

Upon rename SQL Server Management Studio 2008 gave me the error 'The database could not be exclusively locked to perform the operation. (Microsoft SQL Server, Error: 5030)'

I executed this in a query window connected to the master database:
sp_who2

Looking at the DBName column in the results for each match of the db I was trying to rename I executed 'kill xxx' where xxx was equal to the SPID of the row.

After doing this for all the rows rename succeeded immediately.

Monday, September 27, 2010

Back to Lansing!

Thursday September 9th was my last day at Sonoma Partners, the best Customer Relationship Management (CRM) Dynamics parter in the business.  Sonoma delivers projects on time, on scope, and on budget sometimes receiving amazing unsolicited feedback from satisfied customers.

Amelia Marschall and Jeff McWherter gave me the opportunity to work with talented individuals such as Scott Gowell, and Lauren Colton helping individuals in the greater Lansing area overcome technical challenges and giving back to the community by helping fight extreme poverty and cure preventable diseases, or  working to double the number of college graduates in Michigan.

I started at Gravity Works Design and Development on Monday September 13th.  I've already delivered some great value for customers and had some great times.  I'm looking forward to helping grow the Lansing and Michigan economies.

Monday, June 7, 2010

Windbg Ignoring Sympath when called programatically

Working with mdbglib this weekend my call to .sympath was registering but then none of the symbol files could be found.  This post will show how to resolve this issue and the process I followed to solve it.

Environment Windows Server R2 64 bit.  The app is compiled for x86 and running as a 32 bit app.

  1. lm e
    This listed modules which had errors.
  2. reload -f
    Force a reload of all modules.  Noticed none of them were loading from my path.
  3. Hooked up SysInternals Process Monitor filtered to just my process name and just disk activity.  cleared the output right before doing reload -f
    NOticed it was attempting to load modules from the same directory I was running from.
  4. restarted the application
  5. !sym noisy
    To noisy load symbols.
  6. .reload

    DBGHELP: SymSrv load failure: symsrv.dll
    DBGHELP: wntdll.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\SysWOW64\ntdll.dll - 
    DBGHELP: ntdll - export symbols
    DBGHELP: wuser32.pdb - file not found
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\syswow64\USER32.dll - 
    DBGHELP: USER32 - export symbols
    DBGHELP: System.Windows.Forms.pdb - file not found
    *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    *** ERROR: Module load completed but symbols could not be loaded for C:\Windows\assembly\NativeImages_v2.0.50727_32\System.Windows.Forms\fedf1ba58dced4f0b3f8c457648ceed9\System.Windows.Forms.ni.dll
    DBGHELP: System_Windows_Forms_ni - no symbols loaded

  7. Copied symsrv.dll into my local directory even though it is already in the app path.
  8. Restarted, !sym noisy, .reload
    same results
  9. Filter Process monitor where the path ends in symsrv.dll
    Noticed the process is trying to load symsrv.dll from C:\Windows\SysWOW64\
  10. Copied symsrv.dll to C:\Windows\SysWOW64
    HACK!
  11. restarted, !sym noisy, .reload
    success!
  12. .chain
    Shows dbghelp.dll is loaded from c:\Windows\system32\dbghelp.dll
    MSDN writes symsrv must be installed in the same directory as the copy of dbghelp.dll being loaded.
  13. .extpath C:\Program Files (x86)\Debugging Tools for Windows (x86)\
    sets dbgeng's extension search path to where symsrv.dll is.
  14. .chain
    dbghelp.dll is still loaded from the system32 directory.
  15. Prior to running the debugger I set the environmental variable _NT_DEBUGGER_EXTENSION_PATH to C:\Program Files (x86)\Debugging Tools for Windows (x86)\
  16. Restart, .chain
    dbghelp is loaded from the correct directory! but symsrv.dll is still broken :(
    Looking at the stack in Process monitor shows dbgeng.dll is being loaded from C:\Windows\SysWOW64\dbgeng.dll which calls dbghelp.dll in the same directory.
    Changing process monitor to filter for requests ending in dbgeng.dll shows when the program runs it is looking in the executing applications directory for the dll, not finding it, and then loading it from C:\Windows\SysWOW64\dbgeng.dll
  17. Head over to pinvoke.net to grab the loadlibrary signature and put it in my application.

    [DllImport("kernel32", SetLastError = true)]
    static extern IntPtr LoadLibrary(string lpFileName);

  18. Call LoadLibrary from C# prior to loading my debugger and now the correct dll is bing loaded, but the debugging commands don't return any values :(
  19. Reading the LoadLibrary documentation and then the dll search order documentation I think calling SetDllDirectory may do what I want.
  20. Back to pinvoke.net to grab this signature
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool SetDllDirectory(string lpPathName); 
  21. Call SetDllDirectory prior to loading up the debugger and everything now works as expected!

Here is the code:

internal static class UnsafeNativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool SetDllDirectory(string lpPathName);
}



public static class Utility
{
    public static void ProvideWindbgInstallationDirectory(string path)
    {
        if (Directory.Exists(path) == false)
        {
            throw new DirectoryNotFoundException(path);
        }
        #warning add a check that the files we want exist.  maybe even that they are the correct version?
        bool setPath = UnsafeNativeMethods.SetDllDirectory(path);
        if (setPath == false)
        {
            int error = Marshal.GetLastWin32Error();
            throw new InvalidOperationException("Setting the installation directory failed with error code: " + error.ToString());

        }
    }
}



Monday, March 29, 2010

Using WindDbg and Sosex to Identify an ASP.NET Deadlock

This post will show how to use create an ASP.NET deadlock and then then use WinDbg and sosex to identify it the idea is to practice this before needing it when debugging a production server :)


The environment for my test is

  1. Create a new web application and put this in the code behind for Default.aspx:
    public partial class _Default : Page
    {
        /// <summary>
        /// Locks on page load.
        /// </summary>
        protected void Page_Load(object sender, EventArgs e)
        {
            object lockA = new object();

            object lockB = new object();

            ThreadStart firstJob = new ThreadStart(() => this.DualLockingCall(lockA, lockB));
            Thread firstThread = new Thread(firstJob);
            firstThread.Name = "firstThread";
            firstThread.Start();

            ThreadStart secondJob = new ThreadStart(() => this.DualLockingCall(lockB, lockA));
            Thread secondThread = new Thread(secondJob);
            secondThread.Name = "secondThread";
            secondThread.Start();

            Thread.Sleep(Timeout.Infinite);
        }

        /// <summary>
        /// lock on a then lock on b.
        /// </summary>
        /// <param name="onThis">The first instance to lock on.</param>
        /// <param name="onThat">The second instance to lock on.</param>
        private void DualLockingCall(object onThis, object onThat)
        {
            lock (onThis)
            {
                lock (onThat)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
            }
        }
    }
  2. Navigate to the url like localhost/projectName/Default.aspx
  3. Open a command prompt and navigate to the debugging tools for windows directory.
  4. Run this command adplus -pn w3wp.exe -o c:\ -hang
    1. The hang command takes a memory dump immediately.  It doesn't wait for a hang.
  5. Note the output directory.  It should be the last line adplus printed out.
  6. Run windbg and press ctrl+d to open a memory dump.  Navigate to the dump adplus just created.
  7. type .loadby sos mscorwks - This will load the sos.dll from the same directory mscorwks was loaded from.
  8. type .load pathToSosex\sosex.dll
  9. type !dlk
  10. On my machine I get this output:
    *** WARNING: Unable to verify checksum for LockItUp.DLL
    *** ERROR: Module load completed but symbols could not be loaded for LockItUp.DLL
    Deadlock detected:
    CLR thread 8 holds sync block 0000000002583228 OBJ:00000001bf1f2be0[System.Object]
                 waits sync block 00000000025831e0 OBJ:00000001bf1f2bf8[System.Object]
    CLR thread 9 holds sync block 00000000025831e0 OBJ:00000001bf1f2bf8[System.Object]
                 waits sync block 0000000002583228 OBJ:00000001bf1f2be0[System.Object]
    CLR Thread 8 is waiting at LockItUp._Default.DualLockingCall(System.Object, System.Object)(+0x25 IL)(+0x74 Native)
    CLR Thread 9 is waiting at LockItUp._Default.DualLockingCall(System.Object, System.Object)(+0x25 IL)(+0x74 Native)

    1 deadlock detected.
Easy cheesy in this simulated test eh?



Monday, March 22, 2010

A Test of My Mettle

I attended my first Toastmaster's meeting last Wednesday and experienced special guest Ed Hearn deliver an inspirational thought provoking speech entitled: "The Courage to Try."

Mr. Hearn gave examples of individuals achieving seemingly impossible goals such as Wilma Rudolph winning three Olympic Gold medals despite having a severe physical disability as a child.  In each example the actors overcame daunting obstacles to realize fantastic goals.

During the speech I was thinking about people's opportunities in life, and how I did not have to struggle to complete High School, College, or get a professional job.  I've taken advantage of opportunities which were nearly given to me, but I haven't made my own opportunities.  Mr. Hearn's speech combined with some of my other experiences have made me realize the goals I attempt in life are low risk or low effort. When I take on large (1,000+ hours) personal projects I give up on the projects after investing a small (10-700 hours) amount of effort into them compared to the amount of effort required for completion.  I'm assuming I give up because I don't see results and overvalue the opportunity cost.  IE.  I think "Hmmmmmm...... I've spent 50 hours on this which I could have spent completing items x,y, and z on my todo list instead.  It may take another 500 hours to achieve my goal, or I may not even be able to achieve my goal," and sack the project.

I am not sure if this new thinking will bear fruit.  Unless I can talk myself into believing there is significant value in the act of trying I'm not sure I can justify the cost of the attempt.  Maybe because I plan for the worst and hope for the best.  Under this mentality I assume the 1000+ hours invested will not achieve the stated objective.

Monday, March 15, 2010

Viewing ASP.NET Adaptive Rendering

I remembered when ASP.NET launched hearing about adaptive rendering.  Using ASP.NET I did not notice different renderings.  This post shows how one can observe ASP.NET adaptive renderings using fiddler.

The software used for this post is:
  • Visual Studio 2008 SP1
  • Windows Server 2008 R2 Standard
  • Fiddler v2.2.8.6 64-bit
  • IE 8.0
  1. Using Visual Studio create a new ASP.NET Web Application
  2. Modify Default.aspx.cs as follows:
    public partial class _Default : Page


    {
        /// <summary>
        /// Ignores this pages children and demonstrates different HtmlWriters based on
        /// different request strings.
        /// </summary>
        /// <param name="writer">An HtmlWriter passed by ASP.NET.</param>
        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write(HttpContext.Current.Request.Browser.TagWriter.ToString());

            writer.AddStyleAttribute(HtmlTextWriterStyle.Color, "Red");
            writer.RenderBeginTag(HtmlTextWriterTag.P);
            writer.Write("Red text");
            writer.RenderEndTag();
        }
    }
  3. Run the project and note the port it runs on.  In my case the url is http://localhost:56449.  Running in IE 8 I see:
    System.Web.UI.HtmlTextWriter


    Red text
  4. In Fiddler enter Request Builder, build and execute the following request:
    Get http://ipv4.fiddler:YOURPORT HTTP/1.1
    User-Agent: Mozilla(compatible;MSIE 3.0;)
     Host: ipv4.fiddler:YOURPORT
  5. In the Web Sessions click the session resulting from your request
  6. Select the Inspectors tab and select TextView.  On my machine I see:
    System.Web.UI.Html32TextWriter<p><font color="Red">Red text</font></p>
    Since we specified our user-agent as MSIE 3.0 ASP.NET is rendering the style color using the font tag.
  7. Go back to Fiddler Request Builder, build and execute the following request:
    Get http://ipv4.fiddler:YOURPORT HTTP/1.1
    User-Agent: Mozilla(compatible;MSIE 4.0;)
     Host: ipv4.fiddler:YOURPORT
  8. In the Web Sessions click the session resulting from your request
  9. Select the Inspectors tab and select TextView. On my machine I see:
    System.Web.UI.HtmlTextWriter<p style="color:Red;">Red text</p>
  10. There are a couple of differences between the two responses:
    1. HtmlTextWriter was used whereas in the previous request it was an Html32TextWriter.
    2. ASP.NET renedered the style color using CSS instead of the font tag.

To undestand the mechanics making this happen 4 Guys From Rolla have an article describing the details.

Above I used ipv4.fiddler instead of localhost to enable seeing traffic sent to localhost as described in Fiddler's help.

Monday, March 1, 2010

Equal but different JavaScript style

Someone at work pointed out this surprise in JavaScript: [] == ![] evaluates to true.

Reading this I thought it was stating something is equal to the opposite of itself.  For non technical readers can things be equal without being the same?  Currencies?  People?  In English do people sometimes say same but mean equal?

In programming worlds things which are not the same might be equal, but I expect something that is the opposite of itself to definitely not be equal to itself.  For example 1 is not equal to !1  I decided to discover how can this be?

I started by firing up a debugger and looking at the types [] and ![].  ![] is false*.

Then I went to the ECMA-262 spec:

Section 11.9.1 The Equals Operator (==) points us to 11.9.3 abstract equality comparison.

11.9.3 The Abstract Equality Comparison Algorithm
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
7.If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

9.3 ToNumber
  Given Argument Type Boolean The result is 1 if the argument is true. The result is +0 if the argument is false.

Going back to 11.9.3

9. If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.

Section 9.1 ToPrimitive:
For input type Object

Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.

Section 8.12.8 DefaultValue

When the [[DefaultValue]] internal method of O is called with hint Number, the following steps are taken:

  1. Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
  2. If IsCallable(valueOf) is true then,
    1. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.
    2. If val is a primitive value, return val.
  3. Let toString be the result of calling the [[Get]] internal method of object O with argument "toString".
  4. If IsCallable(toString) is true then,
    1. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.
    2. If str is a primitive value, return str.
  5. Throw a TypeError exception.

valueOf defined in Section 15.2.4.4 does not return a primitive so toString() from step 3 is called resulting in ""

Going back to 11.9.3

5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.

Section 9.3.1 ToNumber Applied to the String Type informs us that whitespace returns 0.

The leaves us with ([].toString() == "") == (![] == 0) which can be confirmed in a debugger and also results in [] == ![] resulting in true.



*To know why ![] is false
See section 11.4.9

11.4.9 Logical NOT Operator ( ! )
The production UnaryExpression : ! UnaryExpression is evaluated as follows:
1.Let expr be the result of evaluating UnaryExpression.
2.Let oldValue be ToBoolean(GetValue(expr)).
3.If oldValue is true, return false.
4.Return true.

Step 2 ToBoolean(GetValue(expr))
Takes us to Section 9.2 ToBoolean where: for Argument Type Object the result is true.

Monday, February 15, 2010

Integrating FxCop into Visual Studio

At my previous company we had Visual Studio Team System (VSTS) which has Code Analysis (FxCop) integrated into Visual Studio. To turn it on one enters project properties and clicks on the Code Analysis tab. Each rule or group of rules is turned on by checking a box and the rules execute on every build of the project. I love FxCop because it teaches me how to be a better programmer, finds issues in my code, and makes my APIs more user friendly. I was disappointed upon discovering Code Analysis is a VSTS feature.

This post shows two options for getting FxCop 1.36 running on a each Visual Studio 2008 build without maintaining an FxCop project separate from a C# project.  On the downside it will run the same set of rules for each project, which works for my scenario but may pose problems when working with legacy code and new development because the legacy code will probably violate many rules.

This instructions assume the following environment:
  • MSbuild Version 3.5.30729.4926
  • 64 bit Windows (otherwise you will need to modify the C:\Program Files (x86) paths )
  • Visual Studio 2008
  • FxCop 1.36
Option #1: Integrating FxCop at the project level.
  1. Install FxCop
  2. Create a file: C:\Program Files (x86)\MSBuild\v3.5\FxCop.targets with the following text:
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <!-- Add FxCop to the list of targets to build -->
        <BuildDependsOn>$(BuildDependsOn);FxCop</BuildDependsOn>
      </PropertyGroup>
      <!-- Define the FxCop target we added above.-->
      <Target Name="FxCop">
        <Message Text="$(MSBuildToolsPath)" />
        <!-- The Condition attribute allows others to use the same project file without breaking their build when FxCop is not installed-->
        <Exec Command="&quot;$(ProgramFiles)\Microsoft FxCop 1.36\FxCopCmd.exe&quot; /file:&quot;$(TargetPath)&quot; /console"
              Condition="Exists('$(ProgramFiles)\Microsoft FxCop 1.36\FxCopCmd.exe')" />
        <!-- When running on a 64 bit machine the $(ProgramFiles) variable will be set to c:\program files (x86) when running a 32 bit process which luckily for us Visual Studio and FxCop are.-->
      </Target>
    </Project>
  3. Open the Visual Studio
  4. File --> New Project (ctrl+shift+n)
  5. Select Other Project Types --> Visual Studio Solutions --> Blank Solution
  6. Name the solution FxCopIntegrated and press OK
  7. In Solution Explorer (ctrl +alt + L) right click the solution and select add new project.
  8. Select Visual C# --> Windows --> Class Library
  9. Name the library CodeBandit and press OK.
  10. In Solution Explorer right click the CodeBandit project and click unload project.
  11. If Visual Studio prompts to save files click "yes"
  12. In Solution Explorer right click the CodeBandit project and click "Edit CodeBandit.csproj"
  13. Navigate to the line after this (ctrl+g 52 on my machine):
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  14.  Add this line to reference the file created in step 2:
    <Import Project="$(MSBuildExtensionsPath)\v3.5\FxCop.targets"/>
  15. In Solution Explorer right click the CodeBandit project and click reload project.
  16. If Visual Studio asks to close CodeBandit.csproj click "yes" and "yes" to save changes if prompted.
  17. Build the solution.  On my system I received these warnings:
    CA2210 : Microsoft.Design : Sign 'CodeBandit.dll' with a strong name key.
    CA1014 : Microsoft.Design : Mark 'CodeBandit.dll' with CLSCompliant(true) because it exposes externally visible types.
Option #2 Integrating FxCop across all solutions.
  1. Follow the steps from Option #1 to ensure everything is setup and working.
  2. Create a file: C:\Program Files (x86)\MSBuild\v3.5\Custom.After.Microsoft.Common.targets with the following text:
    <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="FxCop.Targets"/>
    </Project>
  3. Thats it, all the work was performed in Option #1 :)
  4. To verify this add a new VB project to the FxCopIntegrated solution.
  5. Building the newly added project I get these warnings:
    CA1020 : Microsoft.Design : Consider merging the types defined in 'ClassLibrary2' with another namespace.
    CA2210 : Microsoft.Design : Sign 'ClassLibrary2.dll' with a strong name key.
    CA1014 : Microsoft.Design : Mark 'ClassLibrary2.dll' with CLSCompliant(true) because it exposes externally visible types.
    CA1824 : Microsoft.Performance : Because assembly 'ClassLibrary2.dll' contains a ResX-based resource file, mark it with the NeutralResourcesLanguage attribute, specifying the language of the resources within the assembly. This could improve lookup performance the first time a resource is retrieved.
Thanks to John Robbins for pointing me to an old post by the Code Analysis team showing how to call FxCop in a post build event and having it run over the project without maintaining a separate FxCop project file.

Monday, February 8, 2010

Redirecting Assembly Bindings in .NET 3.5

I spent some time last week digging into assembly binding redirection.  This blog post provides working examples of assembly redirection through config files and publisher policies.

This example will use the following command line utilities and versions:
  • csc - C# compiler (Version 3.5.30729.4926)
  • sn - .Strong Name Utility (Version 3.5.30729.1)
  • gacutil - Global Assembly Cache Tool (Version 3.5.30729.1)
  • al - assembly linker (Version 8.00.50727.42)
Helpful utilities not covered in this article:
To get started lets create a program calling a method in another assembly which outputs the callee's assembly version.  These steps also create assemblies for use in the later examples.
  1. Open a Visual Studio Command Prompt and navigate to a working directory where some files can be created.
  2. Create and save a file named demo.cs containing the following text:
    using System;
    using System.Reflection;

    [assembly: AssemblyVersionAttribute("2.0.0.0")]

    public static class Demo
    {
        public static void Main()
        {
            Console.WriteLine("Version " + Assembly.GetExecutingAssembly().GetName().Version.ToString());
        }
    }
  3. from a command prompt type "sn -k key.snk"
  4. type "csc /target:library /keyfile:key.snk demo.cs"
  5. type "echo f | xcopy demo.dll .\bin\v2\demo.dll"
  6. Change the AssemblyVersionAttribute in demo.cs replacing "2.0" with "3.0"
  7. csc /target:library /keyfile:key.snk demo.cs
  8. echo f | xcopy demo.dll .\bin\v3\demo.dll
  9. Change the AssemblyVersionAttribute in demo.cs replacing "3.0" with "4.0"
  10. csc /target:library /keyfile:key.snk demo.cs
  11. echo f | xcopy demo.dll .\bin\v4\demo.dll
  12. Change the AssemblyVersionAttribute in demo.cs replacing "4.0" with "1.0"
  13. csc /target:libarary /keyfile:key.snk demo.cs
    1. echo f | xcopy demo.dll .\bin\v1\demo.dll
  14. Create and save a file named demo.cs containing the following text:
    public static class Program
    {
        public static void Main()
        {
            Demo.Main();
        }
    }
  15. csc /reference:demo.dll program.cs
  16. program.exe
  17. The output should read: Version 1.0.0.0
Redirecting 1.0 to 2.0 version in the GAC.
  1. gacutil /i .\bin\v2\demo.dll
  2. sn -T demo.dll
  3. Note the Public key token as we'll need that in the assemblyIdentity publicKeyToken attribute in the next step.
  4. Create program.exe.config with the following text:
    <?xml version="1.0"?>
    <
    configuration>
     <
    runtime>
      <
    assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <
    dependentAssembly>
        <
    assemblyIdentity name="demo" publicKeyToken="keyFromSn-T" />
        <
    bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
       </
    dependentAssembly>
      </
    assemblyBinding>
     </
    runtime>
    </
    configuration>
  5. program.exe
  6. The output should read:  Version 2.0.0.0
Redirecting 1.0 to Version 3.0 using a URL:
  1. Replace the program.exe.config text with the following:
    <?xml version="1.0"?>
    <configuration>
     <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
         <assemblyIdentity name="demo" publicKeyToken="keyFromSn-T" />
         <bindingRedirect oldVersion="1.0.0.0newVersion="3.0.0.0"/>
            <!--to use an absolute filepath use href="file://D:/Projects/...." or it can point to a web resource: href="http://www.fake.com/demo.dll"-->
         <codeBase version="3.0.0.0" href="./bin/v3/demo.dll" />
        </dependentAssembly>
      </assemblyBinding>
     </runtime>
    </configuration>
  2. program.exe
  3. The output should read: Version 3.0.0.0
Redirecting 1.0 to Version 4.0 in the GAC using a publisher policy.
  1. Create a policy.config file in the working directory with this text:
    <?xml version="1.0"?>
    <configuration>
     <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
        <assemblyIdentity name="demo" publicKeyToken="keyFromSn-T" />
        <bindingRedirect oldVersion="1.0.0.0" newVersion="4.0.0.0" />
       </dependentAssembly>
      </assemblyBinding>
     </runtime>
    </configuration>
  2. al /link:policy.config /out:policy.1.0.demo.dll /keyfile:key.snk
  3. gacutil /i policy.1.0.demo.dll
  4. gacutil /i .\bin\v4\demo.dll
  5. delete the program.exe.config in the working directory or erase the bindingredirect element.  The bindingredirects seem to be applied first and will break this example.
  6. program.exe
  7. The output should read: Version 4.0.0.0

Monday, February 1, 2010

A new life with Microsoft Dynamics CRM...

Friday January 15th was my last day at Urban Science where I learned a lot about software development and made a lot of great friends.

On Monday January 18th I started a new job at Sonoma Partners.  We specialize in customizing and implementing Microsoft Customer Relationship Management software for our customers.

If you are a technologist, programmer, personal contact, or professional contact in the Chicago area give me a call and lets get together.

If you are a potential Microsoft CRM customer, give me a call and lets make a deal ;)

Monday, January 25, 2010

NDepend

Patrick Smacchia kindly gave me the opportunity to use and evaluate his NDepend product.  NDepend is a static analysis tool meaning it can provides insight and guidance about one's work by inspecting assemblies and source code.  It identifies things like classes which may be better as structures by looking at the size of an instance of the class, the number of children, and the depth of inheritance.  It also graphically displays assembly dependencies and can do comparisons of an application between releases.  All in all a very cool tool!


First I started up NDepend to run it over a WPF desktop application I worked on in 2008.  I started by creating a new project.  It was very slick the new project open a .sln file to determine the assemblies to analyze.  NDepend understands there are application assemblies (assemblies compiled from the user's code) and tier assemblies (generally compiled references).  This picture shows what I mean.  After taking just 15 seconds to analyze 353,899 IL instructions NDepend showed an interactive view displaying many different views of the codebase at once which were all linked together, so if I drilled into something in one view or hovered over it there would be an effect in the other windows.  Being a noob this was a bit much for me and I opted to view a static HTML report.  The report didn't look so great in Chrome or FireFox but looked fine in IE8.  I read through the 50 page report awash in information.  I wish I had used the tool in 2008 to focus some of our refactoring efforts.  There are a lot of fantastic queries out of the box showing things that are blatantly wrong with the code we wrote.  Speaking of queries, NDepend has a very cool Code Query Language (CQL) one can use to write queries which throw errors or warnings during a build process.  NDepend comes with many queries out of the box which you can run or use as examples.  


Here is a query provided in the NDepend install showing types that could potentially be marked sealed:
// <Name>Class with no descendant should be sealed if possible</Name>
WARN IF Count > 0 IN SELECT TOP 10 TYPES WHERE
  
IsClass AND
  
NbChildren ==0 AND
  
!IsSealed AND
  
!IsStatic


Here is another query provided in the NDepend install showing classes that might be better as structures:

// <Name>Classes that are candidate to be turned into Structures</Name>
WARN IF Count > 0 IN SELECT TOP 10 TYPES WHERE 
  IsClass AND
  SizeOfInst <= 16 AND    // Structure instance must not be too big, else it degrades performance.
  NbChildren == 0 AND     // Must not have children
  DepthOfInheritance == 1 // Must derive directly from System.Object
  // AND IsSealed    : You might want to add this condition to restreint the set.
  // AND IsImmutable : Structures should be immutable type.
  // AND !IsPublic   : You might want to add this condition if you are developping a framework
  //                   with classes that are intended to be sub-classed by your clients.


These are two examples of things I would like to know but wouldn't like to take the effort to figure out.  NDepend makes time consuming code analysis tasks simple and shareable by providing the Code Query Language.

A couple months later I decided to use NDepend to analyze the ASP.NET solution I worked on in 2009.  I precompiled the application as recommended in the NDepend FAQ and browsed to that output instead of using the ASP.NET solution file to begin my analysis.  Unfortunately this project's code quality wasn't as high as the other project and there were 491,298 IL instructions for NDepend to analyze, although it only took 13 seconds. :)  Again I mostly skipped the UI and dove into the 492 page report.  Being a web app, some of the assemblies have funky names like App_Web_c55buezi which makes some items in the report harder to consume than the human friendly names from the desktop project.  Unfortunately I will not be working on this codebase anymore and did not have significant ownership of it so I didn't diligently read up all the goodness NDepend was giving me access to.

My lesson learned is that I need to run this fabulous tool over code I'm actively working on so I can take advantage of it and learn like I've learned from FxCop.  This tool is great because it has something for everyone.  From rules with specific recommendations on what to fix to strategic insights into your dependency structure.