<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Development</title>
        <link>http://mutable.net/blog/category/2.aspx</link>
        <description>thoughts about Windows and web development</description>
        <language>en-US</language>
        <copyright>David Jade</copyright>
        <managingEditor>blog@mutable.net</managingEditor>
        <generator>Subtext Version 1.9.5.176</generator>
        <item>
            <title>Using LINQ to Objects for recursion</title>
            <link>http://mutable.net/blog/archive/2008/05/23/using-linq-to-objects-for-recursion.aspx</link>
            <description>&lt;p&gt;One of the best new features of .NET 3.5 is LINQ. It's great for accessing data from databases like SQL Server but it's far more useful than just that. LINQ to objects is the killer feature in .NET 3.5 and it can change the way you write code for the better. &lt;/p&gt; &lt;p&gt;I am finding more cases where I can use LINQ for general data manipulation to simplify the code that I write. Coupled with Extension Methods it's a really powerful new way to write algorithms over your data types.&lt;/p&gt; &lt;p&gt;One of my favorite new tricks is to use LINQ to make recursion over a tree of objects simpler. There are many cases where this type of pattern is possible. For instance when working with controls in ASP.NET or Windows Forms each Control has a property named Controls, which is a collection of its child controls. If you need to perform some operation over all of them you can gather them all in just one LINQ statement similar to this:&lt;/p&gt; &lt;p&gt; &lt;/p&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 95%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; height: 64px; background-color: #f4f4f4; border-bottom-style: none"&gt;ChildControls = &lt;font color="#0000ff"&gt;from&lt;/font&gt; nested &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; 
                myform.Controls.Cast&amp;lt;System.Web.UI.Control&amp;gt;().Descendants(
                    c =&amp;gt; c.Controls.Cast&amp;lt;System.Web.UI.Control&amp;gt;())
                &lt;font color="#0000ff"&gt;select&lt;/font&gt; nested;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;The above code is a lot simpler to express than the recursion code that would have been necessary to work with each of the nested controls. And if you need to filter the child controls returned you can just add a LINQ Where clause (or any of the other LINQ operators).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: The Cast&amp;lt;T&amp;gt; function above is necessary since the Controls property return a non-generic IEnumerable. Cast&amp;lt;T&amp;gt; simple wraps the non-generic IEnumerable and returns a typed IEnumerable&amp;lt;T&amp;gt;.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The trick that makes this work is a function called Descendants, which is an extension method that I wrote. You can use it with any IEnumerable&amp;lt;T&amp;gt; collection to descend into those collections. It will descend into those collections by way of a Func&amp;lt;T, IEnumerable&amp;lt;T&amp;gt;&amp;gt; lambda that you supply. In the above example I pass it a lambda function that tells it how to retrieve the nested child controls. &lt;/p&gt;
