Wednesday, June 11, 2014

BluetoothDiscovery on C# via the registry

BluetoothDiscovery

Discover Bluetooth Devices from c# using registry
Fast & easy code to discover blue tooth devices & their COM Ports on Windows 7 & Windows 8.
The idea is to extract out the device id from the registry key path, then match it appropriately to join the name with the port.

For Windows 7: The device id maps to the AssocBdAddr value under the LocalServicesnode.

In the example below:
646E6CC1CAB3 is the device id value that I map to "AssocBdAddr"=hex:b3,ca,c1,6c,6e,64,00,00 under LocalServices.

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM\{00001101-0000-1000-8000-00805f9b34fb}_LOCALMFG&000f\7&5461823&0&646E6CC1CAB3_C00000001\Device Parameters]
"PortName"="COM4"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\BTHPORT\Parameters\LocalServices\{00001101-0000-1000-8000-00805f9b34fb}\0]
"ServiceName"="MyBluetoothDeviceOnWindows7"
"AssocBdAddr"=hex:b3,ca,c1,6c,6e,64,00,00

For Windows 8: The device id maps to to a sub key Dev_XXXX where XXXX is your device id.

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM\{00001101-0000-1000-8000-00805f9b34fb}_LOCALMFG&000f\7&22abdcbb&0&646E6CC16891_C00000000\Device Parameters]
"PortName"="COM14"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM\Dev_646E6CC16891\7&13e7003a&0&BluetoothDevice_646E6CC16891]
"FriendlyName"="MyBluetoothDeviceOnWindows8"
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;
 
namespace BlueToothDevices
{
    public class BluetoothDevice
    {
        public string Name { getset; }
        public string Port { getset; }
        public string DeviceId { getset; }
 
        public override string ToString()
        {
            return string.Format("{0} [ {1} ] {2}", Name, Port, DeviceId);
        }
    }
 
    public class Bluetooth
    {
        private const string NullDeviceId = "000000000000";
        private const string BluetoothRegKey = @"SYSTEM\CurrentControlSet\Enum\BTHENUM";
        private const string BluetoothRegKey7 = @"SYSTEM\CurrentControlSet\services\BTHPORT\Parameters\LocalServices";
        private readonly Dictionary<stringBluetoothDevice> _devices = new Dictionary<stringBluetoothDevice>();
        public BluetoothDevice[] Devices { get { return _devices.Values.ToArray(); } }
 
        public static bool VerboseMode { getset; }
        private static void Verbose(string message, params object[] args)
        {
            if (VerboseMode)
                Console.WriteLine("{0}"string.Format(message, args));
        }
 
        public static List<string> DeviceNames
        {
            get
            {
                var devices = new List<string>();
                foreach (var device in GetBluetoothDevices())
                {
                    if (string.IsNullOrEmpty(device.Port))
                        continue;
 
                    devices.Add(string.Format("{0} {1}", device.Name, device.Port));
                }
                return devices;
            }
        }
 
        public static BluetoothDevice[] GetBluetoothDevices()
        {
            var bluetooth = new Bluetooth();
            bluetooth.GetDeviceNames();
            bluetooth.SetDevicePorts();
            return bluetooth.Devices;
        }
 
        private void GetDeviceNamesForWindows7()
        {
            using (var key = Registry.LocalMachine.OpenSubKey(BluetoothRegKey7, false))
            {
                if (key == null)
                {
                    Console.WriteLine("GetLocalServices: missing HKLM {0}", BluetoothRegKey7);
                    return;
                }
 
                foreach (var name in key.GetSubKeyNames())
                {
                    using (var k1 = OpenSubKey(key, name))
                    {
                        if (k1 == null)
                            continue;
 
                        foreach (var pnpName in k1.GetSubKeyNames())
                        {
                            using (var k2 = OpenSubKey(k1, pnpName))
                            {
                                if (k2 == null)
                                    continue;
 
                                var friendlyName = GetValue(k2, "ServiceName");
                                var deviceId = GetDeviceToken(GetValue(k2, "AssocBdAddr"));
                                if (string.IsNullOrEmpty(deviceId))
                                {
                                    Console.WriteLine("GetLocalServices: Missing required deviceId");
                                    continue;
                                }
 
                                var deviceInfo = new BluetoothDevice
                                {
                                    Name = friendlyName,
                                    DeviceId = deviceId,
                                };
                                Verbose("GetLocalServices: Add {0}", deviceInfo);
                                _devices.Add(deviceId, deviceInfo);
                            }
                        }
                    }
                }
            }
        }
 
