Scott Hanselman

Making Junctions/ReparsePoints visible in PowerShell

May 12, '06 Comments [1] Posted in PowerShell | XML
Sponsored By

CmdjunctionI'm not sure if this the right way to do this, and I'm sure someone more clever will speak up but...I use a lot of junctions/reparsepoints in my development here, and I was used to seeing them called out when I typed "dir" from cmd.exe.

(Notice in the screenshot from cmd.exe that the "foo" directory is actually marked <junction>)

In PowerShell when you type Dir, you're getting an Array of System.IO.FileInfo/System.IO.DirectoryInfo objects that are displayed using some default formatting. That default Formatting is a combination of two things, a Type definition and a View definition.

First, there's no "Mode" property on a DirectoryInfo, so how is one being shown? In PowerShell you can "tack on" new properties with Script. This isn't derivation, as it's type extension. Adding to functionality, type-wide (within the context of PowerShell) for any .NET type. So, in C:\program files\Windows PowerShell\v1.0\ there's a file called types.ps1xml that has this little bit of info:

  <Type>
        <Name>System.IO.DirectoryInfo</Name>
        <Members>
            <ScriptProperty>
                <Name>Mode</Name>
                <GetScriptBlock>
                     here's where the magic happens...
                </GetScriptBlock>
            </ScriptProperty>
        </Members>
    </Type>

So, I created a personal My.Types.Ps1.xml file in my PSConfiguration folder that looked like this, where 1024 is the value of the [int]([System.IO.FileAttributes]::ReparsePoint) enum and "-band" is "bitwise and."

<Types>
   <Type>
        <Name>System.IO.DirectoryInfo</Name>
        <Members>
            <ScriptProperty>
                <Name>ExtMode</Name>
                <GetScriptBlock>
                    $catr = "";
                    if ( $this.Attributes -band 1024 ) { $catr += "j" } else { $catr += "-" };
                    if ( $this.Attributes -band 16 ) { $catr += "d" } else { $catr += "z" };
                    if ( $this.Attributes -band 32 ) { $catr += "a" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 1 )  { $catr += "r" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 2 )  { $catr += "h" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 4 )  { $catr += "s" } else { $catr += "-" } ;
                    $catr
                </GetScriptBlock>
            </ScriptProperty>
        </Members>
    </Type>
    <Type>
        <Name>System.IO.FileInfo</Name>
        <Members>
            <ScriptProperty>
                <Name>ExtMode</Name>
                <GetScriptBlock>
                    # Added the extra "-" to make sure things line up when FileInfos and DirectoryInfos are listed together
                    $catr = "-"; 
                    if ( $this.Attributes -band 16 ) { $catr += "d" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 32 ) { $catr += "a" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 1 )  { $catr += "r" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 2 )  { $catr += "h" } else { $catr += "-" } ;
                    if ( $this.Attributes -band 4 )  { $catr += "s" } else { $catr += "-" } ;
                    $catr
                </GetScriptBlock>
            </ScriptProperty>
          </Members>
     </Type>
</Types>

Then in my Microsoft.PowerShell_profile.ps1 (the file that gets executed every time I start a PowerShell) I added:

$profileTypes = $profile | split-path | join-path -childPath "My.Types.ps1xml"
Update-TypeData $profileTypes

Now, I'm not sure how to get dir (alias get-childitem) to use MY ExtMode rather than its own, but I think I'd need to override the default View Definition. It's a little tricky with these scoped XML files, because I originally named my new property "Mode" and was chastised for my efforts:

Microsoft.PowerShell, C:\Documents and Settings\shanselm\My Documents\PSConfigu
ration\My.Types.ps1xml(39) : Error in type "System.IO.FileInfo": Member "Mode"
is already present.

So, I named it "ExtMode." We shall see if there's a better way, until then, this function worked.

function edir
{
 dir | select ExtMode, LastWriteTime, Length, Name
}

and got me this output. Notice the "j" now in the ExtMode column.

Psedir

If I can't override dir's default view, I probably could also have removed the alias for "dir" and replaced it with my own:

if (test-path alias:\dir) { remove-item -force alias:\dir }
set-alias dir edir

but edir is fine for me now.

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

Forcing an update of a cached JavaScript file in IIS

May 12, '06 Comments [12] Posted in ASP.NET | DasBlog | Javascript
Sponsored By

JavascriptcachingThis might seem obvious to some folks, but to others it's not, so it's worth mentioning. Regardless, it's a good example of a "white box" attitude. Don't assume. Always assert your assumptions with good tests.

A client wanted to know how to 'force' a client to update some javascript that the browser had cached. The easy answer is "change the file."

Here's what happens with a single HTML file and a single JavaScript file, running locally on my machine. The main directory is set to "Expire Immediately" via IIS's properties dialog. That means "keep it fresh."

Underneath the main directory is a directory called /js that is set to expire in 7 days, as seen at right.

