Scott Hanselman

The Weekly Source Code 56 - Visual Studio 2010 and .NET Framework 4 Training Kit - Code Contracts, Parallel Framework and COM Interop

August 12, '10 Comments [11] Posted in ASP.NET | ASP.NET Ajax | ASP.NET Dynamic Data | ASP.NET MVC | BCL | Learning .NET | LINQ | OData | Open Source | Programming | Source Code | VB | Web Services | Win7 | Windows Client | WPF
Sponsored By

Do you like a big pile of source code? Well, there is an imperial buttload of source in the Visual Studio 2010 and .NET Framework 4 Training Kit. It's actually a 178 meg download, which is insane. Perhaps start your download now and get it in the morning when you get up. It's extremely well put together and I say Kudos to the folks that did it. They are better people than I.

I like to explore it while watching TV myself and found myself looking through tonight. I checked my blog and while I thought I'd shared this with you before, Dear Reader, I hadn't. My bad, because it's pure gold. With C# and VB, natch.

Here's an outline of what's inside. I've heard of folks setting up lunch-time study groups and going through each section.

C# 4 Visual Basic 10 
F# Parallel Extensions
Windows Communication Foundation Windows Workflow
Windows Presentation Foundation ASP.NET 4
Windows 7 Entity Framework
ADO.NET Data Services (OData) Managed Extensibility Framework
Visual Studio Team System RIA Services
Office Development  

I love using this kit in my talks, and used it a lot in my Lap Around .NET 4 talk.

There's Labs, Presentations, Demos, Labs and links to online Videos. It'll walk you step by step through loads of content and is a great starter if you're getting into what's new in .NET 4.

Here's a few of my favorite bits, and they aren't the parts you hear the marketing folks gabbing about.

Code Contracts

Remember the old coding adage to "Assert Your Expectations?" Well, sometimes Debug.Assert is either inappropriate or cumbersome and what you really need is a method contract. Methods have names and parameters, and those are contracts. Now they can have conditions like "don't even bother calling this method unless userId is greater than or equal to 0 and make sure the result isn't null!

Code Contracts continues to be revised, with a new version out just last month for both 2008 and 2010. The core types that you need are included in mscorlib with .NET 4.0, but you do need to download the tools to see them inside Visual Studio. If you have VS Pro, you'll get runtime checking and VS Ultimate gets that plus static checking. If I have static checking and the tools I'll see a nice new tab in Project Properties:

Code Contracts Properties Tab in Visual Studio

I can even get Blue Squigglies for Contract Violations as seen below.

A blue squigglie showing that a contract isn't satisfied

As a nice coincidence, you can go and download Chapter 15 of Jon Skeet's C# in Depth for free which happens to be on Code Contracts.

Here's a basic idea of what it looks like. If you have static analysis, you'll get squiggles on the lines I've highlighted as they are points where the Contract isn't being fulfilled. Otherwise you'll get a runtime ContractException. Code Contracts are a great tool when used in conjunction with Test Driven Development.

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics.Contracts;

namespace ContractsDemo
{
[ContractVerification(true)]
class Program
{
static void Main(string[] args)
{
var password = GetPassword(-1);
Console.WriteLine(password.Length);
Console.ReadKey();
}

#region Header
/// <param name="userId">Should be greater than 0</param>
/// <returns>non-null string</returns>
#endregion
static string GetPassword(int userId)
{
Contract.Requires(userId >= 0, "UserId must be");
Contract.Ensures(Contract.Result<string>() != null);

if (userId == 0)
{
// Made some code to log behavior

// User doesn't exist
return null;
}
else if (userId > 0)
{
return "Password";
}

return null;
}
}
}

COM Interop sucks WAY less in .NET 4

I did a lot of COM Interop back in the day and it sucked. It wasn't fun and you always felt when you were leaving managed code and entering COM. You'd have to use Primary Interop Assemblies or PIAs and they were, well, PIAs. I talked about this a little bit last year in Beta 1, but it changed and got simpler in .NET 4 release.

Here's a nice little sample I use from the kit that gets the Processes on your system and then makes a list with LINQ of the big ones, makes a chart in Excel, then pastes the chart into Word.

