Monday, 24 December 2012

When is a bool not a bool?

The answer: it never was in any modern computer. It's just an integer that holds one of two special values. A computer is incapable of storing a single bit by itself. All modern programming languages store a boolean value as one or more bytes behind the scenes, even though it would theoretically fit into one bit. But enough theory. Let's examine the implementation details of a bool in C#.

First of all, lets figure out how to read and write the internal integer value of a bool. There are at least two ways to do so. The safe way looks like this:

[StructLayout(LayoutKind.Explicit)]
struct BoolIntUnion
{
    [FieldOffset(0)]
    public bool Bool;
    [FieldOffset(0)]
    public int Int;
}

If you're wondering what that is, it's the C# version of the C/C++ union. Both the bool and the int are in the same memory location, and thus share the same value. What this means is that changes to one variable are reflected in the other. What it means to us, right now, is that we can change the internal value of the bool at will. Allow me to demonstrate.

BoolIntUnion u = new BoolIntUnion();
u.Bool = true;
Console.WriteLine(u.Int);

Can you guess what this prints? It's not too hard. It simply outputs 1. So, this means that the internal value of true is actually 1. Similarly, false turns out to be 0. (By the way, bool in C# is stored as 4 bytes -- at least in x86, which is why I paired it with an int.)

I did mention that there is another way to convert between bool and int. This uses unsafe code.

bool b = true;
bool *pB = &b;
int *pI = (int*)pB;
int i = *pI;

From now on, I'm not going to write out this conversion code every time. Instead, I'll use these utility methods:

static unsafe int Int(bool value)
{
    return *(int*)(&value);
}

static unsafe bool Bool(int value)
{
    return *(bool*)(&value);
}

Now, lets start our investigation. If true is 1 and false is 0, what about the other 4294967294 possible values? Are they true, false, or neither? As it turns out, none of those options fully describe the behavior of bool.

First, what happens if we convert an abnormal bool value to a string? Lets see.

Console.WriteLine(Bool(42));

Did you expect "True", "False", empty string, or maybe even "FileNotFound"? Well, the answer is "True". Even though the internal value is 42 instead of 1, it still results in "True".

Now let's try it as a condition in an if.

if (Bool(42))
{
    Console.WriteLine("if");
}
else
{
    Console.WriteLine("else");
}

Not surprisingly, this prints "if".

So far, it seems to be acting just as if it were the value true itself. Let's try comparing it directly to true.

Console.WriteLine(Bool(42) == true);

And we get... "True". So, any non-zero integer value stored in a bool is identical to true. That was easy. End of story.

Nope.

Your compiler lied to you.

Even if you compiled with optimizations disabled, your compiler performed a small optimization that happened to give us the wrong result.

Have a look at the generated IL for Main, pulled out of ILSpy (sorry, no syntax highlighting):

.method private hidebysig static 
 void Main (
  string[] args
 ) cil managed 
{
 // Method begins at RVA 0x2050
 // Code size 15 (0xf)
 .maxstack 8
 .entrypoint

 IL_0000: nop
 IL_0001: ldc.i4.s 42
 IL_0003: call bool booltest.Program::Bool(int32)
 IL_0008: call void [mscorlib]System.Console::WriteLine(bool)
 IL_000d: nop
 IL_000e: ret
} // end of method Program::Main

Wait a second. Isn't there supposed to be a comparison between the two calls? There is, but the compiler removed it, as it considers == true to be a no-op.

What we can do to fix this? We can avoid hard-coding the value, and instead calculate it at runtime. For example:

Console.WriteLine(Bool(42) == bool.Parse(bool.TrueString));

Now it prints "False". Obviously the compiler wasn't expecting what we did, so we got the wrong result initially.

We can see now that not all non-zero values are identical. In fact, internally, bools are treated just like ints. As in C/C++, a non-zero integer is a 'truthy' value when used as a boolean. In C#, however, the language goes to great lengths to ensure a bool can only contain 0 or 1, and that an int can never be treated as a bool. As you have seen, there are ways to circumvent this, and when we do, we end up with behavior similar to C/C++.