&lt;p&gt;Here is that extension method:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; LinqExtensions
{
    &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; IEnumerable&amp;lt;T&amp;gt; Descendants&amp;lt;T&amp;gt;(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt; IEnumerable&amp;lt;T&amp;gt; source, 
                                                Func&amp;lt;T, IEnumerable&amp;lt;T&amp;gt;&amp;gt; DescendBy)
    {
        &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (T &lt;span style="color: #0000ff"&gt;value&lt;/span&gt; &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; source)
        {
            &lt;span style="color: #0000ff"&gt;yield&lt;/span&gt; &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;;

            &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (T child &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; DescendBy(&lt;span style="color: #0000ff"&gt;value&lt;/span&gt;).Descendants&amp;lt;T&amp;gt;(DescendBy))
            {
                &lt;span style="color: #0000ff"&gt;yield&lt;/span&gt; &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; child;
            }
        }
    }
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;You could just as easily write a lambda function to descend into a tree structure or any other data structure as long as it supports implemented IEnumerable&amp;lt;T&amp;gt;. Since you can supply the function you need to descend into the data structures, it makes Descendants a generic way to traverse into nested data structures.&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:c107d6c9-3289-4feb-8c23-92c4a9b2eb38" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/.NET" rel="tag"&gt;.NET&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/LINQ" rel="tag"&gt;LINQ&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/C#" rel="tag"&gt;C#&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/361.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2008/05/23/using-linq-to-objects-for-recursion.aspx</guid>
            <pubDate>Fri, 23 May 2008 20:06:22 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/361.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2008/05/23/using-linq-to-objects-for-recursion.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/361.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Vista's 'virtualized' folders</title>
            <link>http://mutable.net/blog/archive/2008/03/31/vistas-virtualized-folders.aspx</link>
            <description>&lt;p&gt;With Windows Vista Microsoft has changed where user account profile and program data gets stored. Instead of storing all profile data under "c:\Documents and Settings\" as they did with Windows XP, things are now split between "c:\Users\" and "c:\ProgramData\" (on a standard Vista installation). In order for older programs that hard-coded these directories names (instead of using the proper API to find them) will continue work, Microsoft has used features of the NTFS file system, specially junction points and symbolic links, to create virtual folders that look like the older locations but in reality point to the new locations. This virtualization scheme is pretty complex and for the most part hidden from regular standard users, but as a programmer and system administrator it is sometimes handy to know how these folders have been constructed. &lt;/p&gt; &lt;p&gt;I have created this diagram that maps out these junction points and symbolic links to their physical directories. I have also include information on the virtualized folders that get used when programs attempt to store data in 'c:\Program Files\'  or 'c:\Windows\'.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p align="center"&gt;&lt;a href="http://mutable.net/blog/images/mutable_net/blog/Vista%20System%20Volume.pdf" target="_blank"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="608" alt="Vista System Volume" src="http://mutable.net/blog/images/mutable_net/blog/WindowsLiveWriter/Vistasvirtualizedfolders_CBB0/VistaSystemVolume.gif" width="472" border="0" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p align="center"&gt;&lt;a href="http://mutable.net/blog/images/mutable_net/blog/Vista%20System%20Volume.pdf" target="_blank"&gt;Click to download this diagram as a PDF file&lt;/a&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:80449490-5cbd-443f-8d2e-34213558963e" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/Windows" rel="tag"&gt;Windows&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/Vista" rel="tag"&gt;Vista&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/FileSystem" rel="tag"&gt;FileSystem&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/Virtualized" rel="tag"&gt;Virtualized&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/UserSettings" rel="tag"&gt;UserSettings&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/359.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2008/03/31/vistas-virtualized-folders.aspx</guid>
            <pubDate>Mon, 31 Mar 2008 22:05:03 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/359.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2008/03/31/vistas-virtualized-folders.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/359.aspx</wfw:commentRss>
        </item>
        <item>
            <title>An intelligent backup system for Windows, part 3</title>
            <link>http://mutable.net/blog/archive/2006/11/21/an-intelligent-backup-system-for-windows-part-3.aspx</link>
            <description>&lt;p&gt;This is the third in a series of articles about a new backup process I have implemented for my home network. In the &lt;a href="http://mutable.net/blog/archive/2006/11/14/An-intelligent-backup-system-for-Windows-part-2.aspx"&gt;previous article&lt;/a&gt; I covered a mirror backup process that maintains a storage-efficient backup history. In this article I'll cover the tools I used and the issues I had to overcome while using them. &lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Common tools and a not so common use of them&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Once I had decided to create a backup system that creates space-conserving &lt;a href="http://www.backup4all.com/mirror-backup.php" target="_blank"&gt;mirror backups&lt;/a&gt; by leveraging NTFS &lt;a href="http://en.wikipedia.org/wiki/Hard_link" target="_blank"&gt;hard links&lt;/a&gt;, I set out to make a simple prototype. It occurred to me that I already had a very good tool for copying data around, a free tool called &lt;a href="http://en.wikipedia.org/wiki/Robocopy" target="_blank"&gt;robocopy&lt;/a&gt; from the Windows sources kit. Robocopy is a very powerful file copying tool that can be configured in a multitude of ways, including the ability to copy files in backup mode, a special mode of file access that can be used to bypass file security for the purposes of backing up files. It is also faster and more reliable than the file copy tools that come with Windows and has a very good set of options to control which files to copy. However robocopy know nothing about the process of creating hard links to previous versions of files. This step I would have to do myself.&lt;/p&gt; &lt;p&gt;In searching for information on how to create hard links, it wasn't long before I ran across references to the &lt;a href="https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/fsutil.mspx?mfr=true" target="_blank"&gt;fsutil&lt;/a&gt; tool that is included in Windows XP and Windows Server 2003. Using this tool you can creating NTFS hard links from the command line. &lt;/p&gt; &lt;p&gt;Together with robocopy and a bit of creative CMD scripting, I was able to throw together a prototype that could create mirror backups while hard linking to the files that had not changed since the previous backup just like rsync did. I started by duplicating the directory structure of the old backup by using robocopy to copy just the directories. Next I used fsutil to hard linked copies of the previous backup files into the new directories. I did this by traversing the old backup directories and using fsutil to create hard links to each of the older files. Then I used robocopy to generate a list of the files that had changed since the last backup, including files that were no longer present. From that listing I then deleted those files from the newly created mirror backup. Finally, I used robocopy to copy over just the newer files into the new mirror backup. While it wasn't the most efficient method, it worked pretty well but it had one important limitation: fsutil only works on local disks. It was also a pretty hacky bit of CMD script since I had to do string manipulation to create the hard links. I had considered re-writing the whole process in C# but then something else popped on my radar.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;PowerShell, isn't that some sort of new gasoline?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;It was about this time that Microsoft released RC2 of &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx" target="_blank"&gt;PowerShell&lt;/a&gt; (which as just recently gone RTM). PowerShell is Microsoft's new administrative scripting language for the future. Besides be a very good replacement for command shell scripting and VBScript, it is also the new foundation of the management tools for the next version of Microsoft Exchange. It is an amazingly powerful scripting language, &lt;a href="http://arstechnica.com/guides/other/msh.ars/1" target="_blank"&gt;easily learned&lt;/a&gt;, easily extended, and is easily the more important tool I have learned in a long time. &lt;/p&gt; &lt;p&gt;PowerShell is different from other scripting languages because it is based on the concept of pipelining objects. Many scripting languages, including the native Windows shell, support pipeling text data from command to command. PowerShell is different however in that it pipelines complete .NET objects instead of just textual data. As full .NET objects, each object in the pipeline has state, properties, and methods. They can be passed as parameters to functions, extended dynamically, coerced into other types, and placed back into the pipeline. Functions in PowerShell can also be treated as objects allowing you to do some types of functional programming tasks that are not easily done in other .NET languages. It is a very powerful idea and my brief description doesn't even scratch the surface of the power that lies within PowerShell. It is all still very new to me but already I am finding many uses for it.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;Tip: Here's a PowerShell gotcha to keep in mind. Every expression in PowerShell that produces output places that output in the pipeline. This can lead to pretty weird debugging issue if you aren't careful. I had more than one case where a function was returning more than I wanted because I was calling a command that placed things in the pipeline without realizing it. There are two ways to avoid this however. One is to assign the output of commands to a variable and the other is to redirect the output to $null (i.e. do-something &amp;gt; $null).&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;PowerShell's object pipeline nature along with the rich set of built-in commands knows as &lt;a href="http://msdn2.microsoft.com/en-us/library/ms714395.aspx" target="_blank"&gt;cmdlets&lt;/a&gt;, makes for a perfect system for doing administrative computer tasks. There are cmdlets for accessing PowerShell &lt;a href="http://msdn2.microsoft.com/en-us/library/ms714460.aspx" target="_blank"&gt;providers&lt;/a&gt; such as the file system and the registry, accessing &lt;a href="http://www.microsoft.com/whdc/system/pnppwr/wmi/default.mspx" target="_blank"&gt;WMI&lt;/a&gt; object, COM objects, and the full 2.0 .NET framework. I've seen examples of everything from a simple file parsing scripts to a simple but complete HTTP server written in PowerShell in just a few lines of code. To me it appeared to be the perfect language for scripting a new backup process. However PowerShell does not offer support for creating NTFS hard links either. For this I would need to extend PowerShell.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Extending PowerShell through custom C# objects and P/Invoke&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Starting with Windows XP there is a new API for creating hard links, &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createhardlink.asp" target="_blank"&gt;CreateHardlink&lt;/a&gt;. In previous versions of Windows, creating hard links was somewhat of a black art. You had to use the complex and sparsely documented Win32 Backup API's. It could be done and there are examples of how to do it out there, but it was not for the faint of heart. The CreateHardlink API however solves that, making it almost trivial to create hard links on NTFS. Furthermore, unlike fsutil the CreateHardlink API fully supports creating hard links on remote network NTFS drives. PowerShell cannot easily call native API's on its own though. To do that, you need to extend PowerShell with a bit on .NET code.&lt;/p&gt; &lt;p&gt;PowerShell is very easy to extend. You can write complete cmdlets', objects that fully plug into the PowerShell pipeline framework or you can just create simple .NET objects that can be created and invoked thanks to PowerShell's ability to access the .NET framework. &lt;/p&gt; &lt;p&gt;Using C# and a bit of P/Invoke it was almost trivial to solve the problem of not being able to create hard links in PowerShell (and .NET) by writing a simple object that called the Win32 CreateHardlink API. Once that was done, I could easily create my new .NET object in PowerShell and use it to create all the hard links that I wanted. Now I could create a more complete backup script from the ground up using PowerShell.&lt;/p&gt; &lt;p&gt;If you'd like to access the CreateHardlink API in PowerShell or .NET, here is a C# code snippet to help you. Simply create a new class in a .DLL and add this method. I added this method as a static member since it does not require any state from the class. This also makes it very easy to call from PowerShell.&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:08689661-8123-41db-a2d6-6fc83ab14ee9" contenteditable="false" style="padding-right: 0px; overflow-y: hidden; display: inline; padding-left: 0px; float: none; overflow-x: auto; padding-bottom: 0px; margin: 10px; width: 490px; padding-top: 0px"&gt;&lt;pre style="background-color: silver"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #000000"&gt;[DllImport(&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;kernel32.dll&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;, SetLastError &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;&lt;span style="color: #000000"&gt;, CharSet &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; CharSet.Auto)]
&lt;/span&gt;&lt;span style="color: #0000ff"&gt;internal&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;static&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;extern&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;int&lt;/span&gt;&lt;span style="color: #000000"&gt; CreateHardLink(&lt;/span&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;&lt;span style="color: #000000"&gt; lpFileName, 
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;&lt;span style="color: #000000"&gt; lpExistingFileName, IntPtr lpSecurityAttributes);

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;static&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;void&lt;/span&gt;&lt;span style="color: #000000"&gt; CreateHardlink(&lt;/span&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;&lt;span style="color: #000000"&gt; strTarget, &lt;/span&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;&lt;span style="color: #000000"&gt; strSource)
{
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;if&lt;/span&gt;&lt;span style="color: #000000"&gt;(CreateHardLink(strTarget, strSource, IntPtr.Zero) &lt;/span&gt;&lt;span style="color: #000000"&gt;==&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #000000"&gt;0&lt;/span&gt;&lt;span style="color: #000000"&gt;)
    {
        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;throw&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;new&lt;/span&gt;&lt;span style="color: #000000"&gt; System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
    }
}&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To call this code from PowerShell, you simply load the .NET assembly and then call that static method on your class. Note that this will throw an exception if it fails so make sure you have a PowerShell trap handler somewhere in your script.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ddfdb99b-e2f1-4847-9f98-ff6b230dc681" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 10px; overflow: auto; width: 490px; padding-top: 0px"&gt;&lt;pre style="background-color: silver"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #008000"&gt;#&lt;/span&gt;&lt;span style="color: #008000"&gt; load the custom .NET assembly&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;[System.Reflection.Assembly]::LoadFrom(&lt;/span&gt;&lt;span style="color: #0000ff"&gt;'&lt;/span&gt;&lt;span style="color: #0000ff"&gt;YourLibrary.dll&lt;/span&gt;&lt;span style="color: #0000ff"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;)

&lt;/span&gt;&lt;span style="color: #008000"&gt;#&lt;/span&gt;&lt;span style="color: #008000"&gt; create a hard link&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;[YourLibraryName.YourClass]::CreateHardlink(&lt;/span&gt;&lt;span style="color: #800080"&gt;$Target&lt;/span&gt;&lt;span style="color: #000000"&gt;, &lt;/span&gt;&lt;span style="color: #800080"&gt;$Source&lt;/span&gt;&lt;span style="color: #000000"&gt;) &lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #800080"&gt;$null&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Whoops, that file is in use&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There was still one more issue to tackle before I could write a robust backup system, accessing files that are in use. Starting with Windows XP Microsoft introduced a new system for accessing files that are currently in use on Windows systems, the &lt;a href="http://technet2.microsoft.com/WindowsServer/en/library/2b0d2457-b7d8-42c3-b6c9-59c145b7765f1033.mspx?mfr=true" target="_blank"&gt;Volume Shadow Copy Service&lt;/a&gt; (VSS for short, but not to be confused with Microsoft's VSS source control system).&lt;/p&gt;
&lt;p&gt;One of the ideas behind VSS is that when requested, the OS will make a read-only copy of the drive, a snapshot frozen in time, available to a backup program. Other programs can continue to change the original disk files but this shadow copy, or snapshot will remain frozen and completely accessible to the program that created it. Furthermore when a backup program requests that a shadow copy is to be created, the OS can coordinate with shadow copy providers to ensure that the data on the disk is in a consistent state before the shadow copy is created. This further ensures that the files that the backup program has access are in a consistent enough state on the disk to be backed up. This is especially useful for files that are either always open or always changing like the system registry, user profiles, Exchange, or SQL databases. Once the backup program is finished with this temporary read-only shadow copy, it then releases it and it disappears from the system. By using the VSS system backup programs can gain access to every file on the drive even if they are exclusively in use by other programs. For me it was essential to use VSS with any backup process I implemented.&lt;/p&gt;
&lt;p&gt;There were a few tough problems though. On windows XP these VSS snapshots are &lt;em&gt;very&lt;/em&gt; temporary in that they only exist for as long as you hold a reference to them via COM. Once released, they auto-delete themselves. And unlike VSS on Windows Server 2003 they cannot be exposed as a drive letter for easy access. You have to access them via the native NT kernel's method of addressing NT namespace objects, the GLOBALROOT namespace. On XP when you ask the VSS service to create a snapshot, what you get is a NT GLOBALROOT path that looks like this: &lt;br /&gt;\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1. Unfortunately this is something that not even the native Windows command shell fully understands and if you try and access it from PowerShell or .NET you'll get an exception telling you that you really shouldn't be accessing internal NT paths in .NET. To solve this I would need another bit of custom code to extend PowerShell.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VSHADOW.EXE and exposing a snapshot as a drive letter&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;VSHADOW is a sample tool that is part of the VSS SDK. It is a command line interface to the VSS API. By using this tool you can create and release VSS snapshots at will. It even has a way around the COM auto-destruction of snapshots on Windows XP by allowing you to call an external program once the snapshot has been created so that you can access the snapshot while VSHADOW is still keeping it alive. It will even create a set of environment variables for you to let you know the names of the GLOBALROOT shadow copies that it has created. This still didn't solve my problem of not being able to access them via PowerShell though (or robocopy for that matter) but having this source code was a good start.&lt;/p&gt;
&lt;p&gt;All physical devices in Windows like hard drives exist in the GLOBALROOT namespace. It is only through device name mapping that we can access them via their friendly DOS names like C:, D:, etc.... Normally the OS creates these device mapping automatically at start up or whenever a new device has been connected. VSS snapshots however don't automatically get recognized and mapped. Mapping a friendly name to a VSS snapshot has to be done by directly using the win32 &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/definedosdevice.asp" target="_blank"&gt;DefineDeviceCreate&lt;/a&gt; API. By using this API you can create and remove DOS device mappings to VSS snapshots on the fly even on Windows XP. But since VSS snapshots are temporary you have to manage them carefully or the system could become unstable.&lt;/p&gt;
&lt;p&gt;Creating a VSS snapshot and mapping it to DOS device names is well beyond what I wanted to try to do in C#. Luckily for me, the VSHADOW C++ source code was written in a very reusable manor and I could easily reuse it by wrapping a COM object around it.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The not so nice COM interop experience with .NET 2.0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Creating a snapshot is not the simplest of procedures. You have to query for the list of VSS writers, map them against the target volume, determine which ones to include in the process, and finally request that the snapshot be created. You have to hold on to the VSS COM interface to keep the snapshot alive on XP for the duration of its use. When you are done, you have to release it in a controlled manor or the VSS system can completely degrade and require a system restart to recover from in most cases. It is also not the fastest process either, something that would come back to bite me later. However the VSHADOW source which is written in C++, was written in such a way that it made it very easy to turn into a COM object using ATL. It was as simple as creating a new ATL COM object project in Visual Studio and including the core VSHADOW sources file into the project. Once I had it building as a COM object it didn't take me long to put a .NET friendly interface on this new COM object that exposed methods to create and destroy VSS snapshots as well as map them to DOS device names.&lt;/p&gt;
&lt;p&gt;PowerShell has native support for create and calling COM objects that is even easier than in other .NET languages. There is no need to create .NET interop classes, you just dynamically creating the COM object and use it much like you would in VBScript. Once I created my new VSS COM object it was trivial to create VSS snapshots on the fly and map them to DOS device names using PowerShell. With my new VSS COM object I now had complete access to VSS snapshots from any tool that could access a standard drive. It has some limitations but for this backup process it works very well.&lt;/p&gt;
&lt;p&gt;Releasing the VSS snapshot in PowerShell however was another story. There is no clean way that I can find to force a created COM object to be released in PowerShell. You have to wait for the .NET garbage collector to do its thing which is usually not until the PowerShell process is exiting. My new COM object had its clean-up code in the COM object's Release method so that when it was released it would clean up the VSS state in the proper way, ensuring that the system remained stable. Unfortunately for me relying on a COM object's Release method to work during the .NET shutdown process proved to be one huge headache.&lt;/p&gt;
&lt;p&gt;After many, many hours of debugging and not really believing what I was seeing I finally had to accept what was going on. From what I was seeing and from the research I have done it is my understanding that Finalizers in .NET, which are called when an object is being destroyed and which are also responsible for calling a COM object's Release method in PowerShell, are not guaranteed to complete when a process shuts down. Usually this is not a problem as the process is going away anyway. It is a problem however when you have native resources to release. &lt;/p&gt;
&lt;p&gt;What I was seeing and not believing for literally hours and hours was that in the middle of my COM object's Release method the PowerShell process would just exit normally. No exceptions, no faults, nothing - just poof it's gone. And every time that it did this it would leave the VSS system in such a state that the machine had to be restarted because I was never given the chance to properly execute the VSS clean-up code which can be a lengthy process. It seems that the PowerShell shutdown process was timing out my clean-up code. It was a complete mess and still one that I cannot believe is acceptable but apparently to the folks who created .NET it is (you can read about it &lt;a href="http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx" target="_blank"&gt;here&lt;/a&gt; in way more detail than anyone should have to know. Just search for "timeout" and "watchdog" on that page). The thought that external native code can have the plugged pulled just blows me away.&lt;/p&gt;
&lt;p&gt;The fix was rather simple once I realized that I cannot count on my COM object's Release method to always complete. I had to move all critical clean-up code and put it in a public method that my PowerShell script would always call. Luckily PowerShell has pretty decent error handling and it wasn't too hard to ensure that I always called the clean-up method on my COM object before PowerShell normally terminates. I'm still not thrilled about this though. I would have preferred that my COM object be allowed to clean up after itself as necessary.&lt;/p&gt;
&lt;p&gt;The moral of this story is that you are responsible for all complex clean-up even when calling native code. Don't depend on the .NET framework to always play nice. &lt;/p&gt;
&lt;p&gt;Now that I had this behind me I had all the pieces that I needed: a robust file copy tool, a powerful scripting language, the ability to create hard links, and full access to Volume Shadow Copy snapshots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;&lt;em&gt;In part four I'll cover the process overview and implementation details of creating the intelligent mirror backup process that I choose to be the foundation of my new backup strategy.&lt;/em&gt;&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:3539fe3c-143a-4db5-a798-5bac82714423" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us tags: &lt;a href="http://del.icio.us/popular/Windows" rel="tag"&gt;Windows&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/PowerShell" rel="tag"&gt;PowerShell&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/backup" rel="tag"&gt;backup&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/VSS" rel="tag"&gt;VSS&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/rsync" rel="tag"&gt;rsync&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/32.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2006/11/21/an-intelligent-backup-system-for-windows-part-3.aspx</guid>
            <pubDate>Tue, 21 Nov 2006 19:34:08 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/32.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2006/11/21/an-intelligent-backup-system-for-windows-part-3.aspx#feedback</comments>
            <slash:comments>14</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/32.aspx</wfw:commentRss>
        </item>
        <item>
            <title>An intelligent backup system for Windows, part 2</title>
            <link>http://mutable.net/blog/archive/2006/11/14/an-intelligent-backup-system-for-windows-part-2.aspx</link>
            <description>&lt;p&gt;This is the second in a series of articles about a new backup process I have implemented for my home network. In the first &lt;a href="http://mutable.net/blog/archive/2006/11/13/An-intelligent-backup-system-for-Windows-part-1.aspx"&gt;article&lt;/a&gt; I covered some background information and why I choose a non-traditional backup process for my network. In this article I'll give an overview of mirror backups, their problems, and the ways in which they can be improved. I'll also talk about rsync and why for me, it was not the right tool for creating backups on Windows.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;A more intelligent mirror backups process&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.backup4all.com/mirror-backup.php" target="_blank"&gt;Mirror backups&lt;/a&gt; have several advantages over the traditional backup process. Like a full backup they are a complete snapshot in time of the data being backed up but since they are typically stored on randomly accessible media (i.e. a disk or online), accessing any part of the backup set is easily accomplished. However, also like a full backup you usually have to copy each file to create the mirror even if that file has not changed since the last backup. This makes storing backup history both a time consuming process and an inefficient use of storage space. You can of course just copy the changed files into an existing mirror backup but then you lose any file change history from the last backup set.&lt;/p&gt; &lt;p&gt;While researching backup solutions I came across a UNIX utility called &lt;a href="http://samba.anu.edu.au/rsync/" target="_blank"&gt;rsync&lt;/a&gt;. Rsync is a tool that was designed to efficiently create file set mirrors. It has a large and flexible set of features including its own network protocol that can efficiently transfer just the differences between versions of a file when creating a new mirror. The most appealing feature to me however is its ability to conserve storage space while still creating complete mirror backups. It does this by leveraging a feature called hard links which are found on modern files systems (including the Windows NTFS file system).&lt;/p&gt; &lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Hard_link" target="_blank"&gt;Hard links&lt;/a&gt; are a method by which the operating system separates the storage of the file's actual data from the file's directory entry. In other words, a file in a directory is really just a named pointer to the file's physical data which is stored somewhere else on the hard disk. Every file stored on a hard disk is stored in this way. Most of the time, there is a one-to-one mapping with each physical file's data having only one named directory entry or file name. However, some file systems allow you to create additional named references to the file's physical data. These reference can exist in the same directory or in any other directory on the file system. Furthermore, they can have the same or a completely different file name. When this is done they share all aspects of the actual data, including the file's attributes such as security settings, creation time, and the last modified time. If you change the file's data or attributes via one reference then all the other references will reflect those changes immediately. You can even delete the original reference to the data and the others will continue to live on. It is only after all references that point to the physical data have been deleted that the actual file data is deleted. &lt;/p&gt; &lt;p&gt;What makes rsync really special is that it can leverage this very powerful file system feature when creating mirror backups. It does this be examining the files being backed up, comparing each file against the copy in the previous mirror backup. Then for each file that it finds to be identical, instead of copying that unchanged file into the new mirror backup it simply creates a new hard link to the file as it exists in the previous mirror backup. This is much, much faster than re-copying the file itself but it essentially does the same thing. It makes the unchanged version of the file available in the newly created mirror backup. Furthermore it is also very space efficient since the file's data is still only stored once physically but it is accessible from every mirror backup that it is hard linked into. &lt;/p&gt; &lt;p&gt;By leveraging hard links, it also allows for another interesting feature. You can physically delete older mirror backups without affecting any of the new mirror backups that may be referencing unchanged data with these backups. Files that have other references will stay alive in those newer mirrors while older files that only have a reference in the mirror being deleted will be destroyed. This makes managing the amount of backup history to keep very easy; you can just delete older mirrors when you no longer want the history they contain. &lt;/p&gt; &lt;p&gt;Together these two features effectively allow you to break the traditional full/incremental backup cycle forever while keeping the best parts of each. You can have several full mirror backups available to preserve file history with no physical duplication of data for files that remain unchanged while still running a process that only copies newer files like an incremental backup. It is the best of both full backups and incremental backups combined in one logical backup process. You can read more about using rsync to create hard linking mirror backups &lt;a href="http://www.mikerubel.org/computers/rsync_snapshots/" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;If rsync is for UNIX, what's this got to do with backing up Windows?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Rsync is a very UNIX-y tool and I am pretty firmly in the Windows camp. However you can still run rsync on Windows using the &lt;a href="http://www.cygwin.com/" target="_blank"&gt;cygwin&lt;/a&gt; system. Cygwin is a software translation layer that was developed to allow certain types of UNIX programs to work on Windows systems. It has been around for a long time and is very functional and reliable. I used cygwin quite successfully to play with rsync on both Windows XP and Windows Server 2003. However for backing up data from Windows' NTFS file system there are some issues.&lt;/p&gt; &lt;p&gt;Windows' NTFS file system supports a wide array for features including security attributes, NTFS alternative data streams, sparse files, encrypted file, and much more. Cygwin and thus rsync know nothing about these additional file attributes since the translation layer was designed to make Windows seem like just another UNIX system. What this means is, that rsync can only copy the aspects of the files that it knows about, i.e. the file's regular data. So if you use rsync to make mirror backups on Windows you will essentially lose all of this other and sometimes important file and directory information (you also lose this information when simply copying files to a CD/DVD as well). This information however is usually preserved when doing traditional backups or when copying files between NTFS locations including across Windows networks. What was needed was a way for rsync to preserve this information on Windows.&lt;/p&gt; &lt;p&gt;I debated for quite some time on whether or not to dive in and start hacking away at the rsync source code to try to teach it more about the Windows NTFS file system and this additional file data. I even went so far as to patch cygwin to teach it about Windows' GLOBALROOT device paths, a feature that was essential in order to use Windows' &lt;a href="http://technet2.microsoft.com/WindowsServer/en/library/2b0d2457-b7d8-42c3-b6c9-59c145b7765f1033.mspx?mfr=true" target="_blank"&gt;Volume Shadow Copy Service&lt;/a&gt; (VSS) with rsync (this patch is now part of the cygwin CVS source, Btw) . However in the end I decided it would probably be much more effort to update rsync than it would be to write my own backup process. Rsync has a lot of features that I do not need and even though my new backup process was likely to be missing some of the features that I really liked about rsync, my new process would still contain the core method by which rsync creates space-conserving mirror backups.&lt;/p&gt; &lt;p&gt;This decision to start fresh on my own backup tool coincided with the time that I had started to play with another tool, Microsoft's recently released &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx" target="_blank"&gt;PowerShell&lt;/a&gt; scripting language. It wasn't long before I realized the PowerShell was a perfect fit for this type of problem.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;em&gt;In part three I'll cover the tools I used to implemented an intelligent mirror backup process in Windows using robocopy, Microsoft's PowerShell scripting language, C#, the Volume Shadow Copy Service, C++ and a little bit of COM interop.&lt;/em&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:7d6c7cb8-4be9-4c05-960d-b622fb5c1448" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us tags: &lt;a href="http://del.icio.us/popular/Windows" rel="tag"&gt;Windows&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/PowerShell" rel="tag"&gt;PowerShell&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/backup" rel="tag"&gt;backup&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/VSS" rel="tag"&gt;VSS&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/rsync" rel="tag"&gt;rsync&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/25.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2006/11/14/an-intelligent-backup-system-for-windows-part-2.aspx</guid>
            <pubDate>Wed, 15 Nov 2006 04:59:30 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/25.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2006/11/14/an-intelligent-backup-system-for-windows-part-2.aspx#feedback</comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/25.aspx</wfw:commentRss>
        </item>
        <item>
            <title>PowerTips for PowerShell</title>
            <link>http://mutable.net/blog/archive/2006/10/05/powertips-for-powershell.aspx</link>
            <description>&lt;p&gt;I'm certainly not a PowerShell expert but I have been finding my way around it lately. Once I got a few interesting scripts put together and saved to a file the next thing I ran up against was how do I launch these without loading them into the interactive environment by hand? By default when you install PowerShell it associates script files (.ps1 files) with notepad. Great for editing, not so great if you want to execute them. My guess is that after the 'Monad/PowerShell virus' story a year back or so that Microsoft got a little too freaked out to just let these things launch when clicked on. Unfortunately this decision also makes it more difficult to schedule a PowerShell script in the task scheduler too. To top it all off it's not quite as simple as just passing your script as a command line argument to PowerShell either. There are two separate steps necessary to enable you to launch PowerShell scripts from the Windows shell.&lt;/p&gt; &lt;p&gt;First you have to set up a file association in the Windows shell to change the default behavior for .ps1 files. However the PowerShell command line doesn't know what to do with a script file. By default it simple takes statements to execute. We can leverage this to our advantage though and construct a statement to cause our script file to be loaded and executed. That statement looks something like this:&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:baedc27e-ae94-4873-a073-44525bbc4d37" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; width: 473px; padding-top: 0px"&gt;&lt;pre style="background-color: white"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #000000"&gt;powershell &lt;/span&gt;&lt;span style="color: #000000"&gt;-&lt;/span&gt;&lt;span style="color: #000000"&gt;command &lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;amp; 'MyScript.psa1' &lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you update the file association with this you can then launch script files from the command line or by clicking on them. You can use the Tools | Folder Options dialog to do this but why not use PowerShell instead? Here are two simple lines of code to update your system registry to tell it how to execute .ps1 script files. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This of course updates your system registry so you should back things up first.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E7:cf412ca8-32cb-491b-aba2-818432d98934" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; width: 481px; padding-top: 0px"&gt;&lt;pre style="background-color: white; word-wrap: break-word"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #000000"&gt;new-item Registry::hkey_classes_root\microsoft.powershellscript.1\shell

new-item Registry::hkey_classes_root\microsoft.powershellscript.1\shell\open

new-item Registry::hkey_classes_root\microsoft.powershellscript.1\shell\open
\command -value ('"' + $PSHOME + '\powershell.exe" -command "&amp;amp; ''%1''"')
&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you run these two PowerShell lines and then try and click on a .ps1 script file you'll see that we're not quite there yet. PowerShell has an execution policy that by default is set to "Restricted." In restricted mode no scripts are allowed to execute, only interactive commands may be executed. By using the PowerShell "set-executionpolicy" cmdlet you can change it to something more sensible like RemoteSigned which allows all locally generated scripts to execute but will only allow downloaded scripts to execute if they have been signed by a trusted source. In PowerShell you can execute the "help  about_signing" command for more info on this.&lt;/p&gt;
&lt;p&gt;Once these two steps are done you can now launch PowerShell scripts directly without having to start up the interactive environment first. In addition you can also use this method to schedule PowerShell scripts in the task scheduler.&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:8af3dd7b-340c-4dd2-926c-cee1edc125ec" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us tags: &lt;a href="http://del.icio.us/popular/Windows" rel="tag"&gt;Windows&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/PowerShell" rel="tag"&gt;PowerShell&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/19.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2006/10/05/powertips-for-powershell.aspx</guid>
            <pubDate>Thu, 05 Oct 2006 20:23:08 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/19.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2006/10/05/powertips-for-powershell.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/19.aspx</wfw:commentRss>
        </item>
        <item>
            <title>How to easily script any Windows application</title>
            <link>http://mutable.net/blog/archive/2006/09/29/how_to_easily_script_any_windows_application.aspx</link>
            <description>&lt;p&gt;Many larger applications having scripting capabilities built in to allow you to automate repetitive tasks but what do you do if you need to script a repetitive task for an application that doesn't have a scripting or macro language built in?  You could use VBScript and the SendKeys function. This works for simple things but it isn't always robust enough. What if you need to do something more complex, for instance: launch an application, wait for the main window to open, resize the window, send some keystrokes, and then click on a specific UI element? Unfortunately VBScript and its set of built-in functions don't always cut it when you need to move beyond SendKeys. &lt;/p&gt; &lt;p&gt;When your needs go beyond what VBScript can do natively you might want to take a look at AutoIt. AutoIt is a small suite of tools, the main component being a stand-alone scripting language. What sets AutoIt apart from other scripting languages is that it is targeted at GUI application scripting and its built-in set of functions enable the finer level of control lacking in VBScript and other scripting languages. The AutoIt scripting language is very VB-like and easy to pick up if you're already familiar with VB. AutoIt has a rich feature set including (taken from the AutoIt web site):&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Provide a general-purpose scripting language for all Windows versions  &lt;/li&gt;&lt;li&gt;Simulate keystrokes (supports most keyboard layouts)  &lt;/li&gt;&lt;li&gt;Simulate mouse movements and clicks  &lt;/li&gt;&lt;li&gt;Move, resize and manipulate windows  &lt;/li&gt;&lt;li&gt;Interact directly with "controls" on a window (set/get text from edit controls, check boxes and radio buttons, select items in drop-down lists, etc.)  &lt;/li&gt;&lt;li&gt;Create complex user interfaces (GUI's)  &lt;/li&gt;&lt;li&gt;Work with the clipboard to cut/paste text items  &lt;/li&gt;&lt;li&gt;Provide a scriptable RunAs function for Windows 2000/XP/2003 &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;However what is really interesting to me is that AutoIt also includes a scriptable COM object essentially making its core application scripting capabilities available to any other COM capable languages including VBA, VBScript, PowerShell, or .Net. The COM interface on this object seems well thought out and is very easy to use. I started using AutoIt for this feature as I needed to automate an application from within Outlook's VBA scripting. Using the AutoIt COM object I was able to write Outlook VBA code to control another application including starting the application, waiting for its main window to launch, and intelligently sending data to the right parts of its UI. &lt;/p&gt; &lt;p&gt;And best of all, AutoIt is free. It can be found &lt;a href="http://www.autoitscript.com/autoit3/" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Note: &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx"&gt;PowerShell&lt;/a&gt; is on the horizon and by all accounts it should be a very powerful tool but it also seems to lack some of this finer level of control for scripting GUI applications. You can however access the AutoIt COM object as well as the full .Net library.&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt; &lt;/p&gt;&lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:935c0892-18c1-4455-b4c1-4a166e99fd81" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us tags: &lt;a href="http://del.icio.us/popular/windows" rel="tag"&gt;windows&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/PowerShell" rel="tag"&gt;PowerShell&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/AutoIt" rel="tag"&gt;AutoIt&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/automation" rel="tag"&gt;automation&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/scripting" rel="tag"&gt;scripting&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/14.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2006/09/29/how_to_easily_script_any_windows_application.aspx</guid>
            <pubDate>Sat, 30 Sep 2006 04:35:50 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/14.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2006/09/29/how_to_easily_script_any_windows_application.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/14.aspx</wfw:commentRss>
        </item>
        <item>
            <title>How to hyperlink to Microsoft Outlook messages</title>
            <link>http://mutable.net/blog/archive/2006/09/02/how_to_hyperlink_to_microsoft_outlook_messages.aspx</link>
            <description>&lt;p&gt;First, a little context on why I wanted to do this in the first place. If you just want to get the source code then just scroll down to the "The source code".&lt;/p&gt; &lt;p&gt;A fair number of the things that make it on to my ToDo list are messages that I receive in Outlook. I've been using Outlook's flags to mark these items for follow up so that I could keep track of them. I use a different colored flag that denotes its own meaning as defined by me. This way I can flag an item using a action type (ToDo, Deferred, Waiting For, etc...). This has worked fairly well for me but once I had a large number of items flagged in Outlook (did I mention that I am a procrastinator?) it didn't really help me keep track of the things I needed to keep track of in any meaningful way. All I could even see was the complete list of flagged items but I couldn't break it down further into projects or context.&lt;/p&gt; &lt;p&gt;To overcome this limitation I've recently started managing my ToDo list outside of Outlook in a program called &lt;a href="http://MyLifeOrganized.net" target="_blank"&gt;MyLifeOrganized&lt;/a&gt; (aka MLO) . MLO allows you to drag/drop Outlook items into MLO's task list. When you do this it creates a new MLO task using the subject of the dropped Outlook item for the task name. However MLO does something else really smart when you drag/drop an Outlook item. It not only put the text of the Outlook item into the notes, it will also create a hyperlink that will open up the original Outlook item when clicked. It does this by using Outlook's URL syntax which looks something like this:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Outlook:&amp;lt;entry_id&amp;gt;&lt;/strong&gt; where &amp;lt;entry_id&amp;gt; is an Outlook Entry ID&lt;/p&gt; &lt;p&gt;Windows naturally understands this form of URLs. If you click on one it will cause Outlook to open the item referenced. This has allowed me to take Outlook items and create MLO items simply by dragging them to the MLO task list. In most cases this feature in MLO does exactly what I want, take an Outlook message that I need to follow up on and place it into my ToDo list. However sometimes this isn't exactly what I want. Sometimes I just want to place a link to Outlook items in the notes of an existing MLO item. Unfortunately MLO doesn't support this but there is a way to do it if you willing to do a little macro work in Outlook.&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 6/4/2007:&lt;/strong&gt; If you are using Office 2007 then you will probably need to enable the Outlook URL protocol handler so that hyperlinks to mail messages work.To do this requires editing the registry. You simple need to create these registry keys (substituting your installation paths of course):&lt;/em&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;li&gt;&lt;b&gt;HKEY_CLASSES_ROOT&lt;/b&gt;  &lt;ul&gt; &lt;li&gt;&lt;strong&gt;outlook&lt;/strong&gt;&lt;br /&gt;(Default) = &lt;a href="Outlook Folders"&gt;URL:Outlook Folders&lt;/a&gt;&lt;br /&gt;URL Protocol&lt;b&gt;=&lt;/b&gt; ""  &lt;ul&gt; &lt;li&gt;&lt;b&gt;DefaultIcon&lt;/b&gt;&lt;br /&gt;(Default) = "C:\PROGRA~1\MICROS~2\OFFICE12\OUTLLIB.DLL,-9403"&lt;/li&gt;&lt;/ul&gt; &lt;ul&gt; &lt;li&gt;&lt;b&gt;shell&lt;/b&gt;  &lt;ul&gt; &lt;li&gt;&lt;b&gt;open&lt;/b&gt;  &lt;ul&gt; &lt;li&gt;&lt;b&gt;command&lt;/b&gt;&lt;br /&gt;(Default) = "C:\PROGRA~1\MICROS~2\OFFICE12\OUTLOOK.EXE" /select "%1"&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/blockquote&gt; &lt;h3&gt;The source code&lt;/h3&gt; &lt;p&gt;Below is the source for a two VB macros that can be added to Outlook. These macros will loop over all of the currently selected messages, getting the subject and Outlook EntryID for each message. With these two pieces of information they then build a string of text with the message's subject and its Outlook URL, each on their own line. It extends this text string for each selected message and places the resulting text string on the clipboard. The end result is one block of text that contains the message subject followed by the Outlook URL for each selected message. This text can then be pasted into any document that understands hyperlinks. This includes the notes of MLO items as well as all of the other Microsoft Office applications. This macro will work with multiple items selected in the main Outlook window as well as from the opened window of a single Outlook message. To use it, simple invoke the CopyItemIDs() macro. You can bind this macro to a menu or toolbar button for easier access within Outlook.&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: &lt;/strong&gt;I should mention that if you are using Microsoft Exchange server, the message Entry ID can change on you and break any existing Outlook URLs. This unfortunately always happens if you move a message to another folder so if you plan on using this, only invoke this macro after you have moved the message to a new folder.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;&lt;strong&gt;Update: &lt;/strong&gt;There is just one more thing you must do before you run this script. You need to include a reference to FM20.dll, which is the Forms 2.0 library. This will allow you to use the DataObject to manipulate the clipboard. Thanks to 'Some Guy' who pointed this omission out.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt;&lt;div class="wlWriterSmartContent" id="57F11A72-B0E5-49c7-9094-E3A15BD5B5E7:543d21a2-3d87-451c-a2d3-d9ff81ec2ae2" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; width: 496px; padding-top: 0px"&gt;&lt;pre style="background-color: white"&gt;&lt;div&gt;&lt;!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt;&lt;span style="color: #0000ff"&gt;Sub&lt;/span&gt;&lt;span style="color: #000000"&gt; CopyItemIDs()
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Dim&lt;/span&gt;&lt;span style="color: #000000"&gt; myOLApp &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; Application
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Dim&lt;/span&gt;&lt;span style="color: #000000"&gt; myNameSpace &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; NameSpace
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Dim&lt;/span&gt;&lt;span style="color: #000000"&gt; currentMessage &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; MailItem
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Dim&lt;/span&gt;&lt;span style="color: #000000"&gt; ClipBoard &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;String&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Dim&lt;/span&gt;&lt;span style="color: #000000"&gt; DataO &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; DataObject
    
    &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; Housekeeping: set up the macro environment&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; myOLApp &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;CreateObject&lt;/span&gt;&lt;span style="color: #000000"&gt;(&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;Outlook.Application&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;)
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; myNameSpace &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; myOLApp.GetNamespace(&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;MAPI&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;)
    
    &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; Figure out if the active window is a list of messages or one message&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; in its own window&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;On&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Error&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;GoTo&lt;/span&gt;&lt;span style="color: #000000"&gt; QuitIfError    &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; But if there's a problem, skip it&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Select&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Case&lt;/span&gt;&lt;span style="color: #000000"&gt; myOLApp.ActiveWindow.Class
        &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; The active window is a list of messages (folder); this means there&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;        &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; might be several selected messages&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Case&lt;/span&gt;&lt;span style="color: #000000"&gt; olExplorer
            &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; build the clipboard string&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;            &lt;/span&gt;&lt;span style="color: #0000ff"&gt;For&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Each&lt;/span&gt;&lt;span style="color: #000000"&gt; currentMessage In myOLApp.ActiveExplorer.Selection
                ClipBoard &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; GetMsgDetails(currentMessage, ClipBoard)
            &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Next&lt;/span&gt;&lt;span style="color: #000000"&gt;
             
        &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; The active window is a message window, meaning there will only&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;        &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; be one selected message (the one in this window)&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;        &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Case&lt;/span&gt;&lt;span style="color: #000000"&gt; olInspector
            &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; build the clipboard string&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;            ClipBoard &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; GetMsgDetails(myOLApp.ActiveInspector.CurrentItem, _
                                         ClipBoard)
        &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; can't handle any other kind of window; anything else will be ignored&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;End&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Select&lt;/span&gt;&lt;span style="color: #000000"&gt;
    
QuitIfError:       &lt;/span&gt;&lt;span style="color: #008000"&gt;'&lt;/span&gt;&lt;span style="color: #008000"&gt; Come here if there was some kind of problem&lt;/span&gt;&lt;span style="color: #008000"&gt;
&lt;/span&gt;&lt;span style="color: #000000"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; myOLApp &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Nothing&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; myNameSpace &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Nothing&lt;/span&gt;&lt;span style="color: #000000"&gt;
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; currentMessage &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Nothing&lt;/span&gt;&lt;span style="color: #000000"&gt;

    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; DataO &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;New&lt;/span&gt;&lt;span style="color: #000000"&gt; DataObject
    DataO.Clear
    DataO.SetText ClipBoard
    DataO.PutInClipboard
    
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Set&lt;/span&gt;&lt;span style="color: #000000"&gt; DataO &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Nothing&lt;/span&gt;&lt;span style="color: #000000"&gt;

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;End Sub&lt;/span&gt;&lt;span style="color: #000000"&gt;

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;Function&lt;/span&gt;&lt;span style="color: #000000"&gt; GetMsgDetails(Item &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; MailItem, Details &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;String&lt;/span&gt;&lt;span style="color: #000000"&gt;) &lt;/span&gt;&lt;span style="color: #0000ff"&gt;As&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;String&lt;/span&gt;&lt;span style="color: #000000"&gt;

    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;If&lt;/span&gt;&lt;span style="color: #000000"&gt; Details &lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #000000"&gt;""&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;Then&lt;/span&gt;&lt;span style="color: #000000"&gt;
        Details &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; Details &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; vbCrLf
    &lt;/span&gt;&lt;span style="color: #0000ff"&gt;End&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #0000ff"&gt;If&lt;/span&gt;&lt;span style="color: #000000"&gt;
    Details &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; Details &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; Item.Subject &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; vbCrLf
    Details &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; Details &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt;Outlook:&lt;/span&gt;&lt;span style="color: #000000"&gt;"&lt;/span&gt;&lt;span style="color: #000000"&gt; &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; Item.EntryID &lt;/span&gt;&lt;span style="color: #000000"&gt;+&lt;/span&gt;&lt;span style="color: #000000"&gt; vbCrLf

    GetMsgDetails &lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt; Details

&lt;/span&gt;&lt;span style="color: #0000ff"&gt;End Function&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="wlWriterSmartContent" id="0767317B-992E-4b12-91E0-4F059A8CECA8:83f05d6c-7205-4f64-9c3e-d3f7d23b3158" contenteditable="false" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us tags: &lt;a href="http://del.icio.us/popular/Windows" rel="tag"&gt;Windows&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/VBA" rel="tag"&gt;VBA&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/automation" rel="tag"&gt;automation&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/Outlook" rel="tag"&gt;Outlook&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/scripting" rel="tag"&gt;scripting&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/productivity" rel="tag"&gt;productivity&lt;/a&gt;, &lt;a href="http://del.icio.us/popular/GTD" rel="tag"&gt;GTD&lt;/a&gt;&lt;/div&gt;&lt;img src="http://mutable.net/blog/aggbug/10.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>David Jade</dc:creator>
            <guid>http://mutable.net/blog/archive/2006/09/02/how_to_hyperlink_to_microsoft_outlook_messages.aspx</guid>
            <pubDate>Sun, 03 Sep 2006 01:11:22 GMT</pubDate>
            <wfw:comment>http://mutable.net/blog/comments/10.aspx</wfw:comment>
            <comments>http://mutable.net/blog/archive/2006/09/02/how_to_hyperlink_to_microsoft_outlook_messages.aspx#feedback</comments>
            <slash:comments>13</slash:comments>
            <wfw:commentRss>http://mutable.net/blog/comments/commentRss/10.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>