Scott Hanselman

SlowCheetah - Web.config Transformation Syntax now generalized for any XML configuration file

August 23, '11 Comments [71] Posted in ASP.NET | Tools
Sponsored By

I did a post last year called If You're Using XCopy, You're Doing It Wrong that also included a video of my talk at Mix10 where I show how to deploy website with Web Deploy. One of the cooler not-very-well-known features is called Web.config Transformation. Once folks see it, then immediately want a general solution.

First, from the previous post:

You can right-click on your web.config and click "Add Config Transforms." When you do this, you'll get a web.debug.config and a web.release.config. You can make a web.whatever.config if you like, as long as the name lines up with a configuration profile. These files are just the changes you want made, not a complete copy of your web.config.

You might think you'd want to use XSLT to transform a web.config, but while they feels intuitively right it's actually very verbose.

Here's two transforms, one using XSLT and the same one using the XML Document Transform syntax/namespace. As with all things there's multiple ways in XSLT to do this, but you get the general idea. XSLT is a generalized tree transformation language, while this deployment one is optimized for a specific subset of common scenarios. But, the cool part is that each XDT transform is a .NET plugin, so you can make your own.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
  <xsl:copy>          
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="/configuration/appSettings">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <xsl:element name="add">
      <xsl:attribute name="key">NewSetting</xsl:attribute>
      <xsl:attribute name="value">New Setting Value</xsl:attribute>
    </xsl:element>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Or the same thing via the deployment transform:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/>
</appSettings>
</configuration>

This kind of config file transformation is so useful in fact, that it's one of the #1 feature requests...as a generalized solution. Folks want to transform their app.configs, or any XML file as part of their builds. Additionally, the current system only runs the transforms as a part of the publish process and folks would rather the transform happen "on F5" or on build. So, my team members Sayed Ibrahim Hashimi and Chuck England have done just that as a small VSiX called SlowCheetah XML Transforms.

In this screenshot I've created a simple Console Application, made a basic app.config, then right clicked and selected "Add Transform." This gives an app.debug.config and app.release.config. You could make and name these however you like, for example app.testing.config, etc.

An app.config file, the transform file, and the result in three panes

For example, here's a basic app.config for my Console app:

<?xml version="1.0" encoding="utf-8" ?>
<configuration >
<appSettings>
<add key="appName" value="Something"/>
<add key="url" value="http://hanselman.com/"/>
<add key="email" value="awesome@hanselman.com" />
</appSettings>
</configuration>

And here's the transform to change my one value in my development time config to a debug value (or test, staging, etc). You can do this for connectionStrings, app keys, compiler settings, anything in an XML or config file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="appName" value="Demo-debug" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
<add key="email" value="debug@contoso.com" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>

Note the Transform="Replace?" That can be replace, or insert, etc. Lots of choices. The ShowCheetah XML Transform Add-In also adds a "Preview Transform" right-click menu which makes writing these way easier.

The clever part is that there's no magic. All the functionality is installed to %LOCALAPPDATA%\Microsoft\MSBuild\SlowCheetah\v1\ and lives in a standard MSBuild .targets file. You don't even need the plugin if you are installing this on a build server, just copy the files or even check them in with your source and refer to them in your project or MSBuild files. It's added in your project file for you via the tooling like any custom targets:

<Import Project="$(LOCALAPPDATA)\Microsoft\MSBuild\SlowCheetah\v1\Microsoft.Transforms.targets" Condition="Exists('$(LOCALAPPDATA)\Microsoft\MSBuild\SlowCheetah\v1\Microsoft.Transforms.targets')" />

Between the build targets and the added menu items, you get:

  • Added tooling to desktop project to create XDT transforms
  • Ability to transform
    • app.config for desktop projects based on build configuration
    • any XML file to the output folder based on build configuration
  • Added tooling to enable previewing XDT transforms
  • For web projects you can easily transform other XML files during package/publish

Let Sayed and the team know what you think, if it's useful, and if you use it at Sayed's blog, or the SlowCheetah project site on the VS Gallery. Enjoy!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

Creating a Podcast Player with HTML5 for a IE9 Pinned Site on Windows 7

August 22, '11 Comments [8] Posted in IE9 | Javascript
Sponsored By

I was looking at the HTML5Rocks podcast player sample today, and remembered I never blogged about the poor man's Podcast Player that I created for the This Developer's Life for IE9 in April. Currently there's a Flash Player on each page on TDL and one day someone will create the One HTML5 Podcast Player to Rule Them All, but so far I haven't seen it. Most are just one-offs and samples. As is my little demo here.

