Playing with NDepend

Few days ago I used NDepend tool to analyze the code of one of our bigger projects in Sogeti. Here are my thoughts on the tool.

NDepend is a static dependency analyzer tool. It scans your compiled code, the PDBs and the source code and produces a “map” of your code dependencies, metrics and structures. It has been around for several years now, and it has kept beeing better in every new edition.

The default view of NDepend is the so-called “Report” that highlights all the potential issues with your code. It gives you a trove of information to optimize your code, in a HTML format that’s easy to share. The report contains the summary of all the main features of the tool: rule violations, dependency cycles, complex code and so on.

NDepend Report

The next feature that I used is the dependency visualization. There are two “flavours” to it: the dependency graph and the dependency matrix.

The dependency graph is easy to grasp but it can only be meaningfully used with a handful of classes and namespaces. It quickly becomes a mess with more than a couple of dozen objects.

NDepend Dependency Graph

The dependency matrix is a much more powerful tool. It can be used to spot dependency cycles, infrastructure classes, “god” classes, cohesion and coupling and so on. However, it’s not as intuitive to analyze as the graph, it takes some time playing with it to grasp the information without consulting the help windows.

NDepend Dependency Matrix

NDepend has a full set of code complexity and maintainibility rules. It can quickly highlight the most important issues so that you can concentrate on fixing the most critical parts of your code.

NDepend Code Rules

But it’s just the surface of it. NDepend has a full-blown language called CQLinq, akin to SQL, that allows you to query your code. You can find classes that have high coupling, methods that are too complex, nested structures in the code and a myriad of other code patterns.

NDepend CQLinq

In my opinion, NDepend is an invaluable tool if you’re concerned about your code quality (and you should be). It takes some time to grasp and master all the power of it, as it can be an overwhelming experience for a first-time user, but it’s a time well invested.

Thanks to Patrick Smacchia (the main force behind NDepend) for the NDepend license for MVPs, that allowed me to fully evaluate the tool.

 

Creative Uses of IDisposable

We all know IDisposable interface in NET Framework. It is used to signal to any object with dependencies to other objects when should it dispose of them. IDisposable is a very handy feature together with the using statement, when IDisposable is called automatically when we exit a using code block.

I was reading the Introduction to Rx by Lee Campbell, in preparation of my session for DotNetSpain conference in February. There, I’ve found some creative uses of IDisposable.

Basically, it leverages the fact that Dispose method is called after “using” statement code block exits. So, we can use it to do some wrapping around that code block.

Before the code in the block is executed, the IDisposable constructor will be invoked. After the code is executed, the Dispose method will be invoked. In effect, our code will be surrounded by “Pre” and “Post” actions in an elegant way.

In Campbell’s exemple, a console app can leverage IDisposable to print text in different color inside a code block.

The “wrapper” is a short class named ConsoleColor that implements IDisposable. It remembers the previous console foreground color by saving it in the constructor and restoring it in the Dispose method.

The calling code just decorates Console.WriteLine code blocks with custom ConsoleColor instances, and voilà, we have a multi-color console application.

2016-01-30_13-31-52

How to Set Printer Default Paper Bin in .NET

Yesterday I was challenged with a task that seemed trivial but it was a real pain to solve.

The customer had a VB.NET application that spawns Adobe Reader via command line options to force a specific PDF file to be printed on a specified printer (using the “semi-clandestine” /t switch). The problem that they were facing was that the VB.NET Compatibility Power Pack’s Printer object (used to simulate the equivalent object in VB6) didn’t change the default paper bin (or tray) for the printer. The change just sticks to the application’s session lifetime.

I turned to Win32 API for the solution. Indeed, there is a winspool.drv library that has the necessary functions to change several printer parameters. I used many blogs and forum posts for a source, which I list here:

Finally, the solution involved coaxing the Win32 API structures into the .NET world using marshalling (using System.Marshal namespace and its methods). I had no success until I set the CharSet attribute to Auto encoding for the API structures. It seems that by default it uses Ansi encoding and the data is messed up.

The final code for the tray change routine is here (it’s in VB.NET but easily translated into C#). It has four main methods:

  • SetTray (sets the default printer tray to a tray number. In VB.NET it’s the Printer.PaperBin or RawKind property)
  • GetPrinterName (gets the default printer name)
  • SavePrinterSettings (copies the printer settings into memory)
  • RestorePrinterSettings (replaces the printer settings with previously saved ones)
Code Snippet
  1. Imports System.Drawing.Printing
  2. Imports System.Runtime.InteropServices
  3.  
  4.  
  5. Public Class PrinterNative
  6.  
  7.  
  8.     Private Declare Auto Function DocumentProperties Lib "winspool.drv" _
  9.      (ByVal hWnd As IntPtr, ByVal hPrinter As IntPtr, ByVal pDeviceName As String, _
  10.       ByVal pDevModeOutput As IntPtr, ByVal pDevModeInput As IntPtr, ByVal fMode As Int32) As Integer
  11.  
  12.     Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterW" _
  13.      (ByVal hPrinter As IntPtr, ByVal Level As Integer, ByVal pPrinter As IntPtr, _
  14.       ByVal cbBuf As Integer, ByRef pcbNeeded As Integer) As Integer
  15.  
  16.     Private Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" _
  17.      (ByVal hPrinter As IntPtr, ByVal level As Integer, ByVal pPrinterInfoIn As IntPtr, _
  18.       ByVal command As Int32) As Boolean
  19.  
  20.     <DllImport("winspool.drv", EntryPoint:="OpenPrinterA", ExactSpelling:=True, _
  21.        SetLastError:=True, CallingConvention:=CallingConvention.StdCall, _
  22.        CharSet:=CharSet.Ansi)> _
  23.     Private Shared Function OpenPrinter(ByVal pPrinterName As String, _
  24.   ByRef hPrinter As IntPtr, ByRef pDefault As PRINTER_DEFAULTS) As Boolean
  25.     End Function
  26.  
  27.     <DllImport("winspool.drv", EntryPoint:="ClosePrinter", SetLastError:=True, ExactSpelling:=True, _
  28.      CallingConvention:=CallingConvention.StdCall)> _
  29.     Private Shared Function ClosePrinter(ByVal hPrinter As Int32) As Boolean
  30.     End Function
  31.  
  32.     Declare Function GetDefaultPrinter Lib "winspool.drv" Alias "GetDefaultPrinterA" _
  33.      (ByVal pszBuffer As System.Text.StringBuilder, ByRef pcchBuffer As Int32) As Boolean
  34.  
  35.     Declare Function SetDefaultPrinter Lib "winspool.drv" Alias "SetDefaultPrinterA" _
  36.      (ByVal pszPrinter As String) As Boolean
  37.  
  38.     Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
  39.      (ByVal hpvDest As IntPtr, ByVal hpvSource As IntPtr, ByVal cbCopy As Long)
  40.  
  41.  
  42.     Private Structure PRINTER_DEFAULTS
  43.         Dim pDatatype As String
  44.         Dim pDevMode As Long
  45.         Dim pDesiredAccess As Long
  46.     End Structure
  47.  
  48.     Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
  49.     Private Const PRINTER_ACCESS_ADMINISTER = &H4
  50.     Private Const PRINTER_ACCESS_USE = &H8
  51.     Private Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
  52.  
  53.     Private Const DM_IN_BUFFER As Integer = 8
  54.     Private Const DM_IN_PROMPT As Integer = 4
  55.     Private Const DM_OUT_BUFFER As Integer = 2
  56.  
  57.     Private Structure PRINTER_INFO_9
  58.         Dim pDevMode As IntPtr
  59.     End Structure
  60.  
  61.  
  62.  
  63.     <StructLayout(LayoutKind.Sequential)> _
  64.     Private Structure PRINTER_INFO_2
  65.         <MarshalAs(UnmanagedType.LPTStr)> Public pServerName As String
  66.         <MarshalAs(UnmanagedType.LPTStr)> Public pPrinterName As String
  67.         <MarshalAs(UnmanagedType.LPTStr)> Public pShareName As String
  68.         <MarshalAs(UnmanagedType.LPTStr)> Public pPortName As String
  69.         <MarshalAs(UnmanagedType.LPTStr)> Public pDriverName As String
  70.         <MarshalAs(UnmanagedType.LPTStr)> Public pComment As String
  71.         <MarshalAs(UnmanagedType.LPTStr)> Public pLocation As String
  72.  
  73.         Public pDevMode As IntPtr
  74.  
  75.         <MarshalAs(UnmanagedType.LPTStr)> Public pSepFile As String
  76.         <MarshalAs(UnmanagedType.LPTStr)> Public pPrintProcessor As String
  77.         <MarshalAs(UnmanagedType.LPTStr)> Public pDatatype As String
  78.         <MarshalAs(UnmanagedType.LPTStr)> Public pParameters As String
  79.  
  80.         Public pSecurityDescriptor As IntPtr
  81.         Public Attributes As Integer
  82.         Public Priority As Integer
  83.         Public DefaultPriority As Integer
  84.         Public StartTime As Integer
  85.         Public UntilTime As Integer
  86.         Public Status As Integer
  87.         Public cJobs As Integer
  88.         Public AveragePPM As Integer
  89.     End Structure
  90.  
  91.  
  92.     <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
  93.     Public Structure DEVMODE
  94.         <MarshalAs(UnmanagedType.ByValTStr, Sizeconst:=32)> Public pDeviceName As String
  95.         Public dmSpecVersion As Short
  96.         Public dmDriverVersion As Short
  97.         Public dmSize As Short
  98.         Public dmDriverExtra As Short
  99.         Public dmFields As Integer
  100.         Public dmOrientation As Short
  101.         Public dmPaperSize As Short
  102.         Public dmPaperLength As Short
  103.         Public dmPaperWidth As Short
  104.         Public dmScale As Short
  105.         Public dmCopies As Short
  106.         Public dmDefaultSource As Short
  107.         Public dmPrintQuality As Short
  108.         Public dmColor As Short
  109.         Public dmDuplex As Short
  110.         Public dmYResolution As Short
  111.         Public dmTTOption As Short
  112.         Public dmCollate As Short
  113.         <MarshalAs(UnmanagedType.ByValTStr, Sizeconst:=32)> Public dmFormName As String
  114.         Public dmUnusedPadding As Short
  115.         Public dmBitsPerPel As Integer
  116.         Public dmPelsWidth As Integer
  117.         Public dmPelsHeight As Integer
  118.         Public dmNup As Integer
  119.         Public dmDisplayFrequency As Integer
  120.         Public dmICMMethod As Integer
  121.         Public dmICMIntent As Integer
  122.         Public dmMediaType As Integer
  123.         Public dmDitherType As Integer
  124.         Public dmReserved1 As Integer
  125.         Public dmReserved2 As Integer
  126.         Public dmPanningWidth As Integer
  127.         Public dmPanningHeight As Integer
  128.  
  129.     End Structure
  130.  
  131.  
  132.  
  133.     Private pOriginalDEVMODE As IntPtr
  134.  
  135.  
  136.     Public Sub SavePrinterSettings(ByVal printerName As String)
  137.         Dim Needed As Integer
  138.         Dim hPrinter As IntPtr
  139.         If printerName = "" Then Exit Sub
  140.  
  141.         Try
  142.             If OpenPrinter(printerName, hPrinter, Nothing) = False Then Exit Sub
  143.             'Save original printer settings data (DEVMODE structure)
  144.             Needed = DocumentProperties(Form1.Handle, hPrinter, printerName, Nothing, Nothing, 0)
  145.             Dim pFullDevMode As IntPtr = Marshal.AllocHGlobal(Needed) 'buffer for DEVMODE structure
  146.             DocumentProperties(Form1.Handle, hPrinter, printerName, pFullDevMode, Nothing, DM_OUT_BUFFER)
  147.             pOriginalDEVMODE = Marshal.AllocHGlobal(Needed)
  148.             CopyMemory(pOriginalDEVMODE, pFullDevMode, Needed)
  149.         Catch ex As Exception
  150.             MsgBox(ex.Message)
  151.         End Try
  152.     End Sub
  153.  
  154.     Public Sub RestorePrinterSettings(ByVal printerName As String)
  155.         Dim hPrinter As IntPtr
  156.         If printerName = "" Then Exit Sub
  157.  
  158.         Try
  159.             If OpenPrinter(printerName, hPrinter, Nothing) = False Then Exit Sub
  160.             Dim PI9 As New PRINTER_INFO_9
  161.             PI9.pDevMode = pOriginalDEVMODE
  162.             Dim pPI9 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(PI9))
  163.             Marshal.StructureToPtr(PI9, pPI9, True)
  164.             SetPrinter(hPrinter, 9, pPI9, 0&)
  165.             Marshal.FreeHGlobal(pPI9) 'pOriginalDEVMODE will be free too
  166.             ClosePrinter(hPrinter)
  167.  
  168.  
  169.         Catch ex As Exception
  170.             MsgBox(ex.Message)
  171.         End Try
  172.  
  173.  
  174.     End Sub
  175.  
  176.     Function GetPrinterName() As String
  177.         Dim buffer As New System.Text.StringBuilder(256)
  178.         Dim PrinterName As String = String.Empty
  179.         'Get default printer's name
  180.         GetDefaultPrinter(buffer, 256)
  181.         PrinterName = buffer.ToString
  182.         If PrinterName = "" Then
  183.             MsgBox("Can't find default printer.")
  184.         End If
  185.         Return PrinterName
  186.     End Function
  187.  
  188.     Sub SetTray(ByVal printerName As String, ByVal trayNumber As Integer)
  189.         Dim hPrinter As IntPtr
  190.         Dim Needed As Integer
  191.  
  192.         OpenPrinter(printerName, hPrinter, Nothing)
  193.  
  194.         'Get original printer settings data (DEVMODE structure)
  195.         Needed = DocumentProperties(IntPtr.Zero, hPrinter, printerName, Nothing, Nothing, 0)
  196.         Dim pFullDevMode As IntPtr = Marshal.AllocHGlobal(Needed) 'buffer for DEVMODE structure
  197.         DocumentProperties(IntPtr.Zero, hPrinter, printerName, pFullDevMode, Nothing, DM_OUT_BUFFER)
  198.  
  199.         Dim pDevMode9 As DEVMODE = Marshal.PtrToStructure(pFullDevMode, GetType(DEVMODE))
  200.  
  201.         ' Tray change
  202.         pDevMode9.dmDefaultSource = trayNumber
  203.  
  204.         Marshal.StructureToPtr(pDevMode9, pFullDevMode, True)
  205.  
  206.         Dim PI9 As New PRINTER_INFO_9
  207.         PI9.pDevMode = pFullDevMode
  208.  
  209.         Dim pPI9 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(PI9))
  210.         Marshal.StructureToPtr(PI9, pPI9, True)
  211.         SetPrinter(hPrinter, 9, pPI9, 0&)
  212.         Marshal.FreeHGlobal(pPI9) 'pFullDevMode will be free too
  213.  
  214.         ClosePrinter(hPrinter)
  215.     End Sub
  216.  
  217. End Class