        private void GetDeviceNames()
        {
            GetDeviceNamesForWindows7();
            GetDeviceNamesForWindows8();
        }
 
        private void GetDeviceNamesForWindows8()
        {
            using (var key = Registry.LocalMachine.OpenSubKey(BluetoothRegKey, false))
            {
                if (key == null)
                {
                    Console.WriteLine("GetDeviceNamesForWindows8: missing HKLM {0}", BluetoothRegKey);
                    return;
                }
 
                foreach (var name in key.GetSubKeyNames())
                {
                    using (var k1 = OpenSubKey(key, name))
                    {
                        if (k1 == null)
                            continue;
 
                        foreach (var pnpName in k1.GetSubKeyNames())
                        {
                            using (var pnpKey = OpenSubKey(k1, pnpName))
                            {
                                if (pnpKey == null)
                                    continue;
 
                                BluetoothDevice deviceInfo;
                                var deviceId = GetDeviceName2(pnpName);
                                if (string.IsNullOrEmpty(deviceId))
                                    continue;
 
                                var friendlyName = GetValue(pnpKey, "FriendlyName", pnpName);
                                if (!_devices.TryGetValue(deviceId, out deviceInfo))
                                {
                                    deviceInfo = new BluetoothDevice();
                                    _devices.Add(deviceId, deviceInfo);
                                }
 
                                if (string.IsNullOrEmpty(deviceInfo.Name))
                                    deviceInfo.Name = friendlyName;
                                deviceInfo.DeviceId = deviceId;
                                Verbose("GetDevicenamesForWindows8: Add {0}", deviceInfo);
                            }
                        }
                    }
                }
            }
        }
 
        private void SetDevicePorts()
        {
            using (var key = Registry.LocalMachine.OpenSubKey(BluetoothRegKey, false))
            {
                if (key == null)
                {
                    Console.WriteLine("SetDevicePorts: missing HKLM {0}", BluetoothRegKey);
                    return;
                }
 
                foreach (var name in key.GetSubKeyNames())
                {
                    using (var k1 = OpenSubKey(key, name))
                    {
                        if (k1 == null)
                            continue;
 
                        foreach (var pnpName in k1.GetSubKeyNames())
                        {
                            using (var pnpKey = OpenSubKey(k1, pnpName))
                            {
                                if (pnpKey == null)
                                    continue;
 
                                BluetoothDevice deviceInfo;
                                var isLocalMfg = name.Contains("LOCALMFG");
                                if (!isLocalMfg)
                                    continue;
 
                                var deviceId = GetDeviceName(pnpName);
                                if (deviceId == NullDeviceId)
                                    continue;
 
                                if (string.IsNullOrEmpty(deviceId))
                                {
                                    Console.WriteLine("SetDevicePorts:Missing DeviceId for {0}\\{1}", name, pnpName);
                                    continue;
                                }
 
                                if (!_devices.TryGetValue(deviceId, out deviceInfo))
                                {
                                    Console.WriteLine("SetDevicePorts: Missing DeviceInfo for device: {0} {1}\\{2}", deviceId, name, pnpName);
                                    continue;
                                }
 
                                if (!string.IsNullOrEmpty(deviceInfo.Port))
                                    continue;
 
                                if (!pnpKey.GetSubKeyNames().Contains("Device Parameters"))
                                {
                                    Console.WriteLine("SetDevicePorts:Missing Device Parameters for {0}\\{1}", name, pnpName);
                                    continue;
                                }
 
                                var portName = GetPortName(pnpKey);
                                if (string.IsNullOrEmpty(portName))
                                {
                                    Console.WriteLine("SetDevicePorts: Missing PortName for {0}", pnpName);
                                    continue;
                                }
                                deviceInfo.Port = portName;
                            }
                        }
                    }
                }
            }
        }
 
