All posts tagged windows

That’s what’s up. We are going to stop windows from stealing focus with C#. No frills, no crap, no more of this nonsense. It’s time to stop.

Q: Why does Windows do this and why isn’t there a setting to fix this?
A: Remember Fred Johnson who lived down the street in the 70’s? You know… that slightly overweight jerk that bullied the smart kids? Well, one day on the way home from school he kicked Billy Gates dog and said something off color about his “parentage”. So, now we need to suffer for Fred’s sins. No need to Wiki this, it’s a true story.

 
Joking Aside

So, back in the early 2000’s we had the Windows XP PowerToy called TweakUI which allowed us to control how initialized windows interacted with the desktop environment. This involved changing the following registry key:

HKEY_CURRENT_USER\Control Panel\Desktop\Foreground\LockTimeout

In the modern versions of Windows, this override no longer works. For a long while, this left us SOL and drifting in space looking for the answer… that is until now.

 
The Solution

In my opinion, the C# community was right in thinking the best approach is to get on the P/Invoke track.

For this solution the key is LockSetForegroundWindow on a timer. For this solution we will use the following methods:


// Lock
public static uint LSFW_LOCK = 1;
public static uint LSFW_UNLOCK = 2;

/// <summary>
/// Locks the set foreground window.
/// </summary>
/// <param name="uLockCode">The u lock code.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool LockSetForegroundWindow(uint uLockCode);

/// <summary>
/// Gets the foreground window.
/// </summary>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

 
Now that we have identified the P/Invoke piece, let’s look at how we can implement this effectively with said timer in a WPF application. To do this we are going to create a controller to facilitate all our focus needs:


using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;

using Win32Windows = Xcalibur.Win32.Win32ApiHelper.Windows;

namespace Xcalibur.DontInterruptMe
{
    /// <summary>
    /// Window focus controller.
    /// </summary>
    public class FocusController
    {
        #region Members
        
        private readonly DispatcherTimer _timer;
        private IntPtr _currentHandle;
        private bool _isRunning;
        private bool _isStarted;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="FocusController"/> class.
        /// </summary>
        public FocusController()
        {
            this._isRunning = false;
            this._isStarted = false;

            // Set timer
            this._timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
            this._timer.Tick += (s, e) => this.EvaluateAsync(); 

            // Start
            this.Start();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            if (this._isStarted) return;

            // Start timer
            this._timer.IsEnabled = true;
            this._timer.Start();
            this._isStarted = true;
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            if (!this._isStarted) return;

            // Stop timer
            this._timer.Stop();
            this._timer.IsEnabled = false;
            this._isStarted = false;

            // Unlock set foreground window
            LockSetForegroundWindow(LSFW_UNLOCK);
        }

        /// <summary>
        /// Evaluates this instance.
        /// </summary>
        private void Evaluate()
        {
            if (_isRunning) return;

            // Set as "running"
            _isRunning = true;

            // Get current
            var activeWindowHandle = GetForegroundWindow();
            if (_currentHandle == activeWindowHandle)
            {
                _isRunning = false;
                return;
            }

            // Store current handle
            _currentHandle = activeWindowHandle;

            // Handle cannot be 0
            if (activeWindowHandle == IntPtr.Zero)
            {
                _isRunning = false;
                return;
            }

            // Get related process
            var processes = Process.GetProcesses();
            var currentProcess = processes.FirstOrDefault(x => x.MainWindowHandle == _currentHandle);

            // currentProcess must exist, and the MainWindowTitle must be valid.
            if (currentProcess == null || string.IsNullOrEmpty(currentProcess.MainWindowTitle))
            {
                _isRunning = false;
                return;
            }

            // Lock set foreground window
            LockSetForegroundWindow(LSFW_LOCK);

            // Set as "not running"
            _isRunning = false;
        }

        /// <summary>
        /// Evaluates the asynchronous.
        /// </summary>
        /// <returns></returns>
        private async Task EvaluateAsync()
        {
            await Task.Run(() => this.Evaluate());
        }

        #endregion

        #region P/Invoke 

        // Lock
        public static uint LSFW_LOCK = 1;
        public static uint LSFW_UNLOCK = 2;

        /// <summary>
        /// Locks the set foreground window.
        /// </summary>
        /// <param name="uLockCode">The u lock code.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool LockSetForegroundWindow(uint uLockCode);

        /// <summary>
        /// Gets the foreground window.
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        #endregion
    }
}

 
The last piece is the implementation of the FocusController from App.xaml:


using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Hardcodet.Wpf.TaskbarNotification;

using Microsoft.Win32;
using Application = System.Windows.Application;