I hope it can be useful to somebody and spare him or her a day or two looking for an answer.

Como diseñar clases para ser heredadas (con Code Contracts)

Mi ex-compañero Eduard Tomàs ha escrito un artículo en su blog que me ha gustado mucho por la magnífica exposición que hace de un tema tan “seco” como puede ser el principio de sustitución de Liskov.

Desde aquí le envío ánimos para publicar más artículos como este.

El post original lo podéis leer en esta dirección: http://geeks.ms/blogs/etomas/archive/2010/04/12/dise-241-ar-clases-para-ser-heredadas.aspx

Windows Defender Update and the Page Error in Development Web Server

Another of the strange, unexplained errors with a simple (yet unexpected) cause.

SYMPTOMS

You are developing an ASP.NET Site or Web Project in Visual Studio 2005/2008. You start the Debug process. Your browser window opens with a message:

Page Load Error

or

The page cannot be found.

Additionally, you have Windows Defender installed on your machine.

THE CAUSE

A flawed update of the Windows Defender removes the localhost entries from hosts file in windowssystem32devicesetc folder.

SOLUTION

Manually add the following entries back to the hosts file:

127.0.0.1 localhost

More details: http://www.h-online.com/security/Windows-Defender-False-alarm-triggered-by-hosts-file–/news/112814

