Archive for the ‘Uncategorized’ Category

There’s an op for that

Friday, April 2nd, 2010

Just found this extension method in my team’s code:

public static int ConvertNull(this int? value, int defaultValue)
{
    if (value == null)
        return defaultValue;
    return ((int)value);
}

Um, what’s wrong with the null coalescing operator?

Homework assignment: Fix the Socket API

Friday, September 25th, 2009

Your task is to fix the .NET Socket API. This is as unhelpful as an API gets. There are several rules that a user of a Socket must follow. Create an API that proves that they are following all of the rules.

Here is the relevant part of the existing unhelpful API:

public class Socket : IDisposable
{
    public Socket(SocketInformation socketInformation);
    public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);

    public Socket Accept();
    public void Bind(EndPoint localEP);
    public void Connect(EndPoint remoteEP);
    public void Connect(string host, int port);
    public void Dispose();
    public void Listen(int backlog);
    public int Receive(byte[] buffer);
    public int Send(byte[] buffer);
}

Here are the rules as documented in MSDN:

  • You cannot use a Socket returned from Accept to accept any additional connections from the connection queue.
  • You cannot call Accept, Bind, Connect, Listen, Receive, or Send if the socket has been closed.
  • You must call Bind before you can call the Listen method.
  • You must call Listen before calling Accept.
  • Connect(string host, int port) can only be called if addressFamily is either InterNetwork or InterNetworkV6.
  • Connect cannot be called if Listen has been called.
  • You have to either call Connect or Accept before Sending and Receiving data.
  • If the socket has been previously disconnected, then you cannot use Connect to restore the connection.

You may use as the basis of your proof the concepts that were discussed in previous Q.E.D. articles, including:

Please post responses in the comments, email them to mperry@adventuresinsoftware.com, or post links on your own blog. I will share my solution next week.

Wet floor effect batch transform using WPF

Saturday, June 13th, 2009

My wife just took some new photos for her scrapbooking website. I have a coverflow effect on that page, so I needed to apply the wet floor transform to 64 images. Rather than do it all by hand, I wrote some WPF code to do it for me:

public class WetFloor
{
    public static void Render(string source, string target, int targetWidth)
    {
        using (Stream input = File.OpenRead(source))
        {
            // Load the source jpg.
            JpegBitmapDecoder sourceJpg = new JpegBitmapDecoder(input, BitmapCreateOptions.None, BitmapCacheOption.Default);
            if (sourceJpg.Frames.Count != 1)
                throw new Exception(String.Format("I don't know how to handle {0} frames.", sourceJpg.Frames.Count));
            BitmapFrame sourceBitmap = sourceJpg.Frames[0];

            // Create a render target scaled to 400 pixels at 150% the aspect ratio.
            double scale = (double)targetWidth / sourceBitmap.PixelWidth;
            RenderTargetBitmap renderTarget = new RenderTargetBitmap(
                targetWidth, (int)Math.Round(scale * (float)sourceBitmap.PixelHeight * 1.5),
                sourceBitmap.DpiX, sourceBitmap.DpiY,
                PixelFormats.Pbgra32);

            // Paint a rectangle with the source bitmap.
            Rectangle original = new Rectangle()
            {
                Fill = new ImageBrush(sourceBitmap),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height
            };

            original.Arrange(new Rect(new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(original);

            // Paint a black rectangle.
            Rectangle shade = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Black),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height
            };

            shade.Arrange(new Rect(new Point(0, scale * sourceBitmap.Height), new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(shade);

            // Paint an inverted rectangle with the source bitmap.
            Border reflection = new Border()
            {
                Background = new ImageBrush(sourceBitmap),
                Width = scale * sourceBitmap.Width,
                Height = scale * sourceBitmap.Height,
                RenderTransform = new ScaleTransform(1.0f, -1.0f, scale * sourceBitmap.Width / 2, scale * sourceBitmap.Height / 2),
                OpacityMask = new LinearGradientBrush()
                {
                    StartPoint = new Point(0, 1),
                    EndPoint = new Point(0, 0),
                    GradientStops = new GradientStopCollection(new GradientStop[]
                    {
                        new GradientStop()
                        {
                            Offset = -0.3f,
                            Color = Colors.Black
                        },
                        new GradientStop()
                        {
                            Offset = 0.5f,
                            Color = Colors.Transparent
                        }
                    })
                }
            };

            reflection.Arrange(new Rect(new Point(0, scale * sourceBitmap.Height), new Size(scale * sourceBitmap.Width, scale * sourceBitmap.Height)));
            renderTarget.Render(reflection);

            // Render the final product.
            JpegBitmapEncoder png = new JpegBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(renderTarget));
            using (Stream fs = File.Create(target))
            {
                png.Save(fs);
            }
        }
    }
}

Enjoy.

Ignore whitespace in TFS compare

Friday, April 10th, 2009

Thanks to JB Brown, James Manning, and Brian Harry for this information. Unfortunately, nobody I could find puts it all in one place, step-by-step. So here it is.

