Feeds:
Posts
Comments

Archive for the ‘Miscellaneous’ Category

First off, what the fork is a Human Interface Device? Well, you are potentially using two right now, your keyboard and mouse. When referring to an HID we are usually speaking of a USB-HID, or a device connected to your PC that supports bi-directional communication. Wait, how does your keyboard support bi-directional communication? Well, when Caps Lock isn’t there a light to notify you of that? In fact, on your Windows OS, if you go to Start > Control Panel > Device Manager, and expand the Keyboards Node, there is a good chance you’ll see a HID Keyboard Device listed. While there, expand the Human Interface Devices node to see what other HIDs are currently connected if any. For more general information on Human Interface Devices have a look here.

Now, if you’ve ever written an application that requires to you to communicate with one of these HIDs, then you probably have to go find the correct target device first. I’m going to show you how to do that in the following sample. First, we need a class to give us access to functions like SetupDiGetDeviceInterfaceDetail, SetupDiEnumDeviceInterfaces, and a few others. Much of the functionality in the following class, and the others, originated from a nice codeplex project here. Now for the HIDImports.cs code.

//////////////////////////////////////////////////////////////////////////////////
//	HIDImports.cs
//	For more information: http://wiimotelib.codeplex.com/
//////////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace CreateBinaryFromXml
{
        public class HIDImports
        {
            //Flags controlling what is included in the device information set built by SetupDiGetClassDevs
            internal const int DIGCF_DEFAULT = 0x00000001;   //only valid with DIGCF_DEVICEINTERFACE
            internal const int DIGCF_PRESENT = 0x00000002;
            internal const int DIGCF_ALLCLASSES = 0x00000004;
            internal const int DIGCF_PROFILE = 0x00000008;
            internal const int DIGCF_DEVICEINTERFACE = 0x00000010;

            [Flags]
            internal enum EFileAttributes : uint
            {
                   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
            }

            [StructLayout(LayoutKind.Sequential)]
            internal struct SP_DEVINFO_DATA
            {
                   public uint cbSize;
                   public Guid ClassGuid;
                   public uint DevInst;
                   public IntPtr Reserved;
            }

            [StructLayout(LayoutKind.Sequential)]
            internal struct SP_DEVICE_INTERFACE_DATA
            {
                   public int cbSize;
                   public Guid InterfaceClassGuid;
                   public int Flags;
                   public IntPtr RESERVED;
            }

            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
            {
                   public UInt32 cbSize;
                   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
                   public string DevicePath;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct HIDD_ATTRIBUTES
            {
                   public int Size;
                   public short VendorID;
                   public short ProductID;
                   public short VersionNumber;
            }

            [DllImport(@"hid.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern void HidD_GetHidGuid(out Guid gHid);

            [DllImport("hid.dll")]
            internal static extern Boolean HidD_GetAttributes(IntPtr HidDeviceObject, ref HIDD_ATTRIBUTES Attributes);

            [DllImport("hid.dll")]
            internal extern static bool HidD_SetOutputReport(IntPtr HidDeviceObject, byte[] lpReportBuffer, uint ReportBufferLength);

            [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
            internal static extern IntPtr SetupDiGetClassDevs(
                   ref Guid ClassGuid,
                   [MarshalAs(UnmanagedType.LPTStr)] string Enumerator,
                   IntPtr hwndParent,
                   UInt32 Flags);

            [DllImport(@"setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern Boolean SetupDiEnumDeviceInterfaces(
                   IntPtr hDevInfo,
                   IntPtr devInvo,
                   ref Guid interfaceClassGuid,
                   UInt32 memberIndex,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

            [DllImport(@"setupapi.dll", SetLastError = true)]
            internal static extern Boolean SetupDiGetDeviceInterfaceDetail(
                   IntPtr hDevInfo,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
                   IntPtr deviceInterfaceDetailData,
                   UInt32 deviceInterfaceDetailDataSize,
                   out UInt32 requiredSize,
                   IntPtr deviceInfoData);

            [DllImport(@"setupapi.dll", SetLastError = true)]
            internal static extern Boolean SetupDiGetDeviceInterfaceDetail(
                   IntPtr hDevInfo,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
                   ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
                   UInt32 deviceInterfaceDetailDataSize,
                   out UInt32 requiredSize,
                   IntPtr deviceInfoData);

            [DllImport(@"setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern UInt16 SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);

            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
            internal static extern SafeFileHandle CreateFile(
                   string fileName,
                   [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
                   [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
                   IntPtr securityAttributes,
                   [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
                   [MarshalAs(UnmanagedType.U4)] EFileAttributes flags,
                   IntPtr template);

            [DllImport("kernel32.dll", SetLastError=true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseHandle(IntPtr hObject);
       }
}

And now on to the fun part. Here is where we enumerate through the connected HIDs. There are a bunch of comments in the code that should guide you through what is going on.

public static string EnumerateHids()
{
    StringBuilder details = new StringBuilder();
    HIDImports.HIDD_ATTRIBUTES deviceAttributes;
    Guid guid;
    uint index = 0;

    // get the GUID of the HID class
    HIDImports.HidD_GetHidGuid(out guid);

    // get a handle to all devices that are part of the HID class
    IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);

    // create a new interface data struct and initialize its size
    HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
    diData.cbSize = Marshal.SizeOf(diData);

    // get a device interface to a single device (enumerate all devices)
    while (HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index, ref diData))
    {
        UInt32 size = 0;

        // get the buffer size for this device detail instance (returned in the size parameter)
        HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);

        // create a detail struct and set its size
        HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();

        //On Win x86, cbSize = 5, On x64, cbSize = 8
        diDetail.cbSize = (IntPtr.Size == 8) ? (uint)8 : (uint)5;

        // actually get the detail struct
        if (HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero))
        {
            // open a read/write handle to our device using the DevicePath returned
            SafeHandle safeHandle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero);

            // create an attributes struct and initialize the size
            deviceAttributes = new HIDImports.HIDD_ATTRIBUTES();
            deviceAttributes.Size = Marshal.SizeOf(deviceAttributes);

            // get the attributes of the current device
            if (HIDImports.HidD_GetAttributes(safeHandle.DangerousGetHandle(), ref deviceAttributes))
            {
                details.AppendLine("--- HID DEVICE FOUND ---");
                details.AppendLine(String.Format("ProductID: 0x{0}", deviceAttributes.ProductID.ToString("X4")));
                details.AppendLine(String.Format("VendorID: 0x{0}", deviceAttributes.VendorID.ToString("X4")));
                details.AppendLine(String.Format("VersionNumber: 0x{0}", deviceAttributes.VersionNumber.ToString("X4")));
                details.AppendLine(String.Format("Size: 0x{0}", deviceAttributes.Size.ToString("X4")));
                details.AppendLine();
            }
        }
        index++;
    }
    return details.ToString();
}

So as you can see, we’re looping over all HIDs and creating a string to let you know which devices are connected and what their attributes are. Now all you have to do is connect to the device that matches your ProductId, VendorId, etc. I may go through doing this in a follow up post, but that’s it for now. Again, I would like to attribute the bulk of this work to the WiiMote codeplex project referenced earlier in the post. Enjoy!!

Read Full Post »

Have you ever wanted to add a method to an existing type without recompiling? Well in C# 3.0 you were given that ability with extension methods. On the MSDN it states that “extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.” I am going to give you a very simple example. Lets say you are creating a music management application and you have a class called Artist that looks like the following:

namespace MusicApp
{
     public class Artist
     {
          #region Properties

          public DateTime BirthDate { get; set; }
          public string FirstName { get; set; }
          public string LastName { get; set; }

          #endregion

          #region Constructor(s)

          public Artist(string lastName, string firstName, DateTime birthDate)
          {
               this.BirthDate = birthDate;
               this.LastName = lastName;
               this.FirstName = firstName;
          }

          #endregion
     }
}

As you can see the artist class has a birth date. What if we wanted to find the age of the artist? We could easily add a method to the class to do so, but lets use an extension method instead. We’ll be placing the code for our extension method inside a new public static class conveniently called ExtensionMethods. Here is the code:

static class ExtensionMethods
{
     public static int Age(this Artist artist)
     {
          DateTime now = DateTime.Now;
          int yearsPassed = DateTime.Now.Year - artist.BirthDate.Year;

          if (now.Month < artist.BirthDate.Month || (now.Month == artist.BirthDate.Month && now.Day < artist.BirthDate.Day))
          {
               yearsPassed--;
          }

          return yearsPassed;
     }
}

Please note that the first parameter to the extension method specifies the type the method operates on and is preceded by the this modifier. In order to use the new extension method we must import the namespace it was defined in. We are then able to determine an artist’s age like so. Enjoy!

Artist tomPetty = new Artist("Petty", "Tom", new System.DateTime(1950, 10, 20));
int age = tomPetty.Age();

Read Full Post »

A little while back I was tasked with creating a custom window in WPF that had its own look and feel. In order to do this you must first set the WindowStyle to None. This is great because it opens the door for you to customize your new window to look however you want. Unfortunately, when you set your WindowStyle to None you lose all of the base functionality of the window. You now have to implement native window actions such as sizing, dragging, the minimize, maximize/restore, and close buttons, etc. After looking around the web for a while I found a really good starting point for creating my custom window. Here is a link to a sample project that has an Office Style custom window already created.

http://blog.opennetcf.com/ayakhnin/content/binary/OfficeStyleWindow.zip

In this project you will find a nicely styled window whose look should be easily customizable. Now we just have to add some code to make our new window accommodate for the taskbar. One thing we must be especially aware of is accommodating for the taskbar when it is set to auto-hide. When the taskbar is auto-hidden, you have to leave 2 pixels available for the bar on the docked edge so the user can mouse over that area to restore the hidden taskbar.

After much testing and digging I ended up writing a public static WindowSizing class that should handle the bar correctly. Using the sample linked above, we must first insert these two lines inside of the Window1 constructor after InitializeComponent(); in the Window1.xaml.cs file.

this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.SourceInitialized += new EventHandler(Window1_SourceInitialized);

Here is the code for the event handlers.

void Window1_Loaded(object sender, RoutedEventArgs e)
{
     this.WindowState = WindowState.Maximized;
}

void Window1_SourceInitialized(object sender, EventArgs e)
{
     WindowSizing.WindowInitialized(this);
}

And here is the public static WindowSizing class code which should be self explanatory.

using System;
using System.Runtime.InteropServices;
using System.Windows;

namespace OfficeStyleWindowProject
{
    public static class WindowSizing
    {
        const int MONITOR_DEFAULTTONEAREST = 0x00000002;

        #region DLLImports

        [DllImport("shell32", CallingConvention = CallingConvention.StdCall)]
        public static extern int SHAppBarMessage(int dwMessage, ref APPBARDATA pData);

        [DllImport("user32", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32")]
        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

        [DllImport("user32")]
        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

        #endregion

        private static MINMAXINFO AdjustWorkingAreaForAutoHide(IntPtr monitorContainingApplication, MINMAXINFO mmi)
        {
            IntPtr hwnd = FindWindow("Shell_TrayWnd", null);
            if (hwnd == null) return mmi;
            IntPtr monitorWithTaskbarOnIt = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
            if (!monitorContainingApplication.Equals(monitorWithTaskbarOnIt)) return mmi;
            APPBARDATA abd = new APPBARDATA();
            abd.cbSize = Marshal.SizeOf(abd);
            abd.hWnd = hwnd;
            SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref abd);
            int uEdge = GetEdge(abd.rc);
            bool autoHide = System.Convert.ToBoolean(SHAppBarMessage((int)ABMsg.ABM_GETSTATE, ref abd));

            if (!autoHide) return mmi;

            switch (uEdge)
            {
                case (int)ABEdge.ABE_LEFT:
                    mmi.ptMaxPosition.x += 2;
                    mmi.ptMaxTrackSize.x -= 2;
                    mmi.ptMaxSize.x -= 2;
                    break;
                case (int)ABEdge.ABE_RIGHT:
                    mmi.ptMaxSize.x -= 2;
                    mmi.ptMaxTrackSize.x -= 2;
                    break;
                case (int)ABEdge.ABE_TOP:
                    mmi.ptMaxPosition.y += 2;
                    mmi.ptMaxTrackSize.y -= 2;
                    mmi.ptMaxSize.y -= 2;
                    break;
               case (int)ABEdge.ABE_BOTTOM:
                    mmi.ptMaxSize.y -= 2;
                    mmi.ptMaxTrackSize.y -= 2;
                    break;
                default:
                    return mmi;
            }
            return mmi;
        }

        private static int GetEdge(RECT rc)
        {
            int uEdge = -1;
            if (rc.top == rc.left && rc.bottom > rc.right)
                uEdge = (int)ABEdge.ABE_LEFT;
            else if (rc.top == rc.left && rc.bottom < rc.right)
                uEdge = (int)ABEdge.ABE_TOP;
            else if (rc.top > rc.left)
                uEdge = (int)ABEdge.ABE_BOTTOM;
            else
                uEdge = (int)ABEdge.ABE_RIGHT;
            return uEdge;
        }

        public static void WindowInitialized(Window window)
        {
            IntPtr handle = (new System.Windows.Interop.WindowInteropHelper(window)).Handle;
            System.Windows.Interop.HwndSource.FromHwnd(handle).AddHook(new System.Windows.Interop.HwndSourceHook(WindowProc));
        }

        private static IntPtr WindowProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
               case 0x0024:
                    WmGetMinMaxInfo(hwnd, lParam);
                    handled = true;
                    break;
            }

            return (IntPtr)0;
        }

        private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
        {
            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
            IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

            if (monitorContainingApplication != System.IntPtr.Zero)
            {
                MONITORINFO monitorInfo = new MONITORINFO();
                GetMonitorInfo(monitorContainingApplication, monitorInfo);
                RECT rcWorkArea = monitorInfo.rcWork;
                RECT rcMonitorArea = monitorInfo.rcMonitor;
                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
                mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x;                                                 //maximum drag X size for the window
                mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;                                                 //maximum drag Y size for the window
                mmi.ptMinTrackSize.x = 800;                                                             //minimum drag X size for the window
                mmi.ptMinTrackSize.y = 600;                                                             //minimum drag Y size for the window
                mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi);                  //need to adjust sizing if taskbar is set to autohide
            }
            Marshal.StructureToPtr(mmi, lParam, true);
        }

        public enum ABEdge
        {
            ABE_LEFT = 0,
            ABE_TOP = 1,
            ABE_RIGHT = 2,
            ABE_BOTTOM = 3
        }

        public enum ABMsg
        {
            ABM_NEW = 0,
            ABM_REMOVE = 1,
            ABM_QUERYPOS = 2,
            ABM_SETPOS = 3,
            ABM_GETSTATE = 4,
            ABM_GETTASKBARPOS = 5,
            ABM_ACTIVATE = 6,
            ABM_GETAUTOHIDEBAR = 7,
            ABM_SETAUTOHIDEBAR = 8,
            ABM_WINDOWPOSCHANGED = 9,
            ABM_SETSTATE = 10
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct APPBARDATA
        {
            public int cbSize;
            public IntPtr hWnd;
            public int uCallbackMessage;
            public int uEdge;
            public RECT rc;
            public bool lParam;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MINMAXINFO
        {
            public POINT ptReserved;
            public POINT ptMaxSize;
            public POINT ptMaxPosition;
            public POINT ptMinTrackSize;
            public POINT ptMaxTrackSize;
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class MONITORINFO
        {
            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
            public RECT rcMonitor = new RECT();
            public RECT rcWork = new RECT();
            public int dwFlags = 0;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;

            public POINT(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
    }
}

If you have any questions or problems please feel free to leave a comment.

For a downloadable sample please go here.

Read Full Post »