I hope you learned something by reading this, as I certainly didn't know all this until recently. In reality, bool is just an int and some syntactic sugar. This might be completely expected by C++ programmers, but for people that have only used C#, it may come as a surprise. At the very least, I hope everyone who read this learned something they didn't know before.

Class dismissed.

Thursday, 3 May 2012

Understanding the WPF Layout System

Many people don't understand how the WPF layout system works, or how that knowledge can help them in their projects. I intend to shine a little light on the mechanics behind all those cool layout controls in WPF.

To explain the system, I will give a step-by-step example of creating a custom panel for WPF. This panel will be similar to the StackPanel, with the difference that it will expand the last child to fill the remaining space.

Step 1: Create a new project

Create a new WPF Application project called WpfLayoutSystem.

Step 2: Add the custom panel

Add a new class to the project. Call it ExpandingStackPanel.cs.

To create your own custom panel, create a class that inherits from Panel, and override MeasureOverride and ArrangeOverride. That's it. This is the most basic panel, which doesn't add any functionality, yet.

using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfLayoutSystem
{
    class ExpandingStackPanel : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            return base.MeasureOverride(availableSize);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            return base.ArrangeOverride(finalSize);
        }
    }
}

Step 3: Measure

A layout pass is made up of two steps. Measuring is the first. In the measure step, each control determines how much space it needs. Panels do this by measuring the child controls, and then basing it's desired size on the child controls. Content controls base their size on their content. Each panel tells its children how much space is available, and each child tells its parent how much space it wants.

Lets examine how this works by writing an example. Here is the code for MeasureOverride.

protected override Size MeasureOverride(Size availableSize)
{
    double sumX = 0.0;
    double maxY = 0.0;
    foreach (UIElement child in this.Children)
    {
        child.Measure(new Size(Math.Max(availableSize.Width - sumX, 0.0), availableSize.Height));
        sumX += child.DesiredSize.Width;
        maxY = Math.Max(maxY, child.DesiredSize.Height);
    }
    return new Size(sumX, maxY);
}

Step 4: Arrange

The second step in the layout pass is arranging. In the arrange step, each control arranges its content or children based on the available space. Each panel tells its children how much space they have been given. Each child tells the parent how much space it actually used. You may ask why the measure and arrange passes aren't combined. Often a control is given more or less space than it asked for in the measure pass, and then it will arrange itself differently.

To see how this works, let's write the ArrangeOverride method.

protected override Size ArrangeOverride(Size finalSize)
{
    double x = 0.0;
    for (int i = 0; i < this.Children.Count - 1; i++)
    {
        UIElement child = this.Children[i];
        child.Arrange(new Rect(x, 0.0, child.DesiredSize.Width, child.DesiredSize.Height));
        x += child.DesiredSize.Width;
    }
    if (this.Children.Count > 0)
    {
        UIElement lastChild = this.Children[this.Children.Count - 1];
        lastChild.Arrange(new Rect(x, 0.0, Math.Max(finalSize.Width - x, 0.0), lastChild.DesiredSize.Height));
    }
    return finalSize;
}

Step 5: Using the panel

I used this code to test the panel. Feel free to use it with other code, but I can't claim that it will work in all scenarios. It was just designed as an example. Anyway, here is MainWindow.xaml.

<Window x:Class="WpfLayoutSystem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfLayoutSystem"
        Title="MainWindow" Height="350" Width="525">
    <local:ExpandingStackPanel>
        <Button>A Button</Button>
        <Ellipse Width="80" Height="50" Stroke="Black" />
        <TextBlock>Some</TextBlock>
        <RadioButton>Other</RadioButton>
        <TextBox>Controls</TextBox>
        <Button>Another Button</Button>
    </local:ExpandingStackPanel>
</Window>


And here it is in action.



For your convenience, here is the entire ExpandingStackPanel class.