  • In Visual Studio, select "Tools", "Options", "Source Control", ":Visual Studio Team Foundation Server".
  • Click "Configure User Tools".
  • Click "Add".
  • Extension: .cs
  • Operation: Compare
  • Command: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\diffmerge.exe
  • Arguments: %1 %2 %6 %7 %5 /ignorespace
  • OK
  • OK
  • OK

If you use VB, C++, XML, or any other file extension, go back and add those extensions, too.

And please, Microsoft. Why is this not the default?

The real solution to creating objects in a managed WMI provider

Wednesday, September 3rd, 2008

This is a case of RTFM. The real solution to the problem of creating an object via WMI is the ManagementCreate attribute. This attribute decorates a static method that takes key parameters (themselves decorated with ManagementName) and returns a new management entity.

The mental block that I was running into was the proxy. I was equating a .NET object with a WMI object. I assumed that creation of a .NET object via "new" was equivalent to the creation of a WMI entity via "path ... create". Not the case.

The .NET objects are proxies for the WMI entities. The are created and destroyed with each request. A call to the create method should cause the enumerate method to include the new entity, as well as return the new proxy object. However, the .NET object returned from the create method will be different from the one returned from the create method. These are merely proxies to the same entity.

Problem solved. Moving on.

Solution to creating a managed MWI provider

Tuesday, September 2nd, 2008

I am attempting to create a Windows Service in C# that exposes management interfaces through WMI. I'm having trouble creating new objects from the WMI command line. See the problem description.

In working through the referenced article, I learned a bit more about managed MWI providers. At the half-way point, you have a working coupled WMI provider written in C#. You should be able to enumerate service objects with the following command:

C:\>wmic /NAMESPACE:\\root\test PATH win32_servicehost get /value

I tried that, and sure enough I get a list of service hosts and the services that they are running. These objects are created in code by the EnumerateServiceHosts method.

My problem requires that I create a new object. I have to allow operations to specify its properties and issue it commands. So I tried do create an instance of a Win32_ServiceHost with an ID not yet in the list:

wmic:root\cli>PATH win32_servicehost create id=821
Create instance of 'win32_servicehost' class (Y/N)?y
ERROR:
Code = 0x80041024
Description = Provider is not capable of the attempted operation
Facility = WMI

Not surprising that this would fail, since the code returns null if the process ID is not found. But if I try to create an object using a process ID already in the list, this is what happens:

wmic:root\cli>PATH win32_servicehost create ID=820
Create instance of 'win32_servicehost' class (Y/N)?y
ERROR:
Code = 0x80041019
Description = Object or property already exists
Facility = WMI

So I changed the example code to allow me to create process hosts with IDs that are not in use. Now every ID I use gives me "Object or property already exists". But if I query for an ID that does not exist, it tells me "No Instance(s) Available".

wmic:root\cli>PATH win32_servicehost create id=820
Create instance of 'win32_servicehost' class (Y/N)?y
ERROR:
Code = 0x80041019
Description = Object or property already exists
Facility = WMI

wmic:root\cli>PATH win32_servicehost create id=821
Create instance of 'win32_servicehost' class (Y/N)?y
ERROR:
Code = 0x80041019
Description = Object or property already exists
Facility = WMI

wmic:root\cli>PATH win32_servicehost where ID=820
ID   Services
820  {"SCardSvr"}

wmic:root\cli>PATH win32_servicehost where ID=821
No Instance(s) Available.

Here's my solution

I think I'm approaching the problem from the wrong angle. Create doesn't mean what I think it means. WMI is set up to access objects that already exist.

Instead of creating instances of objects from WMI, I'm going to change my service so that I can return objects created in code. The operations team can issue commands to these existing objects to cause new objects to be created -- in code. Based on what works in the example, this should solve the problem. I'll post when it's working.

Dallas TechFest 2008

Sunday, May 4th, 2008

The first Dallas TechFest was this weekend. There were sessions on .NET, Ruby, REST, Groovy, Flex, AIR, and many other languages, tools, and platforms. It was a chance for members of different communities to get together and cross-train each other. I made a point of stepping outside my comfort zone, rather than sticking with the .NET track.

But the two sessions that I attended within the .NET track were hosted by Carl Franklin and Richard Campbell of .NET Rocks. I couldn't miss the chance to see them. The first was a recording of a .NET Rocks episode on community. The second was Richard on web application performance and scalability. If you ever get a chance to hear Richard speak, you must go.

After his talk, I asked Richard to take a look at Update Controls. He was very gracious and gave me at least a half an hour of his time over lunch to show him the demo. He understood the benefits of the library immediately, and gave me some pointers for getting the idea across more effectively. Finally, he told me that I need to complete the database story, which, as it happens, is in the works.

It was a thrill to finally meet Carl and Richard, the guys who inspired me to launch this site. And it was enlightening to see what's been going on in other parts of the industry. Who knows. Maybe I'll get a chance to present next year.

Raise the level of abstraction

Sunday, March 9th, 2008

There's a long post by Jeff Moser entitled What Does It Take To Become A Grandmaster Developer? The post itself is interesting, but I'd like to call out one thing that I immediately reacted to.

A foolish [assumption] would be to believe that you could exclusively live at the higher levels without understanding the lower levels. [...] Early on, I thought it was always possible to "raise the level of abstraction." I was wrong.

I am all about raising the level of abstraction, so I thought he was calling me foolish. He seems to be saying that we cannot escape the details, even as we find the chunks that make them easier to remember.

After calming down a bit, I realized that he was saying something much more intelligent. He's saying that it is not always possible to completely hide details behind an abstraction. Further, he's implying that it is not always desirable to do so.

Two kinds of abstractions
The reasonable viewpoint that I missed at first is that there are two kinds of abstractions. There is the "leaky" abstraction that lets the underlying detail show through. Then there is the complete abstraction that hides those details from you for your own good. Both of these abstractions are useful.

Examples of leaky abstractions are web application languages like JSP and ASP. These generate HTML for you. They are an abstraction over HTML that relates it to your application data model. But they do not completely hide HTML from you. In fact, both of these languages live inside what looks like an HTML file.

Examples of complete abstractions are languages like C. It completely hides the underlying machine code. For an exceedingly large class of problems, you never have to write, look at, or think about machine code. At one point in history a knowledge of machine code was useful to C programming. But in this day, it's hard to argue that someone needs to have felt that pain in order to be a good C programmer.

Because JSP and ASP are leaky abstractions, they can never be decoupled from HTML. But because C is a compete abstraction, it can be ported (with varying degrees of success) to other platforms.

An exceedingly large class of problems
An abstraction is a model of the underlying idea. C programming isn't machine programming, but it represents machine programming. It captures the essential features required for a certain class of problems. Sure, there are problems outside of this class for which you have to write machine code. Boot loaders, operating systems, and device drivers can probably not be written entirely in C. But these are the minority. An exceedingly large class of programs can be written in C.

To make a complete abstraction, you must first identify the class of problems that you intend to solve. All problems in this class can be solved using only the features of the abstraction. Problems outside this class will require features of the underlying idea.

Necessary and sufficient
Once you have defined a class of problems, you can apply the concepts of necessary and sufficient features to create a complete abstraction for that class. Go back and read previous posts from the dof (Degrees of Freedom) category. This post appears in that category, so skip back.

Each of the features of the abstraction is necessary for solving at least one problem in the class. The sum of the features is sufficient for solving all problems in the class.

This is also known as the Goldilocks principle. The abstraction is neither too big, nor too small. It's just right for the class of problems it solves.

This is also known as composability and orthogonality. Each feature of the abstraction is orthogonal to the others because there is no combination of other features that can achieve the same effect. And the set of features can be composed to create a solution to any problem in the class.

Benefits of a complete abstraction
When you have a complete abstraction of necessary and sufficient features, you can solve any problem in a defined class. As long as you know that your problem belongs to that class, you never have to peel back the cover of the abstraction.

Not all abstractions demand this level of scrutiny and design, but those that do are often the most useful. They hide the details of the underlying idea for your own good. You never have to think about those details again. People new to the idea can focus on the abstraction and never even learn those details. And, those details can change without affecting artifacts created on top of the abstraction.

Bill Gates and Steve Jobs on the magic of software

Friday, June 1st, 2007

Bill Gates and Steve Jobs participated in an interview for D5. The interviewers and audience were generally looking for points of tension, but Bill and Steve gave them so much more. The message was one of passion. Bill summed it up here:

If you look inside my brain, it's filled with software, and the magic of software, and the belief in software, and that's not going to change.

-- Bill Gates

Video on D5

The Work Queue Pattern

Thursday, November 30th, 2006

In each of the Enterprise systems I've worked on, we've had background processes to massage data. They could be report aggregators, media renderers, or message processors. The features that they have in common are:

  • Work items may not be lost.
  • Work must not be interrupted.
  • Work items are independent of one-another.
  • Work items may not be repeated.

The pattern that best implements these features is a work queue. A work queue has a publish-subscribe queue of work items serviced by redundant processors. The durability of the queue ensures that work items are not lost. Processors are allowed to work in parallel since work items are independent. That redundancy ensures that work continues even if a processor goes down. And proper use of the queue ensures that no two processors get the same work item.

This problem could be solved with JMS or MSMQ, but in my experience these solutions imposed undesirable requirements. JMS marries your code to J2EE, while MSMQ requires a Microsoft server environment. I prefer to keep my options open. Fortunately, it is not difficult to implement a work queue directly in the database of your choice.

Here's my solution
First you have to define a work queue table. In addition to columns that describe the work to be done, this table must also have a column that identifies the processor that is performing the work. A foreign key to a processor table works well. The processor table has a primary key (the processor ID) and the name of the machine running the process.

When a processor is ready to perform some work, it must query the work queue table twice. First it looks for any available work item, and then it ensures that no other processor has claimed that work item. This two-step query is the most effective way I've found to detect and resolve collisions let me explain in more detail.

The first query is performed with the transaction isolation level set to "read uncommitted". This allows the query to see any work queue items that are currently being claimed. The result of this query is the work item ID that the processor will try to claim for itself.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET @id = (SELECT WorkItemID FROM WorkQueue WHERE ProcessorID=NULL);

The second query is performed with the standard transaction isolation level of "read committed". It both locks the record and verifies that the work has not yet been claimed.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT ProcessorID FROM WorkQueue (UPDATE) WHERE WorkItemID=@id;

If the row is found and the processor ID comes back NULL, then you know that the work has not yet been claimed. You may now update the ProcessorID and commit the transaction.

However, if the row is not found, then the work has already been completed and deleted from the queue. If the processor ID is not NULL, then a collision has occurred. Another processor is already busy working on that item. So you just commit your transaction and try again.

It took several days of trial-and-error to get the pattern right the first time. But now that I have it, it is easy to create a work queue without the overhead of JMS or MSMQ.