The HTML5Rocks player has some clever bits to it, and it's worth you doing a View Source and checking it out. There is an empty <audio/> tag at the top of the page and they ask it "can you play MP3s?" and show a compatibility message depending on what it says. You can also put text inside the <audio/> element that will be shown if the element isn't supported at all.

 $('#compatibilityMsg').html('Your browser <strong>' + 
($('#player').get(0).canPlayType('audio/mpeg') != '' ? 'can' : 'cannot') +
'</strong> play .mp3 files using HTML5 audio');

Playing audio with the <audio> tag really couldn't be simpler. The HTML5Rocks sample has some extras like setting volume and seeking that you don't see much in samples.

I wanted to create a little Podcast player for TDL that would combine things like Site Pinning and Jump Lists with some custom buttons for pinned sites and use HTML5 audio for playing the show.

So, the features:

  • Pinnable (drag the FavIcon to the TaskBar)
  • Right-click Jumplist with Recent Shows
  • Play the show with Task Buttons from the Preview
  • Previous, Next, Play, Pause.
  • Minimal code

Plus, I'm lousy at JavaScript. Here's what it looks like in action (as an animated GIF, because those are coming back in style, didn't you hear?) for those of you who don't want to try it yourselves.

Here's the general idea as code.

On the home page there is a list of all the episodes, created from a for loop going through the database of shows. I just added a non-preload audio element next to the div for each show:

<audio id="audio-2-0-4-taste" preload="none" src="http://traffic.libsyn.com/devlife/204-Taste.mp3"></audio>
<div class="episode">
<a href='post/2-0-3-education' class="showlink">
<img src="Images//203-lead.png"
alt="2.0.3 Education" width="320" height="220"/>
<div class='title'>
<p>2.0.3 Education</p>
<div class='description'>
<p>Scott and Rob talk to two developers about the role education has played in their careers</p>
</div>
</div>
</a>
</div>

Every show has an href with a class "showlink." I collect them all and give them to my jQuery plugin:

<script>

$(function(){
$(".showlink").IE9PodcastPlayer();
});
</script>

Comments appreciated as I'm still not awesome at jQuery and JavaScript. There are some options/settings that can be passed in, but I set some defaults that work for me since I did it for this one site.

Basically, I add some buttons, setup click events to go forward and backward through the list of shows passed in.

(function( $ ){
$.fn.IE9PodcastPlayer = function(options) {

var settings = {
'playerStatusDiv' : '#podcastStatus',
'currentShowIndex' : 0
};

this.each(function() {
// If options exist, lets merge them
// with our default settings
if ( options ) {
$.extend( settings, options );
}
});

function initButtons() {
try {
// Developer sample code
if(window.external.msIsSiteMode()) {
// Add buttons listener
document.addEventListener('msthumbnailclick', onButtonClicked, false);

// Add buttons
btnPrev = window.external.msSiteModeAddThumbBarButton('/images/prev.ico', 'Previous');
btnPlayPause = window.external.msSiteModeAddThumbBarButton('/images/play.ico', 'Play');
btnNext = window.external.msSiteModeAddThumbBarButton('/images/next.ico', 'Next');

// Add styles
stylePlay = window.external.msSiteModeAddButtonStyle(btnPlayPause, '/images/play.ico', "Play");
stylePause = window.external.msSiteModeAddButtonStyle(btnPlayPause, '/images/pause.ico', "Pause");

// Show thumbar
window.external.msSiteModeShowThumbBar();
}
}
catch(e) {
// fail silently
}
}

function onButtonClicked(e) {
var btnText = "",
lastIndex = 0;

if (e.buttonID !== btnPlayPause) {

switch (e.buttonID) {
case btnPrev:
btnText = "Previous";
settings.currentShowIndex--;
if (settings.currentShowIndex < 0) { settings.currentShowIndex = 0 };
break;
case btnNext:
btnText = "Next";
lastIndex = $('audio').length-1;
settings.currentShowIndex++;
if(settings.currentShowIndex >= lastIndex) { settings.currentShowIndex = lastIndex };
break;
}

stopAll();
$(settings.playerStatusDiv).text(btnText).fadeIn('slow',
function(){playPause(settings.currentShowIndex)}
);
} else {

playPause(settings.currentShowIndex);

}
}

function stopAll() {
$('audio').each(function(){
try {
$(this)[0].pause();
}
catch(e) {
// fail silently
};
});
$(settings.playerStatusDiv).hide();
}

function playPause(podcastID) {
var player = $('audio')[podcastID];
if(player.paused)
{
player.play();
$(settings.playerStatusDiv).text("Playing...").fadeIn('slow');
}
else
{
player.pause();
$(settings.playerStatusDiv).text("Paused").fadeIn('slow');
}

updatePlayPauseButton(podcastID);
}

function updatePlayPauseButton(podcastID) {
var player = $('audio')[podcastID];
try {
if(window.external.msIsSiteMode()) {
if (player.paused)
window.external.msSiteModeShowButtonStyle(btnPlayPause, stylePlay);
else
window.external.msSiteModeShowButtonStyle(btnPlayPause, stylePause);
}
}
catch(e) {
// fail silently
}
}

function addSite() {
try {
window.external.msAddSiteMode();
}
catch (e) {
alert("This feature is only available in Internet Explorer 9. Bummer.");
}
}

initButtons();
updatePlayPauseButton(0);

if(location.pathname == "/")
{
window.external.msSiteModeClearJumpList()
window.external.msSiteModeCreateJumplist('Episodes');

//This is the stuff we selected and passed in!
this.filter(":lt(10)").each(function() {
window.external.msSiteModeAddJumpListItem($(this).text().substring(41,60), this.href, '/favicon.ico');
});

window.external.msSiteModeShowJumplist();
}

};
})( jQuery );

The one part that I had to hack and still feel yucky about is at the end here with the filter() and substring(). See how I have the truncating of the show title hard coded? I was getting too much text when I passed in the whole <a/> element and children. I need to figure out a cleaner way to tell my plugin about my titles AND my audio files. Comments always appreciated.

If you REALLY want to see a jQuery plugin that will enable pinning and dynamic jump lists and isn't a piece of crap that I wrote, check out Pinify the jQuery Site Pinning Plugin or on NuGet.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

Summer 2011 Stats - Tailoring your Blog to your Readers' Browsers

August 22, '11 Comments [16] Posted in Blogging | Musings
Sponsored By

I was poking around in my Google Analytics and checking out some browsers stats as I wanted to make sure my new site redesign looked good on the browsers that matter to you all, Dear Reader. As I said in the redesign post (insert egotistical self-quote here. Seriously, who DOEs that?):

This new site design looks great in every awesome browser like Opera, IE9, Chrome, FireFox, and even all the way back to IE7. This site looks like a steaming pile of ass in IE6. As it should. 
- Scott Hanselman

Of course, as some internal naysayers are right to say, nayly, these charts are invalid as they are inside the HanselBubble where (they say, these sayers of nay) that we all run Chrome 99 and Beta Builds of Windows 7+1.

Nonsense! I say. It's always interesting to see what the user demographic is. With that, to the charts! First, the big one, Visits by Browser.

Chrome 35%, Firefox 28%, IE 22%, Safari 5% and Opera 2% with a bunch of random stuff that doesn't matter.

Interesting, almost looks like the Google Logo. You've got the big 3 in there almost with 1/3 each, but Chrome is ahead and IE and Firefox are more like 1/4 each. We made sure this blogs and works OK on the top 90% of the browsers that hit it and it should look OK on the rest.

Now, let's drill in. First, Chrome:

Chrome v12 and v13 take up over 50% of the pie

Looks like Chrome's auto-updating strategy sure works. I do worry myself, sometimes, that a security bug will sneak in to one of my thrice-daily Chrome updates, but we'll see. There's a balance between security and stability and new features. I hope they've found it. Everyone is basically on Chrome 12 or 13 it seems. I'm on 15 'cause I'm a canary.

What about Firefox?

Firefox 5 has 58% with 3.6 at 10% and 6 at 8.7%

Nearly 60% on 5.0, but 6.0 is doing pretty well given it JUST came out. I'm a little surprised at the Firefox 3.6 numbers. 10% is a little high, IMHO, but there are those that believe that was a great release and have stuck with it.

Next, Internet Explorer:

IE9 with 43%, IE8 with 42%, IE7 with 11% and IE6 with 2.44% 

Big numbers for IE9. I assume that's because Windows 7 isn't Windows Vista and folks are really liking it. Also possibly that folks who read this blog do Microsoft tech and have installed IE9 to be on the latest stuff. Kudos to Jeremy for getting this site's markup to work on IE9, IE8 and IE7. I'm afraid not for you guys on IE6. I'm sorry your corporation is oppressing you.

Screen Resolution

Y'all got some LARGE monitors and high-resolution displays! 90+% of my readers are running screens wider than 1024x768.

90% of my readers are running larger than 1024x768

One could argue from these numbers that I could target 1280 as a browser width, but with this new redesign Jeremy and I decided to support not only mobile (decently, although we need to take a deeper look at how to make the fonts more readable) but also really small resolutions on netbooks and stuff. Here's this site resized to about 800x700:

My site resized really small. I've removed the right rail and expanded the text.

And still this same blog, but now resized to about 1500x1500:

My site resized really big. Looks good.

Mobile

On the Mobile side, I had about 15k PV from an Android Browser version which is almost as much as Opera on my site, nearly 2%. This explains all the emails I've gotten about a bad cert issue that only shows up on Android. There's almost 6% Safari, and about half are iOS devices.

86% of the mobile browsers that hit my site have a touch screen. I don't have good percentage numbers but I do have this sorted list of visits by mobile device. I know that about 8% of the browsers that hit my site are mobile.

iPhone 21k Visits, iPad 17k Visits, Unknown 6k, iPod Touch 1.4k

I don't obsess over these stats; I maybe look at them twice a year or when making large decisions. It IS useful for you, Dear Reader, to take a look at your own readership. Perhaps they are Cupcake baking Moms, or Linux Hackers, or Fly-fishing Retirees. What browsers do they run? What resolutions are their screens? How fast are their connections? Does your website or blog know who your audience is and cater to not only their minds but also their browsers?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

How to REALLY hurt yourself with PSEXEC - Deleting the Undeletable Registry Key and More

August 19, '11 Comments [19] Posted in Tools
Sponsored By

A while back I installed some crazy open source network adapter virtualization software. Don't ask. It was a bad idea and I happily clicked Yes Yes Yes, Oh Yes, I Know, Hurt Me, Warning, Sure, Absolutely, Next, Next, Finish without a thought.

Later, things started acting wonky so I uninstalled it. Unfortunately, their uninstaller didn't so much "uninstall" the program as it "uninstalled the uninstaller then left everything around chilling." This left my system in a really weird state with half-connected phantom network adapters and I was getting some weird hangs. Each time I uninstalled these 7 (!) phantom network adapters, they'd be back on a reboot. Clearing removing them with the Device Manager UI wasn't working  for me.

At this point it was either time for a repave of the machine (basically a complete reinstall since I got it into a bad state) or running around in the registry and deleting these phantom adapters. Since I'm supposed to be technical, and since I was ready to reinstall it wouldn't hurt if I messed up the system, I fired up regedit.exe. I ran it as Administrator so I could have (I thought) the ability to delete anything.

I searched for the brand of the adapter I was wanting to delete, and when I started deleting stuff I was getting smacked with
"Access Denied." What?!? I'm the all powerful Administrator! sudo make me a sandwich!

Administrator isn't all powerful. There are other, deeper users like SYSTEM that have access to things Administrator (rightfully) doesn't. Considering that I am trying to removing low level network stuff it was good that I was being stopped.

That said, what's the best way to motivate an engineer? Tell him something  isn't possible. I hate getting Access Denied. Deny this, I say! So I turned to the SysInternals Tools, specifically PsExec.

If there was one tool that really "takes the safety off the gun," it's PsExec. You can hurt yourself and your system with PsExec in ways where you'll not realize until it's too late. There aren't enough words with big enough fonts and scary enough evocative stock photography to fully express how dangerous this tool is.

That said, here's cheerfully how to use it! Woohoo!

psexec -s -i regedit.exe

This has to be run as an Administrator. It This line says, "run this command as the SYSTEM account and let the process interact with the desktop." If I look at the Task Manager after running this command, check out regedit.exe:

Oy! Scary! See that RegEdit is running as SYSTEM

At this point, I was able to delete basically anything in the registry. One other way I could possibly delete keys that I couldn't as regular Administrator would be to launch Windows into Safe Mode and try editing from there.

Other awesome things you can do with PsExec include running processes remotely (which is kind what it's doing when I run it as SYSTEM, just all on on machine.) with a Service that gets automatically installed remotely and then runs the processes.

Here's how they do it (it's not a secret):

PsExec allows redirects of the input and output of a remotely started executable through the use of SMB and the hidden $ADMIN share on the remote system. With this share, PsExec uses the Windows Service control Manager API to start the PsExecsvc service on the remote system which creates a named pipe that PsExec communicates with. This named pipe is what allows for input/output redirection back to the system that launched PsExec.

For example:

psexec.exe \\REMOTECOMPUTER –i –u DOMAIN\username –p Password –d yourexe.exe

It's useful to remotely execute things, but it's even more useful to "terminal" (not quite like ssh) into systems like this:

psexec \\remotecomputer cmd.exe

Which means you can run commands on that remote computer as if you were there, in a SSHy way.

C:\>hostname
HANSELMANLAPTOP
C:\>psexec \\REMOTECOMPUTER -u DOMAIN\Administrator cmd.exe
C:\>hostname
REMOTECOMPUTER

Powerful but really useful stuff for when you really don't feel like RDP'ing (Remote Desktop'ing) into a machine to simply run a command.

Be careful out there!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

Hire and Pay a Designer and Be Happy

August 19, '11 Comments [76] Posted in Musings
Sponsored By

I got a haircut today. And I hired a designer to redo my blog theme. Both of these things put a pep in my step. I feel SO much better now.

I hear that there's supposed to be some kind of unicorn out there called a "devigner" that is both an amazing designer AND an amazing coder. I can only assume these mystery people are also pretty, well dressed, socially adept, and good at Halo. All I know is that I'm NOT that person.

When you meet a designer who really "gets" you, you've found a gem. I explained the feeling, the texture, the style, and the elements of what I wanted to designer Jeremy Kratz. Jeremy also designed Stack Overflow, in fact, although I only learned of that after I hired him. Isn't that an amazing coincidence? Funny how these things work out.

My old blog sucked     My new blog doesn't suck

Of course, if you are reading this post via RSS, you don't care and that's fine. If you are actually here on the site, you'll see that the new design has a light CSS media query and some specific widths and min-width's set to make the site look better on your mobile device or tablets, as well as low-resolution screens. Basically any browser that supports CSS media queries will have the right bar removed when resized to under 1024 pixels wide, while still looking pretty nice at sizes as small as about 700 wide.

Here's how you do a CSS media query. This is basic, certainly, but it's a clean way to get a good looking site on a large monitor, tablet, and most smart phones.

@media screen and (max-width:1024px)
{
#bars
{
padding-right: 0;
}

#mainbar
{
border-right: 0;
margin-right: 0;
padding-right: 0;
}
#sidebar
{
display: none;
}
}

There's a new navigation bar and search at the top with quick access to podcasts and videos both here and on Channel 9. Also check out the newly styled Calendar View if you want to see posts by month or year. There's also the ridiculous Archive of every post I've ever done by category. Comments are styled more cleanly and are easier to follow now as well. In the upper corner is the Feed, Twitter, Facebook, and Google+.

There's no way I could have done this. I'd been messing about here and there with my blog over the last few years trying to update it, remove some things, make it fresher. I was fooling myself into thinking that skills with CSS were design skills. I was fooling myself that having good taste and style meant I could design myself out of a web paper sack. Just because you like nice shoes doesn't mean you'd make a good shoe designer.

I was even thinking I'd have to move off of DasBlog onto something like WordPress. I wasn't sure if DasBlog (which is pretty long in the tooth right now) could handle it. The thing is, though, that this has been a pretty high traffic few weeks and DasBlog has pushed a million page views on this single little box since the first if July. It has survived two Slashdottings, a FireBalling, and being on the home page of HackerNews three times in that period. At the same time, I've watched sad little WordPress blogs get decimated when they got similar traffic, tipping over easily.

The fact is, I like DasBlog. It's got no database, it's fast as hell, and it's more flexible than I give it credit for. I was thrilled that Jeremy was able to create a new DasBlog-specific theme and I was able to drop it in and get it working in 5 minutes. I'm still tinkering here and there, but it's done and it's lovely.

This new site design looks great in every awesome browser like Opera, IE9, Chrome, FireFox, and even all the way back to IE7. It looks like a steaming pile of ass in IE6. As it should.

Coincidentally there's new other designs coming on some sites that I'm either directly involved in or orbiting in an advisory capacity, so expect to see some dramatic changes on sites like http://www.asp.net and mini-sites like http://microsoft.com/net over the next few days and weeks.

Thanks, Dear Reader. Now, go hug your friendly neighborhood designer. They are better at this than you.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is 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 SherWeb

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