using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfLayoutSystem
{
    class ExpandingStackPanel : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            double sumX = 0.0;
            double maxY = 0.0;
            foreach (UIElement child in this.Children)
            {
                child.Measure(new Size(Math.Max(availableSize.Width - sumX, 0.0), availableSize.Height));
                sumX += child.DesiredSize.Width;
                maxY = Math.Max(maxY, child.DesiredSize.Height);
            }
            return new Size(sumX, maxY);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double x = 0.0;
            for (int i = 0; i < this.Children.Count - 1; i++)
            {
                UIElement child = this.Children[i];
                child.Arrange(new Rect(x, 0.0, child.DesiredSize.Width, child.DesiredSize.Height));
                x += child.DesiredSize.Width;
            }
            if (this.Children.Count > 0)
            {
                UIElement lastChild = this.Children[this.Children.Count - 1];
                lastChild.Arrange(new Rect(x, 0.0, Math.Max(finalSize.Width - x, 0.0), lastChild.DesiredSize.Height));
            }
            return finalSize;
        }
    }
}


Hopefully that gave you an idea of how the layout system works in WPF.

Until next time!

Tuesday, 20 December 2011

Introduction to Windows 8 Developer Preview: Review

In this post I want to go over some of the features in Windows 8 and compare it to previous versions of Windows.

The most obvious thing is the new GUI. Let's start with the Start menu, since this is used much more than in previous versions. The Start menu has changed drastically, mostly in looks. The Start menu is the first thing you see when you log in. It is a full screen menu with a list of apps. When you click the Start button on the desktop, the desktop slides away to reveal the Start menu. This is fundamentally different than the old Start menu, which appeared on top of the desktop and used only one corner of the screen. There are two kinds of apps on the Start menu: ordinary applications and Metro style apps. Ordinary applications open on the desktop and behave just as Windows applications have always behaved. Metro style apps are very similar to Windows Phone apps. They appear full-screen, and are independent of the desktop. In fact, the desktop acts very much like a Metro app itself, as it can be 'snapped', hidden, and otherwise treated the same way. The Metro interface has a new look for its widgets, with very square-looking controls and the absence of gradients and 3D effects. The desktop looks quite similar to Windows Vista and Windows 7. The widgets look the same, but the windows have gotten a facelift. Instead of the rounded corners for the borders that the original Windows Aero displayed, the corners are now square. The title text has a larger font and is centered in the title bar. The Windows Aero 'stripes' on the title bar are gone. There are a few changes to the appearance, but the Windows Aero experience is still there. That should give you a bit of an idea of the new look in Windows 8.

There were some drastic changes in the functionality of standard Windows features. The Task Manager is completely redone. The default view is a simple list of open apps, similar to the default page in the old Task Manager. However, by clicking the More details button, you can access more information in multiple tabs, which is slightly more powerful than the classic Task Manager. The default tab is the Processes tab. This is not the same as the Processes tab in the original Task Manager. It has been renamed to Details. The new Processes tab has more information, including color-coded resource usage stats: the processes that are using the most CPU/RAM/IO are highlighted. The Task Manager as a whole manages to present the same amount of information, but in an easier-to-digest format. The Start menu has hidden some features deemed less useful, such as the Shut Down button. The search box which was so useful in Windows Vista/7 seems to have disappeared too, but it is just camouflaged. If you start typing while in the Start menu, it automatically opens up a search that works the same as you are used to. The Start menu has also hidden the usual links to Computer, Documents, Pictures, etc. Some other Windows fixtures, such as everybody's favourite Blue Screen of Death, have changed. The BSOD is still blue, but instead of a glaring blue, it is more of a sky blue. Instead of a pixelated text-mode font, it uses an anti-aliased font in multiple sizes. The first thing you will see is a giant :( emoticon, and below that there is an extremely short description of the error.

Here is a list of a few of the things I liked or disliked about Windows Developer Preview.

Pros:

  • The GUI is very attractive and easy to use.
  • The new Metro style apps are simple, useful, and very cool.
Cons:
  • Some stuff is hard to find, especially on the first day.
  • Apps can't be closed, just suspended, presumably increasing memory usage.
  • For people on desktop PC's, the fact that most apps are designed for touch input is a little unfamiliar.
Here is the grade that I would give Windows 8: 4/5 stars.

I think this will be a very successful project for Microsoft. It is a very modern operating system that takes advantage of the move toward tablet PC's. It has a few features that make it hard to get used to, but with a little time it becomes second nature (a lot like the drag-up-to-maximize feature of Windows Aero, that always gets me when I use XP). This is the start of a new era for Windows.

In the next post, I want to get into some programming for Windows 8, especially the Metro style interface and the Windows Runtime (WinRT).