Here's an abridged HTTP Header view (via ieHttpHeaders) after hitting the page for the first time ever important stuff in bold.

GET /javascriptcachingtest/default.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Cache-Control: no-cache
Expires: Fri, 12 May 2006 19:03:59 GMT
Date: Fri, 12 May 2006 19:03:59 GMT
Content-Type: text/html
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
ETag: "b01be5ef575c61:df3"
Content-Length: 115

GET /javascriptcachingtest/js/test.js HTTP/1.1
Referer:
http://localhost/javascriptcachingtest/default.htm
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Cache-Control: max-age=604800
Expires: Fri, 19 May 2006 19:03:59 GMT
Date: Fri, 12 May 2006 19:03:59 GMT
Content-Type: application/x-javascript
Last-Modified: Fri, 12 May 2006 18:54:28 GMT
ETag: "50b1c1d4f775c61:df3"
Content-Length: 151

Note that both files were returned with HTTP 200 OK and the Javascript file had a Last-Modified header returned and an Expires date a week in the future. Now I'll hit F5 to refresh.

GET /javascriptcachingtest/default.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Cache-Control: no-cache
Expires: Fri, 12 May 2006 19:11:30 GMT
Date: Fri, 12 May 2006 19:11:30 GMT
Content-Type: text/html
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
ETag: "b01be5ef575c61:df3"
Content-Length: 115

GET /javascriptcachingtest/js/test.js HTTP/1.1
Referer:
http://localhost/javascriptcachingtest/default.htm
If-Modified-Since: Fri, 12 May 2006 19:03:59 GMT
If-None-Match: W/"50b1c1d4f775c61:df3"
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)
Host: localhost
Connection: Keep-Alive

HTTP/1.1 304 Not Modified
Server: Microsoft-IIS/5.1
Date: Fri, 19 May 2006 19:03:59 GMT
X-Powered-By: ASP.NET
Cache-Control: max-age=604800
Expires: Fri, 19 May 2006 19:03:59 GMT
ETag: "50b1c1d4f775c61:df3"
Content-Length: 0

JavascripttouchNote that the JavaScript file wasn't return (Content-Length: 0), the ETag is the same, and instead a 304 Not Modified was returned. This is the essense of client side caching and is something you should be exploiting (Sadly, fewer folks than you think do this) to get good throughput, efficiency and save on bandwidth costs.

Now, I'll "touch" the file - change it's modified date using the touch.exe I've got in my c:\utils folder (from http://unxutils.sourceforge.net/). Of course, there are other ways to do this, but you get the idea.

We've touched the file, so we'll hit F5 again to refresh:

GET /javascriptcachingtest/default.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Cache-Control: no-cache
Expires: Fri, 12 May 2006 19:11:30 GMT
Date: Fri, 12 May 2006 19:11:30 GMT
Content-Type: text/html
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
ETag: "b01be5ef575c61:df3"
Content-Length: 115