If you've used Office Automation from managed code before, notice that you can say Range[] now, and not get_range(). You can call COM methods like ChartWizard with named parameters, and without including Type.Missing fifteen times. As an aside, notice also the default parameter value on the method.

static void GenerateChart(bool copyToWord = false)
{
var excel = new Excel.Application();
excel.Visible = true;
excel.Workbooks.Add();

excel.Range["A1"].Value2 = "Process Name";
excel.Range["B1"].Value2 = "Memory Usage";

var processes = Process.GetProcesses()
.OrderByDescending(p => p.WorkingSet64)
.Take(10);
int i = 2;
foreach (var p in processes)
{
excel.Range["A" + i].Value2 = p.ProcessName;
excel.Range["B" + i].Value2 = p.WorkingSet64;
i++;
}

Excel.Range range = excel.Range["A1"];
Excel.Chart chart = (Excel.Chart)excel.ActiveWorkbook.Charts.Add(
After: excel.ActiveSheet);

chart.ChartWizard(Source: range.CurrentRegion,
Title: "Memory Usage in " + Environment.MachineName);

chart.ChartStyle = 45;
chart.CopyPicture(Excel.XlPictureAppearance.xlScreen,
Excel.XlCopyPictureFormat.xlBitmap,
Excel.XlPictureAppearance.xlScreen);

if (copyToWord)
{
var word = new Word.Application();
word.Visible = true;
word.Documents.Add();

word.Selection.Paste();
}
}

You can also embed your PIAs in your assemblies rather than carrying them around and the runtime will use Type Equivalence to figure out that your embedded types are the same types it needs and it'll just work. One less thing to deploy.

Parallel Extensions

The #1 reason, IMHO, to look at .NET 4 is the parallelism. I say this not as a Microsoft Shill, but rather as a dude who owns a 6-core (12 with hyper-threading) processor. My most favorite app in the Training Kit is ContosoAutomotive. It's a little WPF app that loads a few hundred thousand cars into a grid. There's an interface, ICarQuery, that a bunch of plugins implement, and the app foreach's over the CarQueries.

This snippet here uses the new System.Threading.Task stuff and makes a background task. That's all one line there, from StartNew() all the way to the bottom. It says, "do this chunk in the background." and it's a wonderfully natural and fluent interface. It also keeps your UI thread painting so your app doesn't freeze up with that "curtain of not responding" that one sees all the time.

private void RunQueries()
{
this.DisableSearch();
Task.Factory.StartNew(() =>
{
this.BeginTiming();
foreach (var query in this.CarQueries)
{
if (this.searchOperation.Token.IsCancellationRequested)
{
return;
}

query.Run(this.cars, true);
};
this.EndSequentialTiming();
}, this.searchOperation.Token).ContinueWith(_ => this.EnableSearch());
}

StartNew() also has a cancellation token that we check, in case someone clicked Cancel midway through, and there's a ContinueWith at the end that re-enables or disabled Search button.

Here's my system with the queries running. This is all in memory, generating and querying random cars.12% CPU across 12 processors single threaded

And the app says it took 2.3 seconds. OK, what if I do this in parallel, using all the processors?

2.389 seconds serially

Here's the changed code. Now we have a Parallel.ForEach instead. Mostly looks the same.

private void RunQueriesInParallel()
{
this.DisableSearch();
Task.Factory.StartNew(() =>
{
try
{
this.BeginTiming();
var options = new ParallelOptions() { CancellationToken = this.searchOperation.Token };
Parallel.ForEach(this.CarQueries, options, (query) =>
{
query.Run(this.cars, true);
});
this.EndParallelTiming();
}
catch (OperationCanceledException) { /* Do nothing as we cancelled it */ }
}, this.searchOperation.Token).ContinueWith(_ => this.EnableSearch());
}

This code says "go do this in a background thread, and while you're there, parallelize this as you like." This loop is "embarrassingly parallel." It's a big for loop over 2 million cars in memory. No reason it can't be broken apart and made faster.

Here's the deal, though. It was SO fast, that Task Manager didn't update fast enough to show the work. The work was too easy. You can see it used more CPU and that there was a spike of load across 10 of the 12, but the work wasn't enough to peg the processors.