Saturday, 10 December 2011

Introduction to Windows 8 Developer Preview: Setup

I have been investigating Windows Developer Preview recently, and I thought it fit to share some of my experience. This post is mostly how to get Windows 8 set up, but in the near future I want to post a quick review, and get into programming in Windows 8.

First, lets start with some info on getting your own Windows Developer Preview setup running. The first step is to download it from the Windows Dev Center. As of this writing, there are three downloads that include Windows Developer Preview. There are two downloads, for x86 and x64, as well as one for x64 that includes the developer tools. Hopefully there will soon be a download for the x86 folks who want to use the developer tools. The download you probably want is the first one, 'Windows 8 Developer Preview with developer tools English, 64-bit (x64)'. It is a DVD image of the installation disk for Windows Developer Preview.

Once you have the file downloaded, there are two options. If you have a spare computer that you can wipe and install Windows, I recommend that option. But if you are like me and don't have one, Windows 8 works on a virtual machine. I will explain how to get it set up for those that haven't used one before.

I used VMware Player to run Windows 8. For anyone who wants to use Microsoft Virtual PC, don't. Virtual PC doesn't support x64 operating systems, which is a major barrier in this case. VMware player also has a toolset that installs on the guest OS, and can greatly improve the user experience.

Before you set up the VM, you need to think about how to share your computer's resources between the host and the guest. I recommend at least 4GB of RAM on the host, but you may be able to squeak by with 2GB. I have 4GB, and I ran Windows 8 with 1.5GB of RAM, and it worked fine. I had tried it with 2GB, but that caused my host (Windows 7 for the curious) to almost freeze sometimes. I recommend giving Windows 8 at least 1GB. Disk space shouldn't be an issue. VMware recommends 60GB for Windows 7 x64 (the closest option to Windows 8).

Setting up the VM is easy. in VMware player, click 'Create a New Virtual Machine'. In the first dialog, select 'I will install the operating system later.' In the next dialog, select Windows 7 x64 as the guest OS. The rest is self explanatory. On the last dialog, click 'Customize Hardware...'. There you can set the available memory, processors, drives, etc. Set everything as you would like, and also 'insert' the Windows installation disk. In the CD/DVD settings, select 'Use ISO image file' and enter the path to the ISO file that you downloaded.

Once you have everything tuned to your preference, close the hardware options and click Finish. You are all ready to run Windows 8. Click 'Play virtual machine' and when the machine starts, follow the instructions to set up Windows. Once you are set up, you should see a green screen with a list of apps.

The first thing you want to do if you are using VMware Player is to install the VMware tools. Open the desktop and in the VMware menu (not within Windows 8) select Virtual Machine > Install VMware Tools... This 'inserts' a CD with the VMware tools into the virtual machine. Install the tools from the CD and reboot the VM. Then you are pretty well set up completely.

Feel free to to explore the new Windows. A helpful tip: to shut down the computer, move the mouse to the bottom left corner of the screen and select Settings. This opens a menu that includes power options. I couldn't figure out how to do it myself, so I figured you might not either.

Look for a short review of Windows 8 features soon!

Thursday, 15 September 2011

Communicating Between VSTO and UDF's in Excel

This post will explain how to communicate between a VSTO add-in for Excel, and a managed automation add-in containing a user-defined function (UDF).

The situation is this: you need a UDF to add functionality to Excel. You think it would be nice to add some buttons to the Excel Ribbon, to help with some state management. You would probably create a VSTO add-in to store some program state, and you would create a class for the UDF in the same project. From the UDF you would access that program state by accessing ThisAddIn through the Globals.ThisAddIn static property. But everything is not OK. Excel loads your add-in DLL through VSTO and your VSTO add-in works fine. But Excel loads your add-in DLL into a separate AppDomain for the UDF side of the add-in, which means that a nearly impassable AppDomain boundary exists between your VSTO add-in and your UDF. Here is how to properly communicate between the two sides.

1. Create a new 'Excel 2007 Add-in' project in Visual Studio. You will get a simple ThisAddIn class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;

namespace ExcelAddIn1
{
    public partial class ThisAddIn
    {
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion
    }
}