namespace Xcalibur.DontInterruptMe
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region Members

        private static Mutex _instanceMutex;
        private FocusController _focusController;

        #endregion

        #region Methods

        /// <summary>
        /// Checks if application is already running.
        /// </summary>
        /// <returns></returns>
        private static bool StartInstance()
        {
            // Set mutex
            _instanceMutex = new Mutex(true, Constants.ApplicationKey);

            // Check if already running
            bool isAlreadyInUse = false;
            try
            {
                isAlreadyInUse = !_instanceMutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                KillInstance();
                isAlreadyInUse = false;
            }
            catch (Exception)
            {
                _instanceMutex.Close();
                isAlreadyInUse = false;
            }
            return isAlreadyInUse;
        }

        /// <summary>
        /// Kills the instance.
        /// </summary>
        /// <param name="code">The code.</param>
        private static void KillInstance(int code = 0)
        {
            if (_instanceMutex == null) return;

            // Owning application should release mutex
            if (code == 0)
            {
                try
                {
                    _instanceMutex.ReleaseMutex();
                }
                catch (Exception) { }
            }
            _instanceMutex.Close();
        }

        #endregion

        #region Events

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Application.Startup" /> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the 
        /// event data.</param>
        protected override void OnStartup(StartupEventArgs e)
        {
            // Check if running
            if (StartInstance())
            {
                // Already running, Exit
                Current.Shutdown(1);
            }

            // Invoke focus controller
            this._focusController = new FocusController();

            // Base
            base.OnStartup(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the 
        /// event data.</param>
        protected override void OnExit(ExitEventArgs e)
        {
            if (this._focusController != null)
            {
                // Gracefully exit
                this._focusController.Stop();
            }

            // Kill instance
            KillInstance(e.ApplicationExitCode);

            // Base
            base.OnExit(e);
        }

        #endregion
    }
}

 
You will notice I added the RunOnce Mutex Solution from our last post Restricting WPF Applications to run only once with a Mutex to avoid any potential issues with multiple instances.

 
Future Changes

In the next version of this application I will be tossing Process.GetProcesses() from the Evaluate method for a much faster P/Invoke solution I am planning to use in the next version of Astronomy.

If you want the complete, free product, download Don’t Interrupt Me! now.

Otherwise my friends, Happy Coding!

Getting the Windows Key

Categories: .Net, C#, Console, WPF
Comments: No

In my last post I went over how to retrieve the Windows Product Activation information from Windows Vista / 7.0 / 2008. Today, we are going to do something on the same thread but very much more desirable. And that is retrieving the Windows Key information.

For as long as Windows has had a key, admins have been looking to be able to retrieve the Windows key information on the fly: especially when they need to do a re-install and lost their original media casing.

So, let’s get right to it with a console application.


static void Main(string[] args)
{
    string computername = Environment.MachineName;
    const RegistryHive registryRoot = RegistryHive.LocalMachine;
    const string sSubKeyName = @"SOFTWAREMicrosoftWindows NTCurrentVersion";
    const string sValueName = "DigitalProductId";
    string windowsKey = "";

    // Get product Id
    RegistryKey regoutput = RegistryKey.OpenRemoteBaseKey(
        registryRoot, computername, RegistryView.Default).
        OpenSubKey(sSubKeyName);

    // Convert to byte array
    byte[] digitalProductId;
    if (regoutput != null)
    {
        digitalProductId = regoutput.GetValue(sValueName) as byte[];

        // Get Windows Product Key
        windowsKey = (digitalProductId != null)
            ? DecodeProductKey(digitalProductId)
            : "The product key was not accessible";
    }

    else
    {
        windowsKey = "The product key was not accessible";
    }

    // Output 
    Console.WriteLine(windowsKey);
    Console.Read();
}

In this example, I am choosing my local machine as the target. The location of the value we want is located in the registry here:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionDigitalProductId.

We then get the byte array representing the key and decode it with the below method.


/// <summary>
/// Decodes a Microsoft product key based on the provided digital product id.
/// </summary>
/// <param name="digitalProductId">The digital product id.</param>
/// <returns></returns>
private static string DecodeProductKey(byte[] digitalProductId)
{
    // Offset of first byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 34H.
    const int keyStartIndex = 52;

    // Offset of last byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 43H.
    const int keyEndIndex = keyStartIndex + 15;

    // Possible alpha-numeric characters in product key.
    var digits = new[]
            {
                'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R',
                'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9'
            };

    // Length of decoded product key
    const int decodeLength = 29;

    // Length of decoded product key in byte-form.
    // Each byte represents 2 chars.
    const int decodeStringLength = 15;

    // Array of containing the decoded product key.
    var decodedChars = new char[decodeLength];

    // Extract byte 52 to 67 inclusive.
    var hexPid = new ArrayList();
    for (int i = keyStartIndex; i <= keyEndIndex; i++)
    {
        hexPid.Add(digitalProductId[i]);
    }
    for (int i = decodeLength - 1; i >= 0; i--)
    {
        // Every sixth char is a separator.
        if ((i + 1) % 6 == 0)
        {
            decodedChars[i] = '-';
        }
        else
        {
            // Do the actual decoding.
            int digitMapIndex = 0;
            for (int j = decodeStringLength - 1; j >= 0; j--)
            {
                int byteValue = (digitMapIndex << 8) | (byte)hexPid[j];
                hexPid[j] = (byte)(byteValue / 24);
                digitMapIndex = byteValue % 24;
                decodedChars[i] = digits[digitMapIndex];
            }
        }
    }
    return new string(decodedChars);
}