.NET Framework 4.0 and Visual Studio 2010 Training Kit

I’m still digesting the changes in .NET 3.5 and SP1, and the guys from Redmond already make a training kit for the next version of the framework. It’s impossible to follow this pace of changes 🙁

The Visual Studio 2010 and .NET Framework 4.0 Training Kit includes presentations, hands-on labs, and demos. This content is designed to help you learn how to utilize the Visual Studio 2010 features and a variety of framework technologies including: C# 4.0, Visual Basic 10, F#, Parallel Computing Platform, WCF, WF, WPF, ASP.NET AJAX 4.0, ASP.NET MVC Dynamic Data.

Get it from http://www.microsoft.com/downloads/details.aspx?FamilyID=752cb725-969b-4732-a383-ed5740f02e93&displaylang=en

.NET Framework 3.5 SP1 and Training Update

As you might know, Microsoft has released a service pack for .NET Framework 3.5 few days ago. Let’s see what it adds to the standard framework:

  • ASP.NET Dynamic Data mechanism for quick scaffolding of the database operations
  • ADO.NET Data Service framework, with REST-based service to expose data for its consumption by web services and WCF clients
  • Entity Framework for ADO.NET, an middle-layer ORM and query mechanism that provides an abstract view of the data, detached from its physical implementation
  • Improvements to the CLR engine (mainly for permissions and performance) and other components of the framework