        public static string GetDeviceGuid(string value)
        {
            Verbose("GetDeviceGuid: {0}", value);
 
            var idx = value.IndexOf("}");
            if (idx > 0)
            {
                var id = value.Substring(0, idx);
                if (id.Length == 39)
                {
                    Verbose("GetDeviceGuid: returns {0}", id);
                    return id;
                }
            }
            return null;
        }
 
        private static string GetDeviceName(string value)
        {
            //Verbose("GetDeviceName: {0}", value);
 
            var parts1 = value.Split('_');
            if (parts1.Length <= 1)
            {
                Console.WriteLine("GetDeviceName: name parse error on {0}", value);
                return "";
            }
 
            var parts2 = parts1[0].Split('&');
            if (parts2.Length == 0)
            {
                Console.WriteLine("GetDeviceName: name parse error on {0}", value);
                return "";
            }
            var id = parts2[parts2.Length - 1];
 
            Verbose("GetDeviceName: returns {0}", id);
            return id;
        }
 
        private static string GetDeviceName2(string value)
        {
            //Verbose("GetDevicename2: {0}", value);
 
            var parts = value.Split('_');
            if (parts.Length <= 1)
            {
                Console.WriteLine("GetDeviceName2: name parse error on {0}", value);
                return "";
            }
            var deviceId = parts[1];
            if (string.IsNullOrEmpty(deviceId))
            {
                Console.WriteLine("GetDeviceName2: parse error on {0}", value);
                return "";
            }
            Verbose("GetDeviceName2: returns {0}", deviceId);
            return deviceId;
        }
 
        private static string GetPortName(RegistryKey key)
        {
            using (var paramsKey = OpenSubKey(key, "Device Parameters"))
            {
                if (paramsKey == null || !paramsKey.GetValueNames().Contains("PortName"))
                    return null;
 
                var portName = (string)paramsKey.GetValue("PortName");
                Verbose("GetPortName: {0}", portName);
                return portName;
            }
        }
 
        /// <summary>
        /// "AssocBdAddr"=hex:8f,68,c1,6c,6e,64,00,00 to 646E6CC1688F
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetDeviceToken(string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;
 
            var parts = value.ToUpper().Split('-');
            if (parts.Length < 6)
                return null;
 
            var result = string.Join("", parts.Take(6).Reverse());
            Verbose("GetDeviceToken: {0}", result);
 
            return result;
        }
 
        private static string GetValue(RegistryKey key, string name, string defaultValue = null)
        {
            if (!key.GetValueNames().Contains(name))
            {
                Console.WriteLine("Missing Value: {0}", name);
                return defaultValue;
            }
            var value = key.GetValue(name);
            var result = value as string;
            if (result != null)
            {
                Verbose("GetValue: {0} {1}", name, result);
                return result;
            }
            var bytes = value as byte[];
            if (bytes != null)
            {
                result = BitConverter.ToString(bytes);
                Verbose("GetValue: {0} {1}", name, result);
                return result;
            }
 
            return null;
        }
 