Now, if you are running 64-bit Windows (and most of us are), you may be running into problems getting your byte array. Why is that? Well, the short answer is that it has to do with your Configuration Manager settings. The default platform target is x86: which does not have access to several 64-bit spaces including certain registry values.

You can change that by doing the following in Visual Studio:

  1. Open your Project Properties.
  2. Go to the Build tab.
  3. For each Configuration set the Platorm target to Any CPU.
  4. For Visual Studio 2012, make sure to uncheck Prefer 32-bit.

That’s all there is to it.

Until next time.

In today’s blog I am going to give you the method I use to obtain the Windows Product Activation information in Astronomy.

Q: So, where does this information come from?
A: Well, as you may know, you can obtain this information from the following script:
C:\Windows\System32\slmgr.vbs

I always felt this needed to be modified for use in .Net, so I refactored this a bit and created my own replacement method:


/// <summary>
/// Retrieves licensing information.
/// </summary>
/// <param name="managementScope">The management scope.</param>
/// <returns></returns>
public static string GetLicensingInfo(ManagementScope managementScope)
{
	// Output
	string result = "Information Unavailable";
	const string windowsAppId = "55c92734-d682-4d71-983e-d6ec3f16059f";

	// Default values
	const uint HR_SL_E_GRACE_TIME_EXPIRED = 0xC004F009;
	const uint HR_SL_E_NOT_GENUINE = 0xC004F200;

	// Set WMI query
	var query =
		new WqlObjectQuery(
			"SELECT LicenseStatus, GracePeriodRemaining " +
			"FROM SoftwareLicensingProduct " +
			"WHERE (PartialProductKey <> NULL) AND " +
			"(ApplicationId='" + windowsAppId + "')");

	// Get items
	using (var searcher = new ManagementObjectSearcher(managementScope, query))
	
	// Get data
	using (var colItems = searcher.Get())
	{
		foreach (ManagementBaseObject obj in colItems)
		{
			string subline;

			// License Status
			int licenseStatus = Convert.ToInt32(obj.GetPropertyValue("LicenseStatus"));

			// Time remaining: minutes / days
			int gpMin = Convert.ToInt32(obj.GetPropertyValue("GracePeriodRemaining"));
			int gpDay = gpMin / (24 * 60);

			// Evaluate
			switch (licenseStatus)
			{
				case 0:
					result = "Unlicensed";
					break;

				case 1:
					result = "Licensed";
					if (gpMin > 0)
					{
						subline =
							String.Format(
								"Activation expiration: " +
								"{0} minute(s) ({1} day(s))", gpMin, gpDay);
						result += "\n" + subline;
					}
					break;

				case 2:
					result = "Initial grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 3:
					result =
						"Additional grace period (KMS license expired or " +
						"hardware out of tolerance)";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 4:
					result = "Non-genuine grace period.";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 5:
					result = "Notification";

					uint licenseStatusReason;
					uint.TryParse(obj.GetPropertyValue("GracePeriodRemaining").ToString(), 
						out licenseStatusReason);
						
					// Evaluate
					switch (licenseStatusReason)
					{
						case HR_SL_E_NOT_GENUINE:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(non-genuine).", licenseStatusReason);
							break;

						case HR_SL_E_GRACE_TIME_EXPIRED:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(grace time expired).", licenseStatusReason);
							break;

						default:
							subline =
								String.Format("Notification Reason: 0x{0}.",
												licenseStatusReason);
							break;
					}
					result += "\n" + subline;
					break;

				case 6:
					result = "Extended grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				default:
					result = "Unknown";
					break;
			}
		}
	}
	
	// return
	return result;
}

An overboard implementation:


/// <summary>
/// Connects to a target computer through Kerberos authentication.
/// </summary>
/// <param name="computername">The computername.</param>
/// <param name="user">The user.</param>
/// <param name="securePass">The secure pass.</param>
/// <returns>
/// A ManagementScope context for the current connection.
/// </returns>
public static ManagementScope Connect(string computername, string user, 
	SecureString securePass)
{
	// Build an options object for the remote connection
	var options = new ConnectionOptions
		{
			Impersonation = ImpersonationLevel.Impersonate,
			EnablePrivileges = true
		};

	// Set properties
	// Check name
	if (String.IsNullOrEmpty(computername))
	{
		computername = ".";
	}

	// Check credentials
	// Cannot pass a blank user and password.
	if (!String.IsNullOrEmpty(user))
	{
		options.Username = user;
		options.SecurePassword = securePass;
	}
	
	// Make a connection to a remote computer.
	var managementScope =
		new ManagementScope($@"\{computername}\root\cimv2", options);

	try
	{
		// Connect
		managementScope.Connect();
	}
	catch (Exception ex)
	{
		throw ex;
	}
	
	// return
	return managementScope;
}

/// <summary>
/// Gets the license you so desperately want.
/// </summary>
public void GetMyLicense()
{
	// Get the management scope
	var myManagementScope = Connect(".", "XCALIBUR\jarzt", {my secure password});

	// Get the licensing
	string licensing = GetLicensingInfo(myManagementScope);

	// Report to client
	MessageBox.Show("My current licensing status: " + licensing);
}

So, really that’s all there is to it.

Happy coding!