WPF Application Shutdown on Windows 7 x64

3 02 2012

In WPF applications, the Application.Exit event is not (reliably) called – at least on Windows 7 x64 – when the user logs off from or shuts down or restarts the Windows machine. I found that my Application_Exit handler which performs important cleanup was not called.

I tried to remedy this first by hooking into the SystemEvents.SessionEnding event and calling Application.Shutdown from there. After doing this, I found that my Application_Exit handler was called, but then the process seemed to be killed in the middle of the shutdown processing (log output stopped suddenly).

Then I removed the SystemEvents.SessionEnding event handler again and tried to override the Application.OnSessionEnding virtual method. This resulted in the same behavior. The Application_Exit handler was sometimes called, sometimes not, but when it was called, the process died during shutdown processing.

I finally solved this by overwriting the Application.OnSessionEnding method, calling the base class implementation first, then setting SessionEndingCancelEventArgs.Cancel=true to avoid my process being killed by the OS, and finally explicitly calling Application.Shutdown to initiate the shutdown process. The end result is a clean application shutdown while Windows shows the „Application not responding“ screen for several seconds while my application shuts down.

Here’s the code:

public partial class App : Application
{
  protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
  {
    base.OnSessionEnding(e);

    // Cancel application shutdown to prevent Windows 7 killing
    // the process
    e.Cancel = true;

    // Terminate myself
    Shutdown();
  }
}
Advertisements




Get row count from all tables

29 01 2012

The following T-SQL script generates a view of all table names and the number of rows contained in each table for the current SQL database.

SELECT o.name 'TableName', p.rows 'Rows'
FROM sys.objects o
JOIN sys.partitions p ON o.object_id = p.object_id
WHERE (o.type = 'U')
AND (p.index_id IN (0,1))
ORDER BY p.rows DESC

I found this information here.





VS2010 Build and Debug Startup Project

4 11 2011

In large solutions, I often have the need to build and/or debug only the selected startup project.

The main benefit of this is that Visual Studio keeps from building the entire solution when you select Debug – Start new instance from a project’s context menu in the Solution Explorer view. This is different from the behavior when you select Debug – Start Debugging from the main menu. In the latter case, the whole solution is always built which might cause problems when one or more of the generated output artefacts are in use by the system (e. g. running executables).

Today I wrote the following two Visual Studio macros and assigned them to the hotkey Ctrl+B (BuildStartupProject) and Ctrl+D (DebugStartupProject) and it made me happier.

Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE90a
Imports EnvDTE100
Imports System.Diagnostics

Public Module StartupProjectCommands
Sub BuildStartupProject()
  ' Build startup project
  Dim sb As SolutionBuild = DTE.Solution.SolutionBuild
  Dim projName As String = sb.StartupProjects(0)
  DTE.ExecuteCommand("View.Output")
  sb.BuildProject(sb.ActiveConfiguration.Name, projName, False)
End Sub

Sub DebugStartupProject()
  ' Get startup project name
  Dim sb As SolutionBuild = DTE.Solution.SolutionBuild
  Dim projName As String = sb.StartupProjects(0)
  Dim index As Integer = projName.LastIndexOf(".")
  If (index > 0) Then
    projName = projName.Substring(0, index)
    ' Activate SolutionExplorer
    DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate()
    ' Select startup project
    DTE.ActiveWindow.Object.GetItem(projName).Select(vsUISelectionType.vsUISelectionTypeSelect)
    ' Debug startup project
    DTE.ExecuteCommand("ClassViewContextMenus.ClassViewProject.Debug.Startnewinstance")
  End If
End Sub

End Module




Mouse Without Borders

13 09 2011

Nice little tool which allows you to control multiple (up to four) computers with one keyboard and mouse, including Copy&Paste support between these machines.

http://blogs.technet.com/b/next/archive/2011/09/09/microsoft-garage-download-mouse-without-borders.aspx

This is a very cool alternative to using Remote Desktop when you want to use the physical screens of the remote controlled machines.

Hint: If you have problems connecting the machines, make sure their hostnames can be resolved from each other. Seems to use the hostname for communication even when you specify an IP address during connect.





WinHTTP Proxy Settings

11 08 2011

To enable certain network services like the „Windows Update“ service, BITS or the ServerXMLHttpRequest object on hosts behind a HTTP proxy, you need to configure the WinHTTP proxy settings. On Windows XP this was done using the proxycfg.exe utility. On Windows 7 this has been replaced with netsh commands.

Windows XP

Set Proxy Settings

Syntax:

proxycfg –p {proxy address:port} {bypass list}

Example:

proxycfg –p "172.29.240.193:4480" "<local>;172.29.*"