        private static RegistryKey OpenSubKey(RegistryKey key, string name)
        {
            if (key == null)
                return null;
 
            var parent = key.Name.Replace(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\""");
            Verbose("OpenSubKey: {0}\\{1}", parent, name);
 
            var result = key.OpenSubKey(name, false);
            if (result == null)
                Console.WriteLine("OpenSubKey: {0}\\{1}", parent, name);
            return result;
        }
 
    }
}
 
namespace BlueToothDevices
{
    class Program
    {
        static void Main(string[] args)
        {
            Bluetooth.VerboseMode = args.Length > 0 && args[0] == "true";
            foreach (var device in Bluetooth.GetBluetoothDevices())
            {
                Console.WriteLine("{0}", device);
            }
 
            Bluetooth.VerboseMode = false;
            foreach (var port in Bluetooth.DeviceNames)
            {
                Console.WriteLine("Device: {0}", port);
            }
        }
    }
}
 

Tuesday, June 10, 2014

WPF WaterMark Textbox to show PlaceHolder Text

This technique uses the Background property to show / hide placeholder textbox.
Advantage is that Placeholder is shown event when Textbox has the focus

How it works:
When textbox has a value, background set to White to cover up PlaceHolder text.
When textbox is empty, background becomes Transparent to show PlaceHolder text.

Here is basic example.  For my own purposes I turned this into a UserControl.

    <Grid>
        <Grid.Resources>
            <ux:NotEmptyConverter x:Key="NotEmptyConverter" />
            
            <Style TargetType="{x:Type Control}" x:Key="DefaultStyle">
                <Setter Property="FontSize" Value="20" />
                <Setter Property="Margin" Value="10"/>
                <Setter Property="VerticalAlignment" Value="Center"></Setter>
                <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
            </Style>
 
            <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}"></Style>
 
        </Grid.Resources>
 
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Text="Placeholder Text Is Here" Foreground="DarkGray" />
        <TextBox Grid.Row="0" Name="TextBoxEdit" 
                Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
            <TextBox.Style>
                <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource DefaultStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=FirstName.Length, FallbackValue=0, TargetNullValue=0}" Value="0">
                            <Setter Property="Background" Value="Transparent"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Path=FirstName, FallbackValue=0, TargetNullValue=0, Converter={StaticResource NotEmptyConverter}}" Value="false">
                            <Setter Property="Background" Value="White"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>
    </Grid>

// Corresponding value converter to detect non-empty string

    public class NotEmptyConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var s = value as string;
            return string.IsNullOrEmpty(s);
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }

Saturday, February 22, 2014

clink hide command line output at startup

reg query "hklm\software\wow6432node\microsoft\command processor" /v autorun

HKEY_LOCAL_MACHINE\software\wow6432node\microsoft\command processor
    autorun    REG_SZ    "C:\Program Files (x86)\clink\0.4\clink" inject --profile "~\clink" > nul


reg query "hklm\software\microsoft\command processor" /v autorun

HKEY_LOCAL_MACHINE\software\microsoft\command processor
    autorun    REG_SZ    "C:\Program Files (x86)\clink\0.4\clink" inject --profile "~\clink" > nul

Saturday, February 15, 2014

Disable Synaptics TouchPad corner taps - that maximize / restore window positions


Requires registry, since it is not exposed in UI settings control panel app.

This is for Toshiba Satellite P50-A.  Other Synaptic versions can be altered as needed.

Idea is to find all the CornerAction and CornerFlag registry entries under the Synaptics registry key and to turn them off, set to 0 for example.

Steps:

  1. Backup current settings by exporting HKEY_CURRENT_USER\Software\Synaptics contents to file
  2. Save following registry content to file: Synaptics.TurnOffCornerTaps.reg
  3. Import Synaptics.TurnOffCornerTaps.reg
  4. Restart machine


Synaptics.TurnOffCornerTaps.reg
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Synaptics\SynTP\TouchPadPS2_3]
"TopLeftCornerAction"=dword:00000000
"TopRightCornerAction"=dword:00000000
"BottomRightCornerAction"=dword:00000000
"TopLeftCornerFlag"=dword:00000000
"TopRightCornerFlag"=dword:00000000
"BottomLeftCornerFlag"=dword:00000000
"BottomRightCornerFlag"=dword:00000000

Monday, September 23, 2013

Knockout ES5 track nested objects - (JavaScript version)


See updated version on github: https://github.com/CarlosOnline/knockout.es5.mapping

/* Knockout ES5 track nested objects: JavaScript version - compiled via TypeScript.  Original: http://carlosonlineprogramming.blogspot.com/2013/09/knockout-es5-track-nested-objects.html

Solves the problem of ko.track not traversing into nested objects.
  • Calls ko.es5.track on nested objects: allowing tracking of strings, numbers, & arrays.
  • Use ko.es5.computed(function() ...) to mark computed functions.  ko.es5.track will call ko.defineProperty on these marked functions, after calling ko.track on the primitive members.
  • Excludes nested functions/objects with named constructors.  Idea is that TypeScript classes with constructors can call ko.es5.track themselves.  This exclusion can be removed.
  • Does not traverse into nested classes/objects with named constructors will not be traversed.  These constructors should call ko.es5.track themselves.
*/

var koES5;
(function (koES5) {
    function getType(x) {
        if ((x) && (typeof (x) === "object")) {
            if (x.constructor === Date)
                return "date";
            if (x.constructor === Array)
                return "array";
        }
        return typeof x;
    }
 
    var Track = (function () {
        function Track(rootObject) {
            this.rootObject = rootObject;
            this.mapped = [];
            this.track(rootObject);
            this.clearAllMapped();
        }
        Track.prototype.track = function (source, name) {
            if (typeof name === "undefined") { name = null; }
            var _this = this;
            if (source == null || this.isMapped(source))
                return;
            if (name == null)
                name = this.name(source);
 
            var keys = [];
            var computed = [];
            this.setMapped(source);
 
            for (var key in source) {
                var value = source[key];
                var type = getType(value);
 
                switch (type) {
                    case "array":
                    case "string":
                    case "number":
                        //console.log(name + "." + key, type);
                        keys.push(key);
                        break;
 
                    case "function":
                        if (this.isComputed(value)) {
                            //console.log("f> " + name + "." + key, type);
                            computed.push({
                                name: key,
                                fn: value
                            });
                        }
                        break;
 
                    case "object":
                        if (value == null || this.isMapped(value) || !this.isTrackable(value) || !this.isTrackableField(key))
                            continue;
 
                        //console.log("o> " + name + "." + key, type);
                        this.track(value, key);
                        break;
                }
            }
 
            if (keys.length > 0) {
                ko.track(source, keys);
            }
 
            if (computed.length > 0) {
                computed.forEach(function (item) {
                    _this.makeComputed(source, item.name, item.fn);
                });
            }
        };
 
        Track.prototype.name = function (value) {
            var xtor = value.__proto__.constructor;
            return xtor !== undefined && xtor.name != undefined ? xtor.name : "";
        };
 
        Track.prototype.isTrackable = function (value) {
            var xtor = value.__proto__.constructor;
            return xtor.name === "Object";
        };
 
        Track.prototype.isTrackableField = function (key) {
            return key != "__ko_mapping__";
        };
 
        Track.prototype.isMapped = function (value) {
            return (value.__tracked__ === true);
        };
 
        Track.prototype.setMapped = function (value) {
            if (this.isMapped(value))
                return;
            value.__tracked__ = true;
            this.mapped.push(value);
        };
 
        Track.prototype.clearAllMapped = function () {
            this.mapped.forEach(function (value) {
                delete value["__tracked__"];
            });
            this.mapped.unshift();
        };
 
        Track.prototype.isComputed = function (fn) {
            return (fn["__ko_es5_computed__"] === true);
        };
 
        Track.prototype.makeComputed = function (container, name, fn) {
            var nameOverride = fn["__ko_es5_computed_name__"];
            if (nameOverride !== undefined && nameOverride !== "") {
                name = nameOverride;
                delete fn["__ko_es5_computed_name__"];
            }
 
            if (name === undefined || name == "") {
                console.log("Error. Function missing name", fn);
                return;
            }
            ko.defineProperty(container, name, fn);
            delete fn["__ko_es5_computed__"];
        };
        return Track;
    })();
    koES5.Track = Track;
 
    function track(root) {
        new Track(root);
    }
    koES5.track = track;
 
    function computed(fn, name) {
        if (typeof name === "undefined") { name = null; }
        fn["__ko_es5_computed__"] = true;
        if (name || false) {
            fn["__ko_es5_computed_name__"] = true;
        }
        return fn;
    }
    koES5.computed = computed;
 
    ko.es5 = {
        computed: koES5.computed,
        track: koES5.track
    };
})(koES5 || (koES5 = {}));