GET /javascriptcachingtest/js/test.js HTTP/1.1
Accept: */*
Referer:
http://localhost/javascriptcachingtest/default.htm
Accept-Language: en-us
Accept-Encoding: gzip, deflate
If-Modified-Since: Fri, 12 May 2006 19:03:59 GMT
If-None-Match: "50b1c1d4f775c61:df3"
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)
Host: localhost
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Cache-Control: max-age=604800
Expires: Fri, 19 May 2006 19:11:30 GMT
Date: Fri, 12 May 2006 19:11:30 GMT
Content-Type: application/x-javascript
Last-Modified: Fri, 12 May 2006 19:11:29 GMT
ETag: W/"804647dff775c61:df3"
Content-Length: 151

Notice that the browser asks for the JavaScript not only "by name" but also by date, and by ETag, a mostly unique identifier. The IIS server responds with an HTTP 200 OK, returning the freshly changed (in IIS's mind) file along with a new ETag and a new Last-Modified date.

As an aside, DasBlog does a pretty good job in its RSS Syndication Code of programmatically managing If-Modified-Since behavior. Remember that ASP.NET's <%OutputCache%> is SERVER-SIDE. It's not what we've just seen here. If you want this kind of behavior in your ASP.NET code, you'll need to do it manually in code. I'll post examples of that later.

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

Getting Things Done with SpeedFiler

May 11, '06 Comments [4] Posted in Reviews | Tools
Sponsored By

SpeedFilerOmar's turned me on to SpeedFiler, and I'm loving it.  I've talked about my personal system of organization before and my use of Getting Things Done.

In a nutshell, your Inbox is just that, your INbox. Not your "hold everything" box. If it's IN your INbox then it hasn't been processed. The basic processing rules are Do it, Drop it, Delegate it, or Defer it.

Everything else gets filed away for reference, and that's where SpeedFiler comes in. As with all useful niche utilities it is nicely priced at $19.95 (finally a company that understands pricing, unlike some.)

Outlook doesn't support "tagging" of Email messages (categories are a possibility, but they aren't very easy to set or search) so we have this big hierarchy of folders that typically run around in and drag-and-drop messages. Filing stuff isn't very quick if you're trying to bang through your inbox and get it down to zero messages.

MyoutlookfoldersSpeedFiler grabs Shift-Ctrl-V when a message is selected and pops up a dialog with the focus set to receive keyboard input immediately. So if I get a message that I want to blog about, I type:

Shift-Ctrl-V, @bl, Enter

and it's filed. As you're typing it looks up in the the list of folders an offers a list of folders that match you search string.

It's also got an unobtrusive toolbar that has dynamic buttons that change to show the names of the folders you've recently filed things to. That reduces filing to common areas down to one click.

There's a "Go To Folder" text box that you just type the name, or partial name, of a folder and hit enter and you're there.

I put an @sign at the beginning of a number of folders to make them group together. In the "pure" GTD world those are supposed to be "context" that I work in, like @home and @office, so I'm breaking the rules a bit. These contexts are more mental than physical but they work for me.

Some small complaints are there's a pause when Outlook starts up when SpeedFiler enumerates the folders. My folders don't change that much, not more than a few times a week, so if the program would just store the hierarchy in alternate storage and start up with that cached information, it could use a background thread to "catch up" later.

Itzy, the author of SpeedFilter has a blog called Email Overloaded.

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

If you love your family and your kids, do not play Elder Scrolls IV: Oblivion - It's that good

May 11, '06 Comments [18] Posted in Reviews | Gaming
Sponsored By

Elder Scrolls Oblivion Screenshot of a HouseDarn that Phil Haack, he wrote a post on Oblivion as I was writing one on a plane yesterday. If I only had connectivity at 30,000 feet. 

The Elder Scrolls IV: Oblivion is simply one of the best role-playing games ever made. [GameSpot]

No lie, no joke, it's that good.

Others are having similarly disturbing experiences:

Because the game is so damned addictive that when you're not playing, you wind up looking outside and thinking "man, that lighting model is really realistic!" before you remember you're not actually playing at that particular moment.

Even my wife has sat and played it with me for upwards of 30 minutes, which is a huge accomplishment if you ask me. However, "playing" the game with Mo meant "going for a stroll and looking at the scenery" which is something that you can really do for hours. I walked up a mountain over the course of two days, as I did the sun went down, dusk came, then the stars came out, then it started to rain. As we go towards the top of the mountain it began to snow.

There's been so many amazing moments. Once I found an empty camp and decided to sleep there and left my horse outside. I was awakened by a bandit whose bed I was apparently sleeping in. He had already killed my horse, the evil bastard. There's been times I've met a character I'd seen before and he's said "Oh, you again. How are you?"

There's such a sense that you are a part of a larger world that will (and does) continue without you. I've literally watched fights break out in front of me in a bar when some guy decided to pickpocket another. It wasn't scripted, it just happened.

One aside, to the mad fools who paid $500 for a new video card just to play this game, or just bought a whole new machine, seriously, please, get an Xbox360. Playing Oblivion on a 40" HDTV is just amazing. It's never crashed, no problems at all. I would be pretty frustrated if it were crashing often on a PC as some folks are reporting. (Interestingly enough, I've never had Guildwars crash on my PC...)

If you're playing Oblivion, leave a comment about something amazing or surprising that you've seen while playing the game. Here's an article called 64 Most Amazing Oblivion Moments.

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

Hanselminutes Podcast 17

May 11, '06 Comments [2] Posted in Podcast | ASP.NET | XML | Tools
Sponsored By

HanselminutesMy seventeenth Podcast is up. This episode is all about syndication.

We're listed in the iTunes Podcast Directory, so I encourage you to subscribe with a single click (two in Firefox) with the button below. For those of you on slower connections there are lo-fi and torrent-based versions as well.

Subscribe to my Podcast in iTunes

Our sponsors are PeterBlum and the .NET Dev Journal.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

  • Each show will include a number of links, and all those links will be posted along with the show on the site. There were 23 sites mentioned in this episode, some planned, some not.
  • The basic MP3 feed is here, and the iPod friendly one is here. There's a number of other ways you can get it (streaming, straight download, etc) that are all up on the site just below the fold. I use iTunes, myself, to listen to most podcasts, but I also use FeedDemon and it's built in support.
  • Note that for now, because of bandwidth constraints, the feeds always have just the current show. If you want to get an old show (and because many Podcasting Clients aren't smart enough to not download the file more than once) you can always find them at http://www.hanselminutes.com.
  • I have, and will, also include the enclosures to this feed you're reading, so if you're already subscribed to ComputerZen and you're not interested in cluttering your life with another feed, you have the choice to get the 'cast as well.
  • If there's a topic you'd like to hear, perhaps one that is better spoken than presented on a blog, or a great tool you can't live without, contact me and I'll get it in the queue!

Enjoy. Who knows what'll happen in the next show?

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.