19% load across 12 processors 

Did it even make a difference? Seems it was 5x faster and went from 2.389s to 0.4699 seconds. That's embarrassingly parallel. The team likes to call that "delightfully parallel" but I prefer "you're-an-idiot-for-not-doing-this-in-parallel parallel," but that was rejected.

0.4699 seconds when run in parallel. A 5x speedup.

Let's try something harder. How about a large analysis of Baby Names. How many Roberts born in the state of Washington over a 40 year period from a 500MB database?

Here's the normal single-threaded foreach version in Task Manager:

One processor chilling.

Here's the parallel version using 96% CPU.

6 processes working hard!

And here's the timing. Looks like the difference between 20 seconds and under 4 seconds.

PLINQ Demo

You can try this yourself. Notice the processor slider bar there at the bottom.

ProcessorsToUse.Minimum = 1;
ProcessorsToUse.Maximum = Environment.ProcessorCount;
ProcessorsToUse.Value = Environment.ProcessorCount; // Use all processors.

This sample uses "Parallel LINQ" and here's the two queries. Notice the "WithDegreeofParallelism."

seqQuery = from n in names
where n.Name.Equals(queryInfo.Name, StringComparison.InvariantCultureIgnoreCase) &&
n.State == queryInfo.State &&
n.Year >= yearStart && n.Year <= yearEnd
orderby n.Year ascending
select n;

parQuery = from n in names.AsParallel().WithDegreeOfParallelism(ProcessorsToUse.Value)
where n.Name.Equals(queryInfo.Name, StringComparison.InvariantCultureIgnoreCase) &&
n.State == queryInfo.State &&
n.Year >= yearStart && n.Year <= yearEnd
orderby n.Year ascending
select n;