Delete Proxy Settings

proxycfg –d

Verify Proxy Settings

Check the following registry key

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\WinHttpSettings

Windows 7

Set Proxy Settings

Syntax:

netsh winhttp set proxy {proxy address:port} {bypass list}

Example:

netsh winhttp set proxy "172.29.240.193:4480" "<local>;172.29.*"

Delete Proxy Settings

netsh winhttp reset proxy

Verify Proxy Settings

netsh winhttp show proxy

This information has been copied from:

http://www.bohack.com/2010/08/windows-server-2008-replacement-of-proxycfg-exe/





Windows 7 Remote Desktop Logon with empty password

11 08 2011

By default, Windows will not allow the logon over a network with a blank password.

You can disable blank password restrictions by using a policy. To locate and change this policy:

Click Start, point to Run, type gpedit.msc, and then click OK to start the Group Policy Editor.

Open Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options\Accounts: Limit local account use of blank passwords to console logon only.

Double-click Limit local account use of blank passwords to consol logon only.

Click Disabled, and then click OK.

Quit Group Policy Editor.
NOTE: By default, this policy is on (enabled).

Copied from:

http://superuser.com/questions/106917/remote-desktop-without-a-password





Programmatically retrieving MAC Address of IP-less Network Adapter

16 03 2011

There are many developers asking how to programmatically retrieve the MAC address of a network adapter on Windows. There are also many answer showing various solutions ranging from parsing ipconfig /ALL output, through using the .NET System.Net.NetworkInformation.NetworkInterface class or using the GetIfTable API.

These solutions work fine as long as the IP protocol is bound to the network adapter in question. But they cannot retrieve the MAC address of a network adapter which is not bound to the IP protocol stack.

One solution to this problem is to use WMI and query the Win32_NetworkAdapter or (better) Win32_NetworkAdapterConfiguration objects. These do also work with IP-less network adapters. One problem with WMI is, that it’s slow. Another problem is, that the Win32_NetworkAdapter objects cannot be retrieved by a service during system startup – not even when there is a service dependency on the winmgmt service. There seems to be some deadlock situation which causes the WMI querying service to hang for approx. 2 minutes. With the Win32_NetworkAdapterConfiguration class this problem does not occur though.

Just for reference, this is how to query the WMI Win32_NetworkAdapterConfiguration objects from the command line:

wmic path Win32_NetworkAdapterConfiguration get * /format:list

So how does WMI retrieve the MAC address?

After digging around with the amazing API Monitor tool, I learned that WMI is using a DeviceIoControl API call to retrieve the MAC address. This is the required call in C:

#include <WinIoCtl.h>
#include <NtDDNdis.h>

unsigned int code = OID_802_3_CURRENT_ADDRESS;
char macAddress[6];
unsigned int bytesReturned;

BOOL res = DeviceIoControl(
  hDevice,
  IOCTL_NDIS_QUERY_GLOBAL_STATS,
  &code,
  sizeof(code),
  macAddress,
  sizeof(macAddress),
  &bytesReturned,
  NULL
  );

Doing the same thing from C# requires some P/Invoke calls. There is an article How to query miniport driver information (802.11 OIDs) using the DeviceIOControl() function on CodeProject that helped to figure out how to get the DeviceIoControl call straight.

using System;
using System.ComponentModel;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using log4net;
using Microsoft.Win32.SafeHandles;

namespace NetworkUtils
{
  /// 
  /// Helper class which returns network adapter information such
  /// as the MAC address.
  /// 
  public static class NetworkAdapterInfo
  {
    private static readonly ILog _log = LogManager.GetLogger(typeof(NetworkAdapterInfo));

    /// 
    /// Contains enum types and native Win32 functions.
    /// 
    private static class SafeNativeMethods
    {
      [Flags]
      public enum FileAccess : uint
      {
        /// 
        /// Read access
        /// 
        GenericRead = 0x80000000,

        ///// 
        ///// Write access
        ///// 
        //GenericWrite = 0x40000000,
        
        ///// 
        ///// Execute access
        ///// 
        //GenericExecute = 0x20000000,

        ///// 
        ///// Read, write, and execute access
        ///// 
        //GenericAll = 0x10000000
      }

      [Flags]
      public enum ShareMode : uint
      {
        ///// 
        ///// Prevents other processes from opening a file or device if they request delete, read, or write access.
        ///// 
        //None = 0x00000000,

        /// 
        /// Enables subsequent open operations on an object to request read access. 
        /// Otherwise, other processes cannot open the object if they request read access. 
        /// If this flag is not specified, but the object has been opened for read access, the function fails.
        /// 
        Read = 0x00000001,

