Dev Blog - Johan Danforth

I'm Johan Danforth and this is my dev blog - a mix of .NET, ASP.NET, Rest, Azure and some other random coding stuff.

  • Detecting Idle Time with Global Mouse and Keyboard Hooks in WPF

    Years and years ago I wrote this blog post about detecting if the user was idle or active at the keyboard (and mouse) using a global hook. Well that code was for .NET 2.0 and Windows Forms and for some reason I wanted to try the same in WPF and noticed that a few things around the keyboard and mouse hooks didn’t work as expected in the WPF environment. So I had to change a few things and here’s the code for it, working in .NET 4.

    I took the liberty and refactored a few things while at it and here’s the code now. I’m sure I will need it in the far future as well.

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    
    namespace Irm.Tim.Snapper.Util
    {
        public class ClientIdleHandler : IDisposable
        {
            public bool IsActive { get; set; }
    
            int _hHookKbd;
            int _hHookMouse;
    
            public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
            public event HookProc MouseHookProcedure;
            public event HookProc KbdHookProcedure;
    
            //Use this function to install thread-specific hook.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
                 CallingConvention = CallingConvention.StdCall)]
            public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
                IntPtr hInstance, int threadId);
    
            //Call this function to uninstall the hook.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
                 CallingConvention = CallingConvention.StdCall)]
            public static extern bool UnhookWindowsHookEx(int idHook);
    
            //Use this function to pass the hook information to next hook procedure in chain.
            [DllImport("user32.dll", CharSet = CharSet.Auto,
                 CallingConvention = CallingConvention.StdCall)]
            public static extern int CallNextHookEx(int idHook, int nCode,
                IntPtr wParam, IntPtr lParam);
    
            //Use this hook to get the module handle, needed for WPF environment
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr GetModuleHandle(string lpModuleName);
    
            public enum HookType : int
            {
                GlobalKeyboard = 13,
                GlobalMouse = 14
            }
    
            public int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                //user is active, at least with the mouse
                IsActive = true;
                Debug.Print("Mouse active");
    
                //just return the next hook
                return CallNextHookEx(_hHookMouse, nCode, wParam, lParam);
            }
    
            public int KbdHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                //user is active, at least with the keyboard
                IsActive = true;
                Debug.Print("Keyboard active");
    
                //just return the next hook
                return CallNextHookEx(_hHookKbd, nCode, wParam, lParam);
            }
    
            public void Start()
            {
                using (var currentProcess = Process.GetCurrentProcess())
                using (var mainModule = currentProcess.MainModule)
                {
    
                    if (_hHookMouse == 0)
                    {
                        // Create an instance of HookProc.
                        MouseHookProcedure = new HookProc(MouseHookProc);
                        // Create an instance of HookProc.
                        KbdHookProcedure = new HookProc(KbdHookProc);
    
                        //register a global hook
                        _hHookMouse = SetWindowsHookEx((int)HookType.GlobalMouse,
                                                      MouseHookProcedure,
                                                      GetModuleHandle(mainModule.ModuleName),
                                                      0);
                        if (_hHookMouse == 0)
                        {
                            Close();
                            throw new ApplicationException("SetWindowsHookEx() failed for the mouse");
                        }
                    }
    
                    if (_hHookKbd == 0)
                    {
                        //register a global hook
                        _hHookKbd = SetWindowsHookEx((int)HookType.GlobalKeyboard,
                                                    KbdHookProcedure,
                                                    GetModuleHandle(mainModule.ModuleName),
                                                    0);
                        if (_hHookKbd == 0)
                        {
                            Close();
                            throw new ApplicationException("SetWindowsHookEx() failed for the keyboard");
                        }
                    }
                }
            }
    
            public void Close()
            {
                if (_hHookMouse != 0)
                {
                    bool ret = UnhookWindowsHookEx(_hHookMouse);
                    if (ret == false)
                    {
                        throw new ApplicationException("UnhookWindowsHookEx() failed for the mouse");
                    }
                    _hHookMouse = 0;
                }
    
                if (_hHookKbd != 0)
                {
                    bool ret = UnhookWindowsHookEx(_hHookKbd);
                    if (ret == false)
                    {
                        throw new ApplicationException("UnhookWindowsHookEx() failed for the keyboard");
                    }
                    _hHookKbd = 0;
                }
            }
    
            #region IDisposable Members
    
            public void Dispose()
            {
                if (_hHookMouse != 0 || _hHookKbd != 0)
                    Close();
            }
    
            #endregion
        }
    }

    The way you use it is quite simple, for example in a WPF application with a simple Window and a TextBlock:

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <TextBlock Name="IdleTextBox"/>
        </Grid>
    </Window>
    
    

    And in the code behind we wire up the ClientIdleHandler and a DispatcherTimer that ticks every second:

    public partial class MainWindow : Window
    {
        private DispatcherTimer _dispatcherTimer;
        private ClientIdleHandler _clientIdleHandler;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //start client idle hook
            _clientIdleHandler = new ClientIdleHandler();
            _clientIdleHandler.Start();
            
            //start timer
            _dispatcherTimer = new DispatcherTimer();
            _dispatcherTimer.Tick += TimerTick;
            _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1);
            _dispatcherTimer.Start();
        }
    
        private void TimerTick(object sender, EventArgs e)
        {
            if (_clientIdleHandler.IsActive)
            {
                IdleTextBox.Text = "Active";
                //reset IsActive flag
                _clientIdleHandler.IsActive = false;    
            }
            else IdleTextBox.Text = "Idle";
        }
    }

    Remember to reset the ClientIdleHandle IsActive flag after a check.

  • Azure Summit Sweden

    Azure Summit Sweden, held at Microsoft Sweden, was quite successful I would say. Every session was more or less fully booked and had 100 people or so attending.

    I held a session with Dag König on the basics of Web Role and Worker Role and promised to put the code for the “Twitted Demo” on Codeplex so that those who were interested could work on the code together and transform it into something that would or could work in a larger scenario.

    So I put the code up on Codeplex and called it Another Azure Demo Project - http://anotherazuredemoproj.codeplex.com/.

    Feel free to go there, and request to join the project if you like!

    The initial version of the code is made as a simple getting started demo, made for one instance of a web role and one instance of a worker role. The code has a worker role which does a search for specific words or tags on Twitter and stores the number of hits for that word in a table storage. A web page displays the number of hits per minute in a graph. This hand/mouse written sketch should explain the overall idea pretty much I think/hope (it's sketchy on purpose :)
    twitter demo sketch.png
    The Future Version
    The idea is to make the code grow into something that is more "Azureable", something that would support multiple instances of the web and worker role. I've not worked out the final scenario yet, but one place to start would be to support searching for loads of tags, and have the search distributed between multiple instances of the worker role.

  • Extended Silverlight AutoCompleteBox for Handling Enter and Command Binding

    In our RIA Services application, we’re using the AutoCompleteBox control, but what if you want to select something from the list, press Enter and bind to a Command in a viewmodel? Sort of like the Button Command property.

    I decided to extend the control and add a “Command” DependencyProperty for an ICommand which is executed when Enter is pressed and if an item is selected in the AutoCompleteBox. Works for us:

    public class AutoCompleteBoxEx : AutoCompleteBox

    {

        protected override void OnKeyDown(KeyEventArgs e)

        {

            if (SelectedItem != null && e.Key == Key.Enter)

            {

                if (Command != null)

                    Command.Execute(SelectedItem);

     

            }

            base.OnKeyDown(e);

        }

     

        public static readonly DependencyProperty CommandProperty =

            DependencyProperty.RegisterAttached("Command",

                        typeof(ICommand),

                        typeof(AutoCompleteBoxEx),

                        null);

     

        public ICommand Command

        {

            get { return (ICommand)GetValue(CommandProperty); }

            set { SetValue(CommandProperty, value); }

        }

    }

  • Silverlight AutoCompleteBox DataTemplate

    This is more a note for myself how to do this. The Silverlight 4 SDK contains a pretty good AutoCompleteBox that you can use. Download/install the SDK and and the control will be available in your Silverlight Controls Toolbox.

    If you try to use this with RIA Services, you may notice it is displaying the Id/Key in the dropdown, but you yourself want to show a name or description. In that case you have to add a DataTemplate section to the AutoCompleteBox:

    <sdk:AutoCompleteBox x:Name="FilterTextBox"
                           FilterMode="Contains"
                           MaxDropDownHeight="150"
    ItemsSource="{Binding Data, ElementName=dds}"
                           ValueMemberBinding="{Binding Name}">
        <sdk:AutoCompleteBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" />
            </DataTemplate>
        </sdk:AutoCompleteBox.ItemTemplate>
    </sdk:AutoCompleteBox>

    That should do it.

  • Masked Silverlight TextBox for Swedish National Identification Number (Personnummer)

    In our upcoming Silverlight 4 project there’s a need for a textbox with a mask for entering a Swedish national identification number (personnummer) with either 10 or 12 digits, in the form of yyyymmdd-nnnn. It’s quite simple, I just want to make sure only digits are entered and that the dash (-) is placed automatically and a few other small things that I’d like to tweak.

    As far as I can see (correct me if I’m wrong here) there’s no mask feature in SL4 (yet), so I’m simply going to inherit and extend the built in TextBox. Validation of the number is done in the viewmodel instead of built into the control. See upcoming blogpost for that.

    So, enough talk already, hope it can be of use:

    public class PersonnummerTextBox : TextBox
    {
        public bool TwelveDigits { get; set; }

        private int _leftPart;
        private Key _lastKey;

        public PersonnummerTextBox()
        {
            TextChanged += OnTextChanged;
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            _leftPart = TwelveDigits ? 8 : 6;
        }

        /// <summary>
        /// Only allow digits and tab, no shifts or ctrl
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            _lastKey = e.Key;   //used in OnTextChanged to handle Del key

            base.OnKeyDown(e);

            if (((e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) ||
                (e.Key >= Key.D0 && e.Key <= Key.D9) ||
                e.Key == Key.Tab) &&    //so that we can TAB out from the control ;)
                (Keyboard.Modifiers & ModifierKeys.Shift) == 0 &&
                (Keyboard.Modifiers & ModifierKeys.Control) == 0)
            {
                e.Handled = false;
                return;
            }

            e.Handled = true;
        }

        /// <summary>
        /// Make sure the dash (-) is placed on the correct place automatically
        /// either as yyyymmdd-nnnn or yymmdd-nnnn
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            var leftPartPlusDash = (_leftPart + 1);
            var cursorPosition = SelectionStart;
            var strippedText = Text.Replace("-", "");

            if (strippedText.Length > _leftPart)
            {
                //move the cursor after the dash
                if (strippedText.Length == leftPartPlusDash &&
                    cursorPosition == leftPartPlusDash &&
                    _lastKey != Key.Delete)
                    cursorPosition = (_leftPart + 2);

                //put a dash in the right place
                Text = string.Format("{0}-{1}",
                    strippedText.Substring(0, _leftPart),
                    strippedText.Substring(_leftPart, Math.Min(4, strippedText.Length - _leftPart)));
            }
            else
                Text = strippedText;

            //move cursor to the wanted position
            SelectionStart = cursorPosition;
        }
    }
  • Silverlight Watermark TextBox Behavior

    (using Visual Studio 2010 and .NET/Silverlight 4)

    Maybe there is a working solution for this already out there, but I created my own Silverlight Behavior for a basic TextBox Watermark which might be useful.

    I wanted to use it like this in my XAML (look at the behaviors tag):

    <TextBlock Margin="5">Watermarked textbox:</TextBlock>
    <TextBox Margin="5">
        <Interactivity:Interaction.Behaviors>
            <local:Watermark Text="Watermark" Foreground="LightGray" />
        </Interactivity:Interaction.Behaviors>
    </TextBox>

     

    The result should be something like this:

    image

     

    To create a Behavior for Silverlight, you must get hold of the System.Windows.Interactivity assembly which ships with Expression Blend. In my system it’s located at:

    c:\Program Files (x86)\Microsoft SDKs\Expression\Blend\Silverlight\v4.0\Libraries\System.Windows.Interactivity.dll

     

    And the code for the Behavior:

    public class Watermark : Behavior<TextBox>
    {
        private bool _hasWatermark;
        private Brush _textBoxForeground;

        public String Text { get; set; }
        public Brush Foreground { get; set; }

        protected override void OnAttached()
        {
            _textBoxForeground = AssociatedObject.Foreground;

            base.OnAttached();
            if (Text != null)
                SetWatermarkText();
            AssociatedObject.GotFocus += GotFocus;
            AssociatedObject.LostFocus += LostFocus;
        }

        private void LostFocus(object sender, RoutedEventArgs e)
        {
            if (AssociatedObject.Text.Length == 0)
                if (Text != null)
                    SetWatermarkText();
        }

        private void GotFocus(object sender, RoutedEventArgs e)
        {
            if (_hasWatermark)
                RemoveWatermarkText();
        }

        private void RemoveWatermarkText()
        {
            AssociatedObject.Foreground = _textBoxForeground;
            AssociatedObject.Text = "";
            _hasWatermark = false;
        }

        private void SetWatermarkText()
        {
            AssociatedObject.Foreground = Foreground;
            AssociatedObject.Text = Text;
            _hasWatermark = true;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.GotFocus -= GotFocus;
            AssociatedObject.LostFocus -= LostFocus;
        }
    }

    Like so many Watermark-solutions out there I’m hooking into the GotFocus/LostFocus events and to the work there. Works for me. Love Behaviors.

  • Silverlight DataGrid and DataPager Notes

    I need to write these things down somewhere...

    Brad's excellent tutorials and samples for learning Silverlight + RIA Services: http://blogs.msdn.com/b/brada/archive/2009/10/27/index-for-business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update.aspx

    Scott's super blog post about columns and the DataGrid: http://blogs.msdn.com/b/scmorris/archive/2008/03/27/defining-columns-for-a-silverlight-datagrid.aspx (it's getting "old" but still rocks)

    To use the DataPager properly with a DomainDataSource, you have to set a sort order. The docs for the DataPager says:

    If you use the DataPager in an application with an Entity Framework data store, you must order the data returned by your queries for the DataPager to work correctly. The Entity Framework does not support paging of data without an OrderBy clause.

    This can be done with the SortDescriptor property of the DomainDataSource, for example:

    <riaControls:DomainDataSource  Name="dds" AutoLoad="True"         
                                   QueryName="GetVesselsQuery"                               
                                   LoadSize="20" LoadedData="dds_LoadedData">
        <riaControls:DomainDataSource.DomainContext>
            <Services:SomeDomainContext />
        </riaControls:DomainDataSource.DomainContext>
        <riaControls:DomainDataSource.SortDescriptors>
            <riaControls:SortDescriptor PropertyPath="name" Direction="Ascending"/>
        </riaControls:DomainDataSource.SortDescriptors>
    </riaControls:DomainDataSource>

  • TFS Automatic Build Reporting Access to XAP Denied

    If you’re working with TFS and automatic builds of Silverlight apps, you may happen to stumble upon this error in the end of the build:

    Copying file C:\Builds\2\xxx\yyy\Binaries\zzz.xap failed. Access to the path 'C:\Builds\2\xxx\yyy\Sources\www\ClientBin\zzz.xap' is denied.

    The reason for this error I’m not 100% sure of, but it seems that the target file is read only and the build service do not have the proper access to overwrite it. Others have had the same problem with this and the suggested workaround/solution is to look up the \ClientBin\zzz.xap file in Source Control Explorer, right click to bring up the properties and the Security tab, then mark “Deny” to read for the Build Service. I did it for both the Builders group and the project collection build service accounts:

    image

    I found the tip for the solution here.

  • Insufficient Permissions Problems with MSDeploy and TFS Build 2010

    I ran into these problems on a TFS 2010 RC setup where I wanted to deploy a web site as part of the nightly build:

    C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets (3481): Web deployment task failed.(An error occurred when reading the IIS Configuration File 'MACHINE/REDIRECTION'. The identity performing the operation was 'NT AUTHORITY\NETWORK SERVICE'.)  An error occurred when reading the IIS Configuration File 'MACHINE/REDIRECTION'. The identity performing the operation was 'NT AUTHORITY\NETWORK SERVICE'. Filename: \\?\C:\Windows\system32\inetsrv\config\redirection.config Error: Cannot read configuration file due to insufficient permissions 

    As you can see I’m running the build service as NETWORK SERVICE which is quite usual. The first thing I did then was to give NETWORK SERVICE read access to the whole directory where redirection.config is sitting; C:\Windows\system32\inetsrv\config.

    That gave me a new error:

    C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets (3481): Web deployment task failed. (Attempted to perform an unauthorized operation.)

    The reason for this problem was that NETWORK SERVICE didn’t have write permission to the place where I’ve told MSDeploy to put the web site physically on the disk. Once I’d given the NETWORK SERVICE the right permissions, MSDeploy completed as expected!

    NOTE! I’ve not had this problem with TFS 2010 RTM, so it might be just a RC issue!

  • Package and Publish Web Sites with TFS 2010 Build Server

    To package and publish web sites with TFS 2010 Build Server, you can use MSDeploy and some of the new MSBuild arguments. For example:

    /p:DeployOnBuild=True

    /p:DeployTarget=MsDeployPublish

    /p:MSDeployPublishMethod=InProc

    /p:CreatePackageOnPublish=True

    /p:DeployIisAppPath="Default Web Site/WebApplication1"

    /p:MsDeployServiceUrl=localhost

    Does all the work for you! Unfortunately these arguments are not very well documented, yet. Please feel free comment with pointers to good docs. You can enter these arguments when editing the Build Definition, under the Process tab and the Advanced section:

    image

    If you’re working with these things, I’m sure you’ve not missed the PDC 2009 presentation by Vishal Joshi about MS Deploy.

    A few links on the topic:

    http://stackoverflow.com/questions/2636153/where-is-the-documentation-for-msbuild-arguments-to-run-msdeploy

    http://blogs.msdn.com/aspnetue/archive/2010/03/05/automated-deployment-in-asp-net-4-frequently-asked-questions.aspx

    http://www.hanselman.com/blog/WebDeploymentMadeAwesomeIfYoureUsingXCopyYoureDoingItWrong.aspx