The .NET 4 Training Kit has Extensibility demos, and Office Demos and SharePoint Demos and Data Access Demos and on and on. It's great fun and it's a classroom in a box. I encourage you to go download it and use it as a teaching tool at your company or school. You could do brown bags, study groups, presentations (there's lots of PPTs), labs and more.

Hope you enjoy it as much as I do.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 55 - NotABlog: A Local XML-RPC MetaWebLog Endpoint That Lies To Windows Live Writer

August 2, '10 Comments [23] Posted in ASP.NET | Blogging | IIS | Source Code
Sponsored By

WLW in Use My team has to write a lot. We write some blog posts, sure, but we also write a lot of tutorials for various sites. Right now the http://www.asp.net site runs on Umbraco, but MSDN runs on custom internal what-not, and there's other sites as well. The only common thread is HTML.

Sometimes someone will write a tutorial or document in Word, then try to get some HTML out of that, that will only ends in pain and suffering. We could use something like Markdown or Dreamweaver or Expression Web, or write the HTML ourselves, but we keep coming back to Windows Live Writer. No joke, for our workflow, WLW is the best blog editor out there.

It creates nice clean markup, and lets us stick to the basics, which for us are H1, P, PRE, UL, LI, ACRONYM, BLOCKQUOTE, IMG and not much more. It supports plugins for editing and coloring code (I use the PreCode plugin) and has very nice image handling features like Watermarks, resizing and linking to larger versions, etc. We would like to write everything in it.

Problem is, it doesn't include a Save As dialog. We could view|source and copy the HTML directly after we edit, but WLW uses temporary image links of a custom type while editing with names like $whatever.png and hides the images in temp files and opaque blobs.

NOTE: Windows Live Writer does support editing and publishing to the Blogger API, the MetaWeblog API and via AtomPub. AtomPub is the newest and most rigid, but I'm going to start by creating a MetaWeblog Server because I understand it and it's trivially lightweight. I'll try to follow this post up the with the same functionality except with AtomPub soon in order to juxtapose the two. Remember that MetaWeblog extends the Blogger API and is all about blogging and content. AtomPub isn't just about blogging, it's about editing anything. WLW is a blogging client that speaks AtomPub, but it's not a general AtomPub client.

So what I'm going to build is a small local "NotABlog" server that I can point Windows Live Writer to and fool it into thinking it's a blog. Then I'll effectively be able to Create, Read, and Update blog posts (HTML files and their associated images) that are sitting in a local publish folder.

This sample might be interesting to you and your organization (or the not-yet-written AtomPub version) because you could potentially put an endpoint in front of existing systems that you work on and enable business users to edit content with Windows Live Writer. WLW isn't too scary for business folks and it's a nice interface for updating existing custom content management systems that you might have lying around. Creating an editing endpoint for existing clients is a low-effort way to reinvigorate existing content management systems.

Metaweblog

Many years ago (like 12) XML-RPC was created by Dave Winer and some Microsofties. It's not SOAP, it's literally remote procedure calls over HTTP with XML. There are other protocols built on top of XML-RPC, which are just interfaces. Effectively they are agreements that an endpoint will contain certain named methods with certain parameters.

The MetaWeblog API is an XML-RPC interface that Dave made that lets you edit weblog entries. It's older, but it's effectively universal. Here's an example of what an XML-RPC call looks like.



examples.getSomething


70


Charles Cook created an amazing and elegant library called XML-RPC.NET and has given it to the community. He's kept it working nicely such that I was able to get it working in my .NET 4.0 application without any modification even though I was using an older 1.0.0.8 version for .NET 1.0 in my first version. That's a testament to Charles' work. Later I downloaded the latest 2.4.0 from 2008 and it worked nicely also and fixed some bugs.

In 2008, Keyvan Nayyeri created a nice little MetaWeblog ASP stub in ASP.NET so I started building with that. I just return true in the ValidateUser method, because this is for local editing. I lie (return hard-coded stuff in a few places) to Windows Live Writer.

Here's what the interface looks like:

namespace NotABlog
{
public interface IMetaWeblog
{
#region MetaWeblog API

[XmlRpcMethod("metaWeblog.newPost")]
string AddPost(string blogid, string username, string password, Post post, bool publish);

[XmlRpcMethod("metaWeblog.editPost")]
bool UpdatePost(string postid, string username, string password, Post post, bool publish);

[XmlRpcMethod("metaWeblog.getPost")]
Post GetPost(string postid, string username, string password);

[XmlRpcMethod("metaWeblog.getCategories")]
CategoryInfo[] GetCategories(string blogid, string username, string password);

[XmlRpcMethod("metaWeblog.getRecentPosts")]
Post[] GetRecentPosts(string blogid, string username, string password, int numberOfPosts);

[XmlRpcMethod("metaWeblog.newMediaObject")]
MediaObjectInfo NewMediaObject(string blogid, string username, string password, MediaObject mediaObject);

#endregion

#region Blogger API

[XmlRpcMethod("blogger.deletePost")]
[return: XmlRpcReturnValue(Description = "Returns true.")]
bool DeletePost(string key, string postid, string username, string password, bool publish);

[XmlRpcMethod("blogger.getUsersBlogs")]
BlogInfo[] GetUsersBlogs(string key, string username, string password);

[XmlRpcMethod("blogger.getUserInfo")]
UserInfo GetUserInfo(string key, string username, string password);

#endregion
}
}

You just need to derive from Charles' XmlRpcService and he'll handle the routing of the HTTP POSTs and the calling of the methods and tearing apart of the parameters. (Actually, as a curiosity back in the ASP.NET MVC 1.0 timeframe both Phil and I write XmlRpcRoutes and supporting samples just to see if it was possible. It is.)

The idea is to catch the calls and just redirect them to the file system. Here's a simple example:

string IMetaWeblog.AddPost(string blogid, string username, string password,
Post post, bool publish)
{
if (ValidateUser(username, password))
{
string id = string.Empty;
string postFileName;
if (String.IsNullOrEmpty(post.title))
postFileName = Guid.NewGuid() + ".html";
else
postFileName = post.title + ".html";

File.WriteAllText(Path.Combine(LocalPublishPath, postFileName), post.description);

return postFileName;
}
throw new XmlRpcFaultException(0, "User is not valid!");
}

Go run it yourself if you like. Here's how.

Setting up your Local Publishing Endpoint

First, edit the web.config and change your local publish directory if you like. Currently it just puts posts in a folder .\LocalPublish in the current directory of your web application. Free free to change it. I put it in my desktop.











Since this is a web application, I need a Web Server to run it under. I didn't want to require Visual Studio or IIS to run it, so I figured I'd use IIS Express from WebMatrix to do it since it's free and you don't need to be Admin to run it (except on Win2k3). Install WebMatrix and you'll get IIS Express.

I created a batch file called StartNotABlog.bat and put this in it:

SET ExecPath=%ProgramFiles(x86)%
IF "%ExecPath%" == "" SET ExecPath = "%ProgramFiles%

"%ExecPath%\Microsoft WebMatrix\iisexpress.exe" /path:"%CD%" /port:12345

It's pretty straightforward and it'll work on both x86 and x64. It starts up a Web Server on port 12345 with the current directory (that the batch file is running in, not the environment's current directory) as the path.

Now, start up Windows Live Writer. Add a blog, select Other...

What kind of Blog Services

And select "Metaweblog API" as the type.

Choose a Blog Type

Put in http://localhost:12345/MetaWeblogAPI.ashx as the remote posting URL.

Enter your remote posting URL  

There's no username or password so just put in something.

Windows Live Writer (2) 

Click Next and Finish and go edit a post! I'm using it now to edit a 3000 word tutorial on ASP.NET.

image

Not a Blog.zip. Download the Code and have fun playing with it. There's actually not much there but it's already changed how I do my job. Now the trick will be to show the bosses how easy it is to give anything an endpoint that WLW will talk to. Maybe this blog post will help. ;)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Removing Dead Tracks (Duplicates that don't exist) from iTunes using C#

July 4, '10 Comments [36] Posted in Source Code | Tools
Sponsored By

image I hate iTunes on Windows with the heat of a thousand suns. It is a pox on my existence and it has cost me hours of pain and suffering. I'm sure it's all unicorns and spun sugar on MAC, but as a guy using Windows with dozens of gigabytes of music on a shared file server that I've ripped since the first CD I ever purchased, it's hell on earth. I could manage all my music with Zune (and I do have a Zune Pass for leasing music I don't want to buy) but there are iDevices in the house and iTunes is what it takes.

Did I mention it sucks? It's slow, frustrating, and is effectively just a giant listbox that exists only to mess up my music library and occasionally try to sneak Safari and QuickTime onto my machines. But enough about Satan, let's yank some dead tracks, shall we?

At some point, I ended up with hundreds of dead tracks and/or duplicates. I ended up with "LL Cool J - I Need Love 1.mp3" and "LL Cool J - I Need Love 2.mp3" along side the original. It was taking up many gigs of duplicate space. I searched for *1.mp3 and *2.mp3, etc, and deleted the dupes on disk.

However, this then left me with a big iTunes database that THINKS it has music, even though the file on disk is long gone. Now, as a napalm-style solution, you CAN delete your iTunes library completely and re-add it. It'll add just files that exist, but you will lose any edits, artwork, etc, you may have added. This solution wasn't cool for me so I say, nay nay.

There's a MILLION stupid little shareware apps that purport to fix duplicates and remove dead tracks. In my case, removing dupes was easy, but yanking dead tracks isn't worth me spending money.

Instead, I sat down tonight and decided to write a script in C# that said something like "foreach track, does that track exist where the system thinks it should be? No? Delete it."

A little googling with Bing, however, brought me to a three year old post at ScarTech where Shawn (shame he's stopped blogging) did the code for me! Yay. Here's a bit of his code, using the iTunes COM SDK. His stuff was written using Visual Studio Express 2008, but I upgraded to 2010. Also, I'm on 64-bit and his COM code expects x86, so I changed the project properties from AnyCPU to x86 and it worked great.

//create a reference to iTunes
iTunesAppClass iTunes = new iTunesAppClass();
//get a reference to the collection of all tracks
IITTrackCollection tracks = iTunes.LibraryPlaylist.Tracks;
for (int i = trackCount; i > 0; i–)
{
IITTrack track = tracks[i];
if (track.Kind == ITTrackKind.ITTrackKindFile)
{
IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)track;
//if the file doesn’t exist, we’ll delete it from iTunes
if (fileTrack.Location == String.Empty || !System.IO.File.Exists(fileTrack.Location))
{
fileTrack.Delete();
}
}
}

Here's before:

1

So I ran it...

iTunes COM Sample Screenshot 

...and here's the afterparty:

image

Looks like it checked 7219 tracks and removed 570 dead ones. Yum.

I'd also encourage you to check out the second part of Shawn's iTunes and C# tutorial series where he creates a duplicate finder that knows to keep the higher bitrate song.

You can download Shawn's code here but as he hasn't blogged in a LONG time, I've mirrored it at SkyDrive. Big thanks to Shawn at ScarTech for saving me my evening and for tidying up my iTunes.

Good stuff, the internets.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 54 - Can't have Multiple Attributes of the Same Type when using a TypeDescriptor

July 3, '10 Comments [5] Posted in ASP.NET Dynamic Data | Source Code
Sponsored By

When I was in a China a few weeks back, I had a nice chat with a fellow named Zhe Wang who was using ASP.NET Dynamic Data to create a website. He made his own Custom Attribute to apply to his entities and effectively made an authorization scheme so that certain users could access some actions but not others.

In this little example, EntityAuthorize is Zhe's own custom attribute with a few properties.

namespace MyModels.Metadata
{
[EntityAuthorize(Roles="Administrators", AllowedEntityActions=EntityActions.List | EntityActions.Edit)]
[EntityAuthorize(Users="Administrator", DeniedEntityActions=EntityActions.Edit)]
public partial class SurveyMetaData
{
string Address { get; set; }
string Content { get; set; }
string Something { get; set; }
EntityCollection<OtherStuff> OtherThings { get; set; }
}
}

In this example, he's got two attributes stacked up on the same class. There's nothing wrong with this, by the way.

Later on, when it came time to fetch these attributes and take action on them, something weird happened. He was only getting one of the attributes when he fetched them via MetaTable.Attributes.OfType<T>() which is the usual way in Dynamic Data. In frustration he tried to get them using GetCustomAttributes which is slow and not much fun. Here's his clever hack (note: this won't be needed as we'll solve this another way).

public static bool FilterVisible(this MetaTable table, EntityActions action)
{
var usn = HttpContext.Current.User.Identity.Name;
var roles = Roles.GetRolesForUser(usn);
var attrs = table.Attributes.OfType<EntityAuthorizeAttribute>()
.Where
(
//try to get the attributes we need using the DynamicData metatable,
// but we're only getting one of the attributes of this type.
).Union
(
//get it ourselves the slow way using GetCustomAttributes and it works.
table.Attributes.OfType<MetadataTypeAttribute>()
.Select(t => Attribute.GetCustomAttributes(t.MetadataClassType))
.SelectMany(col => col).OfType<EntityAuthorizeAttribute>()
);

var allow = attrs.Any(a => a.AllowedEntityActions.HasFlag(action));

var deny = attrs.Any(a => a.DeniedEntityActions.HasFlag(action));

return allow && (!deny);
}

How does MetaTable.Attributes get populated? Via the a standard System.ComponentModel.TypeDescriptor provider that does all the work of getting the attributes, merging them together and putting them in a collection. However, the TypeDescriptor is kind of goofy in this case due to a weird behavior deep in the system.

Here's a little reproduction application to show the behavior where the TypeDescriptor fails to get both attributes. The TypeDescriptor API has some subtle differences with the standard reflection API.

class Program {
static void Main(string[] args) {
var prop = TypeDescriptor.GetProperties(typeof(Product))["Num"];
foreach (Attribute attrib in prop.Attributes) {
Console.WriteLine(attrib.GetType());
}
}
}

public class Product {
[TestPermission("foo1", "bar1")]
[TestPermission("foo2", "bar2")]
public int Num { get; set; }
}

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class TestPermissionAttribute : Attribute {
public TestPermissionAttribute(string role, string user) { }
}

Notice the explicit AllowMultiple = true in the AttributeUsage attribute. However, deep in Attribute (yes, System.Attribute) there's a virtual property called TypeID and the value of that property becomes a key in the hashtable that the TypeDescriptor uses to remove duplicates. Inside of Attribute, we see:

public virtual object TypeId
{
get
{
return base.GetType();
}
}

Which probably should be something like this, however it's not (psudocode):

public virtual TypeID {
get {
if (IsAllowMultiple()) return this;
return GetType();
}
}

Since it's not really this way, but it IS virtual, you can certainly change your implementation of TypeID to return this, like, ahem, this:

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class TestPermissionAttribute : Attribute {
public TestPermissionAttribute(string role, string user) { }

public override object TypeId { get { return this; } }
}

However, in this naive example, this object has no state. Notice the parameters are not stored, so "this" would be the same object. Add a few fields so the objects are actually different as they now hold some states, and the sample works returning this as the TypeID:

using System;
using System.ComponentModel;
class Program
{
static void Main(string[] args)
{
var prop = TypeDescriptor.GetProperties(typeof(Product))["Num"];
foreach (Attribute attrib in prop.Attributes)
{
Console.WriteLine(attrib.GetType());
}
Console.ReadLine();
}
}

public class Product
{
[TestPermission("foo1", "bar1")]
[TestPermission("foo2", "bar2")]
public int Num { get; set; }
}

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class TestPermissionAttribute : Attribute
{
public TestPermissionAttribute(string role, string user) { Role = role; User = user; }

private string Role;
private string User;

public override object TypeId { get { return this; } }
}

So, the moral is, if you're using TypeDescriptors to get CustomAttributes and you want to have multiple attributes of the same type, make sure you:

  • Override TypeId to return this
  • Make sure AllowMultiple = true
  • Make sure your object has some fields (i.e. state)

Thanks to Zhe Wang and David Ebbo for the samples and their help.

I hope the two people who made it this far into the post and the one person who would ever hit this edge case appreciates this tale of obscurity.

If you like, follow me on Twitter: @shanselman

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

C++ killed my grandpappy - Is C++ hard and where are the C++ coders hiding?

June 29, '10 Comments [52] Posted in Source Code
Sponsored By

After learning BASIC and ASM, for a job I started coding in C, when Hello World on Windows was 92 lines of code. (Apparently Hello World is easier now, says Pete Brown)

One of my first big projects was an app called Foolproof that kept kids in high school from breaking into their school's lab computers. It had a C core engine, a TurboVision DOS component and a 16-bit Windows C++ App. I did the DOS, Win16 and later the Win32 API all in C++. Later I did some VB3, then Delphi, then worked at Nike doing Java (right when RMI was starting, and HotJava when we wrote once and debugged everywhere) until I finally ended up in managed code, mostly doing C#.

Some folks don't know Microsoft has a free C++ Express Edition of Visual Studio 2010 and that it's one of the most used free versions out there. Someone is coding C++, and they are doing a lot of it.

Are you coding in C++, Dear Reader? What are you doing?

Most "managed people" cringe at the idea of using C++, thinking it's perhaps a language of the past and that it's not even possible to write Windows Apps in C++ without a Master's Degree. ;)

However, I just noticed there's a new project called Hilo with source to show people how to use the shiny new things in Windows 7 from C++ and how to make modern Windows applications that have animations and magical stuff that managed people assume belongs only to them.

The first drop of "Hilo" is apparently the first of many, all moving towards building a larger application. This first sample is a content browser (touch enabled!) for looking at videos and photos (yeah, yeah, I know) but you could totally use it to browse other stuff or use it as underlayment for your own app. This from the Windows Team Blog:

The main technologies used in Hilo: Direct2D, Windows Animation Manager, Windows Touch, Libraries and the Shell API.

  • Direct2D: For high performance and high-quality rendering of graphics.
  • Windows Animation Manager: To give the application a unique personality and to improve the user experience.
  • Windows Touch: To provide a more natural user interface through which the user can just “point” and “do”.
  • Libraries: To enable the application to provide users with a single, coherent view of their files.
  • Shell API: To navigate the images, to optimize by using the pre-built thumbnail cache and also to provide icons from actual content.

Even if you're not going to start coding C++, it's always a good idea to "see how the other half lives." For me, it's a return to what I thought I knew, and a way to look at C++ with fresh eyes.

What surprised me was that this app was written against Windows itself, with no frameworks. I always assumed you kind of needed MFC or some other library to be productive.

I'm not sure why I remember it this way, but C++ always made me think of regular expressions, and I assumed my code would **be &all like->this.you~know? You do need to know more about Windows, to be sure, as there is likely a decade of managed programmers who have forgotten what a message pump is, but it wasn't as scary as I had remembered.

Hilo Browser

Just download C++ Express, then the Hilo source, and build it. It has some cool effects with the carousel, like inertia when it spins, scaling, Z-ordering, hardware graphics acceleration. Now all they need is a designer! ;)

As an interesting example, it uses Direct 2D and has an animation for the orbit of the carousel. I removed all the if (SUCCEEDED(hr)) code from this snippet below because that's still the one thing about C++ on Windows that irks me. Don't judge then, judge me for yanking it for brevity.

This source is extremely defensive, dutifully checking HResults at every step. I mean, seriously, if it doesn't Succeed, what are you going to do about it? If (NOTSCREWED(hr)), you know.

For this source, the salient point is that there's some nice high-level Animation constructs like timers, transitions and storyboards.  For some wrongheaded reason I thought it'd be a pile of math so I was pleasantly surprised.

HRESULT OrbitAnimation::Setup(D2D1_ELLIPSE targetEllipse, double targetOpacity, double duration)
{
HRESULT hr = S_OK;

// Animation objects
ComPtr<IUIAnimationManager> animationManager;
ComPtr<IUIAnimationTimer> animationTimer;
ComPtr<IUIAnimationTransitionLibrary> transitionLibrary;
ComPtr<IUIAnimationStoryboard> storyboard;

// Transition objects
ComPtr<IUIAnimationTransition> centerXTransition;
ComPtr<IUIAnimationTransition> centerYTransition;
ComPtr<IUIAnimationTransition> radiusXTransition;
ComPtr<IUIAnimationTransition> radiusYTransition;
ComPtr<IUIAnimationTransition> opacityTransition;

// Initialize animation variables if necessary
if (nullptr == m_centerX)
{
hr = Initialize(targetEllipse, targetOpacity);
}

// Retrieve animation objects
hr = AnimationUtility::GetAnimationManager(&animationManager);
hr = AnimationUtility::GetTransitionLibrary(&transitionLibrary);

hr = AnimationUtility::GetAnimationTimer(&animationTimer);

// Initialize storyboard
hr = animationManager->CreateStoryboard(&storyboard);

// Create one transition for each orbit variable
hr = transitionLibrary->CreateLinearTransition(duration, targetEllipse.point.x, &centerXTransition);
hr = storyboard->AddTransition(m_centerX, centerXTransition);

hr = transitionLibrary->CreateLinearTransition(duration, targetEllipse.point.y, &centerYTransition);
hr = storyboard->AddTransition(m_centerY, centerYTransition);

hr = transitionLibrary->CreateLinearTransition(duration, targetEllipse.radiusX, &radiusXTransition);
hr = storyboard->AddTransition(m_radiusX, radiusXTransition);

hr = transitionLibrary->CreateLinearTransition(duration, targetEllipse.radiusY, &radiusYTransition);
hr = storyboard->AddTransition(m_radiusY, radiusYTransition);

hr = transitionLibrary->CreateLinearTransition(duration, targetOpacity, &opacityTransition);
hr = storyboard->AddTransition(m_opacity, opacityTransition);

hr = AnimationUtility::ScheduleStoryboard(storyboard);

return hr;
}

I was also pleased to see that intellisense worked nicely for everything typed as well as -> deferenced pointers, as seen in the screenshot below. Call me an intellicrack addict or weak-minded but it does speed things up once you know what you're looking for. The C++ Express SKU was a small download as well.

C++ Express Screenshot of Code

I'm not saying it's all puppies and cotton candy, but it's not the post-apocalyptic-Where-is-John-Connor-wasteland I thought it would be. I'll be giving C++ another look as I aim to learn (or relearn) another programming language this year (along with Scala). I'll report back as this application gets bigger and more interesting.

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Previous Page Page 2 of 17 in the Source Code category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.