        /// 
        /// Enables subsequent open operations on an object to request write access. 
        /// Otherwise, other processes cannot open the object if they request write access. 
        /// If this flag is not specified, but the object has been opened for write access, the function fails.
        /// 
        Write = 0x00000002,

        ///// 
        ///// Enables subsequent open operations on an object to request delete access. 
        ///// Otherwise, other processes cannot open the object if they request delete access.
        ///// If this flag is not specified, but the object has been opened for delete access, the function fails.
        ///// 
        //Delete = 0x00000004
      }

      public enum CreationDisposition : uint
      {
        ///// 
        ///// Creates a new file. The function fails if a specified file exists.
        ///// 
        //CreateNew = 1,

        ///// 
        ///// Creates a new file, always. 
        ///// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes, 
        ///// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
        ///// 
        //CreateAlways = 2,

        /// 
        /// Opens a file. The function fails if the file does not exist. 
        /// 
        OpenExisting = 3,

        ///// 
        ///// Opens a file, always. 
        ///// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
        ///// 
        //OpenAlways = 4,

        ///// 
        ///// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
        ///// The calling process must open the file with the GENERIC_WRITE access right. 
        ///// 
        //TruncateExisting = 5
      }

      [Flags]
      public enum FileAttributes : uint
      {
        None = 0x00000000,
        //Readonly = 0x00000001,
        //Hidden = 0x00000002,
        //System = 0x00000004,
        //Directory = 0x00000010,
        //Archive = 0x00000020,
        //Device = 0x00000040,
        //Normal = 0x00000080,
        //Temporary = 0x00000100,
        //SparseFile = 0x00000200,
        //ReparsePoint = 0x00000400,
        //Compressed = 0x00000800,
        //Offline = 0x00001000,
        //NotContentIndexed = 0x00002000,
        //Encrypted = 0x00004000,
        //Write_Through = 0x80000000,
        //Overlapped = 0x40000000,
        //NoBuffering = 0x20000000,
        //RandomAccess = 0x10000000,
        //SequentialScan = 0x08000000,
        //DeleteOnClose = 0x04000000,
        //BackupSemantics = 0x02000000,
        //PosixSemantics = 0x01000000,
        //OpenReparsePoint = 0x00200000,
        //OpenNoRecall = 0x00100000,
        //FirstPipeInstance = 0x00080000
      }

      [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      internal static extern SafeFileHandle CreateFile(
            string lpFileName,
            FileAccess dwDesiredAccess,
            ShareMode dwShareMode,
            IntPtr SecurityAttributes,
            CreationDisposition dwCreationDisposition,
            FileAttributes dwFlagsAndAttributes,
            IntPtr hTemplateFile);

      [Flags]
      private enum DeviceTypes : uint
      {
        PhysicalNetcard = 0x00000017,
      }

      [Flags]
      private enum Methods : uint
      {
        //Buffered = 0,
        //InDirect = 1,
        OutDirect = 2,
        //Neither = 3
      }

      public enum NdisOids : uint
      {
        //OID_802_3_PERMANENT_ADDRESS = 0x01010101,
        OID_802_3_CURRENT_ADDRESS = 0x01010102,
        //OID_802_3_MULTICAST_LIST = 0x01010103,
        //OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104,
      }

      [Flags]
      public enum IOControlCode : uint
      {
        // Physical Netcard
        IOCTL_NDIS_QUERY_GLOBAL_STATS = (DeviceTypes.PhysicalNetcard << 16) | (0x0000 << 2) | Methods.OutDirect,
        //IOCTL_NDIS_QUERY_ALL_STATS = (DeviceTypes.PhysicalNetcard << 16) | (0x0001 << 2) | Methods.OutDirect,
        //IOCTL_NDIS_DO_PNP_OPERATION = (DeviceTypes.PhysicalNetcard << 16) | (0x0002 << 2) | Methods.Buffered,
        //IOCTL_NDIS_QUERY_SELECTED_STATS = (DeviceTypes.PhysicalNetcard << 16) | (0x0003 << 2) | Methods.OutDirect,
        //IOCTL_NDIS_ENUMERATE_INTERFACES = (DeviceTypes.PhysicalNetcard << 16) | (0x0004 << 2) | Methods.Buffered,
        //IOCTL_NDIS_ADD_TDI_DEVICE = (DeviceTypes.PhysicalNetcard << 16) | (0x0005 << 2) | Methods.Buffered,
        //IOCTL_NDIS_GET_LOG_DATA = (DeviceTypes.PhysicalNetcard << 16) | (0x0007 << 2) | Methods.OutDirect,
        //IOCTL_NDIS_GET_VERSION = (DeviceTypes.PhysicalNetcard << 16) | (0x0008 << 2) | Methods.Buffered,
      };

      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
      public static extern bool DeviceIoControl(
          SafeFileHandle hDevice,
          [MarshalAs(UnmanagedType.U4)] IOControlCode dwIoControlCode,
          IntPtr lpInBuffer,
          [MarshalAs(UnmanagedType.U4)]uint nInBufferSize,
          IntPtr lpOutBuffer,
          [MarshalAs(UnmanagedType.U4)] uint nOutputBufferSize,
          [MarshalAs(UnmanagedType.U4)] out uint pBytesReturned,
          IntPtr pOverlapped);
    }