2. Create a class by which will be used for communication between the two sides. It should either contain all the data that you want to share, or a way to get anything you need to. In this example, it will be a simple integer.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExcelAddIn1
{
    public class MySharedClass
    {
        public int MyInt
        {
            get;
            set;
        }

        public MySharedClass()
        {
            MyInt = 0;
        }
    }
}

3. It's time to start getting some COM stuff in here, so set the project to register COM classes. In your project's properties, on the Build tab, check 'Register for COM interop'.

4. OK, back to your shared class. This class will be going through a lot of COM stuff, so let's make it COM-compatible. Note: wherever you see a GUID from here on, PLEASE replace it with a new one.

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ExcelAddIn1
{
    [Guid("B7E6AF59-5D70-420D-A66A-564B0FA4D443")]
    [ComVisible(true)]
    public interface IMySharedClass
    {
        int MyInt
        {
            get;
            set;
        }
    }

    [Guid("B078FCCA-6DDE-43BC-96B6-36CA81DA9305")]
    [ComVisible(true)]
    public class MySharedClass : IMySharedClass
    {
        public int MyInt
        {
            get;
            set;
        }

        public MySharedClass()
        {
            MyInt = 0;
        }
    }
}

5. Now let's initialize our shared class in ThisAddIn. We do this by overriding the RequestComAddInAutomationService method, to expose the class to other add-ins.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;

namespace ExcelAddIn1
{
    public partial class ThisAddIn
    {
        public MySharedClass mySharedClass = null;

        protected override object RequestComAddInAutomationService()
        {
            if (mySharedClass == null)
            {
                mySharedClass = new MySharedClass();
            }
            return mySharedClass;
        }

        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            Random rnd = new Random();
            if (mySharedClass == null)
            {
                mySharedClass = new MySharedClass();
            }
            mySharedClass.MyInt = rnd.Next(1, 11);
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// 
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// 
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion
    }
}

6. Add a reference to the 'Extensibility' DLL. This is where the IDTExtensibility2 interface is found.

7. Create a new class which will eventually expose your UDF. First we will retrieve the instance of the shared class so we can use it in the UDF.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Extensibility;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
    public class Functions : IDTExtensibility2
    {
        static IMySharedClass mySharedClass = null;

        public void OnAddInsUpdate(ref Array custom)
        {
        }

        public void OnBeginShutdown(ref Array custom)
        {
        }

        public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
        {
            Excel.Application app = Application as Excel.Application;
            mySharedClass = app.COMAddIns.Item("ExcelAddIn1").Object;
        }

        public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
        {
        }

        public void OnStartupComplete(ref Array custom)
        {
        }
    }
}

8. Program your UDF. This example will simply return MySharedClass.MyInt. You also have to enter some registry values to register mscoree.dll so that your UDF will work.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Extensibility;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace ExcelAddIn1
{
    [Guid("3B81B6B7-3AF9-454F-AADF-FAF06E5A98F2")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [ComVisible(true)]
    public interface IFunctions
    {
        int MYINT();
    }

    [Guid("F58C591D-A22F-49AD-BC21-A086097DC26B")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class Functions : IFunctions, IDTExtensibility2 // I think IFunctions must be the first in the list 
    {
        static IMySharedClass mySharedClass = null;

        public void OnAddInsUpdate(ref Array custom)
        {
        }

        public void OnBeginShutdown(ref Array custom)
        {
        }

        public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
        {
            Excel.Application app = Application as Excel.Application;
            mySharedClass = app.COMAddIns.Item("ExcelAddIn1").Object;
        }

        public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
        {
        }

        public void OnStartupComplete(ref Array custom)
        {
        }

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type type)
        {
            Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
            RegistryKey key = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true);
            key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll", RegistryValueKind.String);
        }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type type)
        {
            Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false);
        }

        private static string GetSubKeyName(Type type, string subKeyName)
        {
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            s.Append(@"CLSID\{");
            s.Append(type.GUID.ToString().ToUpper());
            s.Append(@"}\");
            s.Append(subKeyName);
            return s.ToString();
        }

        public int MYINT()
        {
            return mySharedClass.MyInt;
        }
    }
}

9. You are done! You can build and test it if you want.