You can download the SP1 from here.

Also, the .NET 3.5 training kit I was referring to before has been updated to the SP1 version. You can grab it here.

DataSource, ValueMember and DisplayMember Order Issues

For two times in a row this week I was asked to do a buddy check of .NET code that performs databinding. I found out that the order of the binding operations is very important:

  • Set the DisplayMember and/or ValueMember first
  • Set the DataSource property after that

You will avoid many exceptions this way 😉

RESOURCES
Scott McMaster at CodeProject has more information regarding this weird databinding Windows Forms issue.

.NET 3.5 Enhancements Training Kit is Available

Wow, that’s fast! I’m still digesting the NET Framework 3.0 training (mainly over WPF now) and the folks from Redmond deliver their first version of .NET 3.5 Enhancements Training Kit for download.

It contains six hands-on labs, as can be seen in the picture below. They cover the new ADO.NET enhancements (Entity Framework and Data Services), ASP.NET new MVC architecture, ASP.NET Silverlight and AJAX controls.

image

Download the kit at http://www.microsoft.com/downloads/details.aspx?familyid=8BDAA836-0BBA-4393-94DB-6C3C4A0C98A1&displaylang=en and start learning. As Jonathan Carter (MS Visual Studio evangelist) states in his blog, by the time these enhancements are RTM, you will be a master!