    /// 
    /// Gets the MAC address of the specified network adapter instance.
    /// 
    /// The network adapter guid.
    /// The network adapter MAC address.
    /// 
    /// 
    /// This implementation uses low level system calls. Though there are
    /// many other options available (like e. g. )
    /// these do only work with network adapters which are bound to the IP stack.
    /// The implementation provided here also works with IP less network adapters.
    /// It uses a DeviceIoControl system call to the network adapter's driver instance.
    /// 
    /// 
    /// An alternate solution could use WMI and query the Win32_NetworkAdapterConfiguration
    /// object, but this is slow and introduces a dependency on the WMI infrastructure.
    /// This in turn causes delays during system startup.
    /// 
    /// 
    public static PhysicalAddress GetMacAddress(string guid)
    {
      _log.DebugFormat("Retrieving MAC address for network adapter: {0}", guid);
      SafeFileHandle hFile = null;
      try
      {
        // Open device driver
        var fileName = string.Format(@"\\.\{0}", guid);
        _log.DebugFormat("Opening network adapter driver: {0}", fileName);
        hFile = SafeNativeMethods.CreateFile(
          fileName,
          SafeNativeMethods.FileAccess.GenericRead,
          SafeNativeMethods.ShareMode.Read | SafeNativeMethods.ShareMode.Write,
          IntPtr.Zero,
          SafeNativeMethods.CreationDisposition.OpenExisting,
          SafeNativeMethods.FileAttributes.None,
          IntPtr.Zero);
        if (hFile.IsInvalid)
        {
          var errorCode = Marshal.GetLastWin32Error();
          var msg = string.Format("CreateFile('{0}') failed with error code {1}", fileName, errorCode);
          _log.Error(msg);
          throw new Win32Exception(errorCode, msg);
        }

        // Execute DeviceIoControl
        _log.DebugFormat("Calling DeviceIoControl on network adapter driver: {0}", fileName);
        var handle = GCHandle.Alloc((uint)SafeNativeMethods.NdisOids.OID_802_3_CURRENT_ADDRESS, GCHandleType.Pinned);
        var ptrInput = handle.AddrOfPinnedObject();
        var address = new byte[6];
        var ptrOutput = Marshal.AllocHGlobal(address.Length);
        uint bytesReturned;
        var res = SafeNativeMethods.DeviceIoControl(
          hFile,
          SafeNativeMethods.IOControlCode.IOCTL_NDIS_QUERY_GLOBAL_STATS,
          ptrInput,
          (uint)Marshal.SizeOf(typeof(uint)),
          ptrOutput,
          (uint)address.Length,
          out bytesReturned,
          IntPtr.Zero);
        handle.Free();
        if (!res)
        {
          Marshal.FreeHGlobal(ptrOutput);
          var errorCode = Marshal.GetLastWin32Error();
          var msg = string.Format("DeviceIoControl failed with error code {0}", errorCode);
          _log.Error(msg);
          throw new Win32Exception(errorCode, msg);
        }
        
        // Validate length of returned value
        if (bytesReturned != address.Length)
        {
          Marshal.FreeHGlobal(ptrOutput);
          var msg = string.Format("DeviceIoControl() returned unexpected size: {0} (expected was: {1}", bytesReturned, address.Length);
          _log.Error(msg);
          throw new InvalidOperationException(msg);
        }

        Marshal.Copy(ptrOutput, address, 0, address.Length);
        Marshal.FreeHGlobal(ptrOutput);

        var macAddress = new PhysicalAddress(address);
        _log.InfoFormat("Retrieved MAC address {0} for network adapter: {1}", macAddress, guid);
        return macAddress;
      }
      catch (Exception ex)
      {
        var msg = string.Format("Failed to retrieve MAC address for network adapter: {0}", guid);
        _log.Error(msg, ex);
        throw;
      }
      finally
      {
        if (hFile != null)
        {
          hFile.Close();
        }
      }
    }
  }
}