<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Director of Random Technologies</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/johnls/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/johnls/feed.xml</link>
    <description>Using diagnostic tools to tune and debug Windows software</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:24:17 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Recent Posts on Code-o-rama</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/recent-posts-on-code-o-rama</link>
      <pubDate>Mon, 23 Jul 2007 19:01:05 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2007/07/23/recent-posts-on-code-o-rama/</guid>
      <description><![CDATA[While I haven't been posting much here, I have been posting pretty regularly over on my main...]]></description>
      <content:encoded><![CDATA[<p>While I haven't been posting much here, I have been posting pretty regularly over on my main development blog <a href="http://code-o-ram.net">code-o-rama</a>.&nbsp;&nbsp; The main reason is that I'm running a newer version of <a href="http://communityserver.org">Community Server</a> over there, which has better editing, etc..&nbsp; Here are some articles you might want to check out:</p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2006/12/29/standalone-unit-test-programs.aspx">Stand alone unit test programs</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/01/03/vs-macro-to-insert-guids.aspx">VS macro to insert GUIDs</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/01/18/tf-command-line-help.aspx">Real Team Foundation command line help</a></p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/02/11/playing-with-vs-macros-getting-the-primaryoutput.aspx">Playing with VS macros: Getting the PrimaryOutput</a> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/02/12/changing-console-fonts.aspx">Changing console fonts</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/03/02/subtle-visible-whitespace-in-vs-2005.aspx">Subtle visible whitespace in VS2005</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/03/22/visual-studio-column-guides-redux.aspx">Visual Studio column guides redux</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/03/22/reading-custom-configuration-sections-in-net-2-0.aspx">Reading custom configuration sections in .NET 2.0</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/03/28/side-by-side-problems.aspx">SxS problems</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/04/16/turning-off-vista-problem-reporting.aspx">Turning off Vista problem reporting</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/04/20/reader-writer-lock-in-net-3-5.aspx">Reader/writer lock in .NET 3.5</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/05/01/sxs-follow-up-serious-errors-should-not-be-hidden.aspx">Putting SxS errors in your face</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/05/01/running-msbuild-scripts-programmatically.aspx">Running MSBuild scripts programmatically</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/05/04/vs-macro-to-insert-todo.aspx">VS macro to insert TODO:</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/07/12/listing-all-solution-project-properties.aspx">Listing all solution project properties</a></p> <p><a href="http://lyon-smith.org/blogs/code-o-rama/archive/2007/07/17/timing-code-on-windows-with-the-rdtsc-instruction.aspx">Getting accurate per thread timing on Windows</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Renamed my blog</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/renamed-my-blog</link>
      <pubDate>Fri, 29 Dec 2006 16:32:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/12/29/renamed-my-blog/</guid>
      <description><![CDATA[Merry Christmas, etc., etc..&nbsp;&nbsp; Hope you are all enjoying your XBOX 360's.&nbsp; New year,...]]></description>
      <content:encoded><![CDATA[<P>Merry Christmas, etc., etc..&nbsp;&nbsp; Hope you are all enjoying your XBOX 360's.&nbsp; New year, new blog name.&nbsp; I'm renaming by blog and refocusing it for 2007.</P>
<P>Recently we decided to give everyone on the Visual Studio Diagnostics team a Director's title (minus the actual promotion).&nbsp; Mine was "Director of Random Technologies" probably on account of the fact that I have a tendency to act a bit like Hammy in "<A class="" href="http://www.imdb.com/title/tt0327084/" mce_href="http://www.imdb.com/title/tt0327084/">Over the Hedge</A>" when it comes to software and computers.&nbsp; It's all good.</P>
<P>The&nbsp;realization hit me this year that the blogs I read the most are the ones that are focused in some way in their scope.&nbsp; They have a theme.&nbsp; Sadly my blog has thus far had no theme.&nbsp; It also helps when trying to write blog entries if you have a theme, otherwise it's a bit like a book without a plot.&nbsp; </P>
<P>So I've decided refocus, and to use my MSDN blog to write solely about Microsoft diagnostic tools and technologies.&nbsp; We'll see how it works out.</P>
<P>Another thing I'm going to try and do is keep my entries short and sweet.</P>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Points</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/microsoft-points</link>
      <pubDate>Fri, 08 Dec 2006 21:25:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/12/08/microsoft-points/</guid>
      <description><![CDATA[We were having a discussion today about Microsoft Points and that it would be useful to be able to...]]></description>
      <content:encoded><![CDATA[We were having a discussion today about Microsoft Points and that it would be useful to be able to transfer them to others as payment for, oh I don't know let's say, um, gambling debts.&nbsp; Hey, that would be just like <STRONG>real</STRONG> money!&nbsp; Couldn't find anything about that, but I did notice that there is a handy dandy <A class="" href="http://www.mspconverter.com/" mce_href="http://www.mspconverter.com/">Microsoft Points Converter</A>.]]></content:encoded>
    </item>
    <item>
      <title>Dynamic vs. Static Instrumentation</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/dynamic-vs-static-instrumentation</link>
      <pubDate>Wed, 15 Nov 2006 12:14:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/11/15/dynamic-vs-static-instrumentation/</guid>
      <description><![CDATA[As you know if you are a Visual Studio Team System user, we provide two types of profilers with the...]]></description>
      <content:encoded><![CDATA[<P>As you know if you are a <A class="" href="http://msdn.microsoft.com/vstudio/teamsystem/default.aspx" mce_href="http://msdn.microsoft.com/vstudio/teamsystem/default.aspx">Visual Studio Team System</A> user, we provide two types of profilers with the product; sampling and trace.&nbsp; If you are not familiar with this feature of&nbsp;VSTS, one place to start are the excellent TechNotes under the Development heading on <A class="" href="http://msdn.microsoft.com/vstudio/teamsystem/reference/technotes/default.aspx" mce_href="http://msdn.microsoft.com/vstudio/teamsystem/reference/technotes/default.aspx">this page</A>.&nbsp; </P>
<P>The trace profiler works by rewriting the method bodies of your program to include <EM>probes</EM>.&nbsp; A probe is a small piece of code, usually a function call, that calls out to the profilers <EM>collection </EM>logic so that it can record the current location of the program execution.</P>
<P>The subject of this blog is how those probes get into your methods.&nbsp; This process is called <EM>instrumentation.&nbsp;</EM>&nbsp;I'll discuss two approaches, and then I'll&nbsp;discuss the pros and cons of each.</P>
<P><STRONG>Static Instrumentation</STRONG></P>
<P>In this approach the instrumentation occurs before your program even runs.&nbsp; Essentially the probes are baked in to you binary.&nbsp; There are two ways this can be done.&nbsp; One is to have the compiler put the probes in.&nbsp; Visual C++ takes this approach to implent <A class="" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/profileguidedoptimization.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/profileguidedoptimization.asp">Profile Guided Optimizations</A> (PGO).&nbsp; Another is to do what is called <EM>post link </EM>instrumentation.&nbsp; With this approach you take the binary that has been emitted by the compiler, crack it open to find all the methods, instrument and then rewrite the binary like nothing ever happened.&nbsp; <BR><EM><BR>Pros &amp; Cons: <BR></EM>This approach yields much faster start-up times.&nbsp; The images that is loaded in to memory is the image that runs.</P>
<P>The rewrite process destroys .NET strong naming, and thus images must be resigned after instrumentation, or <A class="" href="http://blogs.msdn.com/blogs/code-o-rama/archive/2005/12/30/test-signing-and-instrumentation.aspx" mce_href="http://blogs.msdn.com/blogs/code-o-rama/archive/2005/12/30/test-signing-and-instrumentation.aspx">verification skipping</A> must be enabled.&nbsp; Statically instrumented images can also be placed in the <EM>native image cache.</EM></P>
<P>Rewriting the images means you have to deal with the whole question of where to put the files that you instrument.&nbsp; This can be quite a housekeeping chore for the user.</P>
<P>Rewriting PE files correctly is relatively straightforward for .NET applications.&nbsp; It's really hard for native applications, particularly for x64 images, where instrumenting methods can turn them from <A class="" href="http://blogs.msdn.com/oldnewthing/archive/2004/01/14/58579.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2004/01/14/58579.aspx">leaf methods to non-leaf methods</A>.</P>
<P><STRONG>Dynamic Instrumentation.</STRONG>&nbsp; </P>
<P>In this approach the instrumentation occurs as the program is running.&nbsp; In the case of a just in time compilation environment such as .NET, this is acheived by using the <A class="" href="http://msdn.microsoft.com/msdnmag/issues/03/09/NETProfilingAPI/default.aspx" mce_href="http://msdn.microsoft.com/msdnmag/issues/03/09/NETProfilingAPI/default.aspx">.NET profiling API</A> to get notified when methods are about to be compiled and executed for the first time.&nbsp; Your profiler gets the opportunity to rewrite the method that is about to be run, by adding probes or whatever you want.&nbsp; Then the program continues on.&nbsp; Dynamic instrumentation can also be performed on native images too, but doing this is hard and rarely done.&nbsp; The approaches you could take to pull this involve injecting a DLL into the process, suspending it and then using debug information to patch and rewrite methods.<BR><BR><EM>Pros &amp; Cons</EM></P>
<P>Yields significantly slower start-up times and increased memory usage because each method body must be reallocated, as well as the the some CPU load to instrument on the fly.&nbsp; </P>
<P>If you want to do instrumentation at the line level, this approach requires loading additional data from the .pdb files in at runtime too.&nbsp; If you load the actual .pdb's that&nbsp;has a huge runtime cost.&nbsp; </P>
<P>Because the original file was not modified, .NET strong naming is not broken.&nbsp; However, profiling of images in the <EM>native image cache</EM> must be disabled, as these cannot be dynamically instrumented because they are already compiled to native code</P>
<P mce_keep="true"><STRONG>Conclusion</STRONG>&nbsp;</P>
<P mce_keep="true">Hopefully this entry has given you an overview of the two approaches.&nbsp; Please comment with any question you may have.</P>]]></content:encoded>
    </item>
    <item>
      <title>How to Upgrade Software with a Windows Installer Package</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/how-to-upgrade-software-with-a-windows-installer-package</link>
      <pubDate>Mon, 13 Nov 2006 19:32:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/11/13/how-to-upgrade-software-with-a-windows-installer-package/</guid>
      <description><![CDATA[One of the things that you often want to do when creating a Windows Installer using WiX is to...]]></description>
      <content:encoded><![CDATA[<P>One of the things that you often want to do when creating a Windows Installer using <A class="" href="http://wix.sourceforge.net/" mce_href="http://wix.sourceforge.net">WiX</A> is to uninstall the existing product before installing the new one.&nbsp; This is referred to as doing a "<A class="" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/planning_a_major_upgrade.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/planning_a_major_upgrade.asp">major upgrade</A>".&nbsp; The way to pull this off is to add the following WiX snippet to your .wxs file:</P><!-- code formatted by http://manoli.net/csharpformat/ --><PRE class=csharpcode><SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Wix</SPAN> <SPAN class=attr>xmlns</SPAN><SPAN class=kwrd>="http://schemas.microsoft.com/wix/2006/wi"</SPAN> <SPAN class=attr>xmlns:netfx</SPAN><SPAN class=kwrd>="http://schemas.microsoft.com/wix/NetFxExtension"</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Product</SPAN> 
    <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="*"</SPAN> 
    ...
    <SPAN class=attr>UpgradeCode</SPAN><SPAN class=kwrd>="35ED4D41-BA5F-4598-8266-A1B9EB512345"</SPAN><SPAN class=kwrd>&gt;</SPAN>
    ...
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Upgrade</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="35ED4D41-BA5F-4598-8266-A1B9EB512345"</SPAN><SPAN class=kwrd>&gt;</SPAN>
      <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>UpgradeVersion</SPAN> <SPAN class=attr>Minimum</SPAN><SPAN class=kwrd>="$(var.ProductVersion)"</SPAN> <SPAN class=attr>OnlyDetect</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=attr>Property</SPAN><SPAN class=kwrd>="NEWERVERSIONDETECTED"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
      <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>UpgradeVersion</SPAN> <SPAN class=attr>Minimum</SPAN><SPAN class=kwrd>="1.0.0"</SPAN> <SPAN class=attr>IncludeMinimum</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=attr>Maximum</SPAN><SPAN class=kwrd>="$(var.ProductVersion)"</SPAN> <SPAN class=attr>IncludeMaximum</SPAN><SPAN class=kwrd>="no"</SPAN> <SPAN class=attr>Property</SPAN><SPAN class=kwrd>="OLDERVERSIONBEINGUPGRADED"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
    <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Upgrade</SPAN><SPAN class=kwrd>&gt;</SPAN>
    ...
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>InstallExecuteSequence</SPAN><SPAN class=kwrd>&gt;</SPAN>
      <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>RemoveExistingProducts</SPAN> <SPAN class=attr>After</SPAN><SPAN class=kwrd>="InstallInitialize"</SPAN> <SPAN class=kwrd>/&gt;</SPAN>
    <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>InstallExecuteSequence</SPAN><SPAN class=kwrd>&gt;</SPAN>
</PRE>
<P>Note the product's <STRONG>Id</STRONG> attribute set to "*" so you don't forget to change it each time, but make sure the <STRONG>UpgradeCode </STRONG>stays the same forever.&nbsp; You could change the <STRONG>IncludeMaximum </STRONG>attribute to "yes" on the second <STRONG>UpgradeVersion </STRONG>element for debug builds so that you can continuously reinstall during development, even though your version number may not be changing.&nbsp; However I don't recommend you ship that way, because you want repair existing installations by default not uninstall/install them.</P>
<P><EM>My thanks to Bob Arnson on the WiX team for providing this tip.</EM></P>]]></content:encoded>
    </item>
    <item>
      <title>Ethereal Network Packet Protocol Analyzer</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/ethereal-network-packet-protocol-analyzer</link>
      <pubDate>Sun, 07 May 2006 02:12:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/05/07/ethereal-network-packet-protocol-analyzer/</guid>
      <description><![CDATA[I just rediscovered the Ethereal network protocol analyzer.&nbsp; I was trying to track down a...]]></description>
      <content:encoded><![CDATA[<P>I just rediscovered the <A href="http://www.ethereal.com/" mce_href="http://www.ethereal.com/">Ethereal network protocol analyzer</A>.&nbsp; I was trying to track down a problem using the <A href="http://www.pocketsoap.com/tcptrace/" mce_href="http://www.pocketsoap.com/tcptrace/">tcptrace</A> proxy which is a nice simple little tool when it works.&nbsp; I'm not quite sure what the problem was, but I was seeing different behavior when I was using the proxy than when I wasn't, so I went for the full blown driver level sniffing that Ethereal provides.&nbsp; It's based on the work of the <A href="http://www.winpcap.org/install/default.htm" mce_href="http://www.winpcap.org/install/default.htm">WinPCap</A> organization.&nbsp; I'll let you know why I was sniffing network packets in my next post.</P>]]></content:encoded>
    </item>
    <item>
      <title>Visual Studio Macro Balloon</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/visual-studio-macro-balloon</link>
      <pubDate>Tue, 17 Jan 2006 17:30:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/01/17/visual-studio-macro-balloon/</guid>
      <description><![CDATA[I've always loved editors with built in macro languages. Once you have macro control of every facet...]]></description>
      <content:encoded><![CDATA[<P><A href="http://lyon-smith.org/photos/code-o-rama/picture8.aspx" target=_blank><IMG hspace=10 src="http://lyon-smith.org/photos/code-o-rama/images/8/312x130.aspx" align=left vspace=10 border=0 mce_src="http://lyon-smith.org/photos/code-o-rama/images/8/312x130.aspx"></A>I've always loved editors with built in macro languages. Once you have macro control of every facet of the editor you can pull off some really cool and time saving tricks. I gave up macros back in the early 90's for the graphical environment of Microsoft Quick C, Developer Studio and now Visual Studio. So I was overjoyed to see macros make a real comeback with the release of Visual Studio 2002/2003. I'm still a little bit miffed that the macro language is Visual Basic. Not that I have anything against VB, it's just not a language that I'm super fluent in, so it makes the task of writing macros that much more challenging.I was writing a macro the other day, and every time I ran it I noticed this little balloon pop up on the task bar. I couldn't read what the balloon said, it came and went so fast. Finally, I cranked up the macro debugger and while stopped on a breakpoint I got a good look at the little bugger.</P>
<P>It's hard to click on something that flashes up and disappears in the blink of an eye. There's two reasons that I can think of for showing this balloon at all. One is as an informational message to educate you about macro usage. The other is a security feature so that you know that a macro is running. Neither of which I find that compelling.</P>
<P>A few minutes work with <A href="http://www.sysinternals.com/" mce_href="http://www.sysinternals.com/"><STRONG><SPAN style="COLOR: #436a9f">Regmon</SPAN></STRONG></A> and one can discover that setting the following value in the registry to one will disable this balloon for ever more.</P><PRE class=csharpcode>[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0]
"DontShowMacrosBalloon"=dword:00000006</PRE>
<P><EM>Craig Skibo, one of the Microsoft experts on the VSSDK say that this security balloon was the subject of much debate, and that it is in fact coded to not show up after 5 or 6 times of being clicked on. Nice touch, if you could actually click on it. Hence the the value of 6 above.</EM></P>]]></content:encoded>
    </item>
    <item>
      <title>Assembly Probing and Windows Installer</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/assembly-probing-and-windows-installer</link>
      <pubDate>Mon, 09 Jan 2006 05:29:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/01/09/assembly-probing-and-windows-installer/</guid>
      <description><![CDATA[Some platform features are like the stuff hiding under rocks at the seashore. No, I don't mean the...]]></description>
      <content:encoded><![CDATA[<P>Some platform features are like the stuff hiding under rocks at the seashore. No, I don't mean the one's that are slimey and stinky. I'm talking about the one's that are interesting but hard to spot. But, all you need to do is catch a glimpse of them and lift the rock...</P>
<P>Sometime last year, I was reading a page in the MSDN documentation when a certain line caught my attention. The page was <A href="http://msdn2.microsoft.com/en-us/library/15hyw9x3.aspx" target=_blank>"Locating the Assembly through Codebases or Probing"</A> and the line was this one, at the very bottom of the page:</P>
<P><EM>"If Assembly2 is not found at either of those locations, the runtime queries the Windows Installer."</EM></P>
<P>For some reason, this line struck me as interesting – a hitherto unseen mystery in the .NET runtime. OK, it's not like I was finding some hidden clue in the <A href="http://www.randomhouse.com/bantamdell/theruleoffour/abouthp.html" target=_blank>Hypernotomachia Poliphili</A> or anything. But this is my world, and I take my kicks where I can get 'em.</P>
<P>I'd read this line many times before, but I'd never really thought anything of it. Now I was suddenly realizing in all the times I'd seen assembly probing fail, I'd never ever seen it report that it was asking the Windows Installer to find the assembly. Was this just a documentation error, or was this a real feature of the .NET runtime?</P>
<P>To test things out, I created a simple application. It basically consisted of a managed executable (A) that loaded a library (B) using <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemreflectionassemblyclassloadtopic3.asp" target=_blank>Assembly.Load(AssemblyName)</A>. Trying to do things the fastest way possible, I created a Windows installer by creating a Setup Project in VS 2003. I installed the application, deleted library B and ran A, and sure enough there was an assembly load exception, but there was no message in the Fusion log about the Windows Installer being invoked to find the assembly.</P>
<P>I sent of a couple of e-mails to Microsoft folks, and got a response back from the .NET team that indicated that if all was well with the installation, a call to <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiprovideassembly.asp" target=_blank>MsiProvideAssembly</A> with the correct parameters should cause the missing assembly to reappear. OK, so .NET must be relying on this call internally to find the assembly, right? I ran the installed application under a debugger, and I did not see msi.dll, the Windows Installer library, being loaded by the .NET runtime. Very strange. A quick peek confirmed that any Windows Installer probing code had been stripped out of the <A href="http://research.microsoft.com/Collaboration/University/Europe/RFP/Rotor/" target=_blank>Rotor</A> sources, so no help there.</P>
<P>So I <A href="http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=6ca1cd8c-4cc4-4456-8c50-7705b5157548" target=_blank>logged a bug</A> at the Feedback center. It even got a mention in <A href="http://blogs.msdn.com/junfeng/archive/2005/6/3.aspx" target=_blank>Junfeng Zhangs</A> blog, one of the CLR loader wizards. The problem, it would appear, was with the installer built by the VS 2003 Setup project. It was not doing the right magic when it installed the application to allow .NET to find the assembly. What that magic was, and why .NET never loaded msi.dll remained a mystery.Well, I did eventually figure out what was going on, but I never got around to blogging about it.</P>
<P>So for the other pathologically curious folks out there, here's the answer to the mystery. Firstly, as we've already established, .NET is not really at fault. It is however up to some of its usual sneakiness in the never ending quest for performance, which is what made it hard for me to point the finger at the actual cause of the problem in the first place. The fact is, the .NET runtime doesn't even bother loading msi.dll unless it can be certain that the cost will be worthwhile. In order to do that, it actually does some of the checks that MsiProvideAssembly would do. That's why I never saw msi.dll being loaded. To discover this I ran <A href="http://www.sysinternals.com/Utilities/Regmon.html" target=_blank>Regmon</A> on the installed application (with the library deleted), on the assumption that that was the most likely place for further clues, and searched for the word "Installer" in the output. It turns out that the runtime scans the following list of registry keys for a record of the missing assembly:</P><PRE class=csharpcode>HKLM\SOFTWARE\Microsoft\Windows CurrentVersion\Installer\Managed\&lt;SID&gt;  Installer\Assemblies\Global\&lt;Strong Name&gt;
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer Managed\&lt;SID&gt;\Installer\Assemblies\&lt;File Path&gt;HKCU\Software\Microsoft\Installer\Assemblies Global\&lt;Strong Name&gt;
HKCU\Software\Microsoft\Installer\Assemblies\&lt;File Path&gt;HKLM\SOFTWARE\Classes\Installer\Assemblies\Global\&lt;Strong Name&gt;
HKLM\SOFTWARE\Classes\Installer\Assemblies\&lt;File Path&gt;\</PRE>
<P>If any record of the assembly being installed is found, the runtime loads msi.dll to bring back the missing assembly. &lt;SID&gt; appears to be the security identifier of the user that the process is running as. &lt;File Path&gt; is the full path to the file, but any '\' is substituted with a ''. &lt;Strong Name&gt; is the full assembly strong name. "Global" refers to the Global Assembly Cache (GAC).</P>
<P>Unfortunately, in the case that the assembly is found the Windows Installer obviously scans the same registry keys again looking for the assembly. I won't hide the fact that I don't like these sorts of shenanigans – by making assumptions about the behavior of the Windows Installer, the Windows Installer team is now essentially prevented from making changes to the way that MsiProvideAssemby works without a corresponding update to the .NET runtime. But what's done is done.</P>
<P>So how do you create an installer that will cause assembly probing to query the Windows Installer and bring back missing assemblies? Well, I'm sure that one of the commercial installers will do the right thing, but my preferred solution is just to use <A href="http://wix.sourceforge.net/" target=_blank>WiX</A>, given that it's free.</P>
<P>So, let's build another sample from scratch. Firstly, we'll create an assembly called Binding.exe. Here's the code:</P><PRE class=csharpcode><SPAN class=kwrd>using</SPAN> System;
<SPAN class=kwrd>using</SPAN> System.Reflection;
<SPAN class=kwrd>using</SPAN> System.Globalization;
[assembly: AssemblyVersion(<SPAN class=str>"1.0.0.0"</SPAN>)]
[assembly: AssemblyKeyFile(<SPAN class=str>"binding.snk"</SPAN>)]
<SPAN class=kwrd>namespace</SPAN> Binding
{
 <SPAN class=kwrd>class</SPAN> MainClass
 {
  <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>void</SPAN> Main(<SPAN class=kwrd>string</SPAN>[] args)
  {
   AssemblyName myAssemblyName = <SPAN class=kwrd>new</SPAN> AssemblyName();
   myAssemblyName.Name = <SPAN class=str>"ClassLibrary1"</SPAN>;
   myAssemblyName.Version = <SPAN class=kwrd>new</SPAN> Version(<SPAN class=str>"1.0.0.0"</SPAN>);
   myAssemblyName.SetPublicKeyToken(<SPAN class=kwrd>new</SPAN> <SPAN class=kwrd>byte</SPAN>[]
    {0x32, 0xea, 0x68, 0xe6, 0x16, 0x01, 0x2a, 0xe3});
   myAssemblyName.CultureInfo = <SPAN class=kwrd>new</SPAN> CultureInfo(<SPAN class=str>""</SPAN>);
   Assembly ass = Assembly.Load(myAssemblyName);
   MethodInfo method =
    ass.GetType(<SPAN class=str>"ClassLibrary1.Class1"</SPAN>).GetMethod(<SPAN class=str>"Method1"</SPAN>);
   method.Invoke(<SPAN class=kwrd>null</SPAN>, <SPAN class=kwrd>null</SPAN>);
  }
 }
}</PRE>
<P>Just to make things interesting this code builds an AssemblyName and loads an assembly explicitly. Note that everything should be strongly named in order for things to work properly. Don't forget to set the CultureInfo. The code for ClassLibrary1.dll is just:</P><PRE class=csharpcode><SPAN class=kwrd>using</SPAN> System;
<SPAN class=kwrd>using</SPAN> System.Reflection;
<SPAN class=kwrd>using</SPAN> ClassLibrary2;

[assembly: AssemblyVersion(<SPAN class=str>"1.0.0.0"</SPAN>)]
[assembly: AssemblyKeyFile(<SPAN class=str>"binding.snk"</SPAN>)]

<SPAN class=kwrd>namespace</SPAN> ClassLibrary1
{
  <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>class</SPAN> Class1
  {
    <SPAN class=kwrd>public</SPAN> Class1()
    {
    }
   
    <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>void</SPAN> Method1()
    {
      Console.WriteLine(<SPAN class=str>"ClassLibrary1.Class1.Method1"</SPAN>);
      Class2.Method2();
    }
  }
}</PRE>
<P>ClassLibrary1 has an implicit dependency on the assembly ClassLibrary2.dll. The code for ClassLibrary2.dll is:</P><PRE class=csharpcode><SPAN class=kwrd>using</SPAN> System;
<SPAN class=kwrd>using</SPAN> System.Reflection;
<SPAN class=kwrd>using</SPAN> ClassLibrary2;[assembly: AssemblyVersion(<SPAN class=str>"1.0.0.0"</SPAN>)]
[assembly: AssemblyKeyFile(<SPAN class=str>"binding.snk"</SPAN>)]<SPAN class=kwrd>namespace</SPAN> ClassLibrary1
{
  <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>class</SPAN> Class1
  {
    <SPAN class=kwrd>public</SPAN> Class1()
    {
    }
    
    <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>void</SPAN> Method1()
    {
      Console.WriteLine(<SPAN class=str>"ClassLibrary1.Class1.Method1"</SPAN>);
      Class2.Method2();
    }
  }
}</PRE>
<P>OK, now for the installer code:</P><PRE class=csharpcode><SPAN class=kwrd>&lt;?</SPAN><SPAN class=html>xml</SPAN> <SPAN class=attr>version</SPAN><SPAN class=kwrd>="1.0"</SPAN> <SPAN class=attr>encoding</SPAN><SPAN class=kwrd>="utf-8"</SPAN>?<SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Wix</SPAN> <SPAN class=attr>xmlns</SPAN>=<SPAN class=attr>http:</SPAN>//<SPAN class=attr>schemas</SPAN>.<SPAN class=attr>microsoft</SPAN>.<SPAN class=attr>com</SPAN>/<SPAN class=attr>wix</SPAN>/<SPAN class=attr>2003</SPAN>/<SPAN class=attr>01</SPAN>/<SPAN class=attr>wi</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Product</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="DEB8151D-1EEB-4549-A5AD-405C4CD28085"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="BindingProduct"</SPAN> <SPAN class=attr>Language</SPAN><SPAN class=kwrd>="1033"</SPAN> <SPAN class=attr>Version</SPAN><SPAN class=kwrd>="0.0.0.0"</SPAN> <SPAN class=attr>Manufacturer</SPAN><SPAN class=kwrd>="Microsoft Corporation"</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Package</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="511F5323-27B5-47C1-A488-8F92ED38EFEB"</SPAN> <SPAN class=attr>Description</SPAN><SPAN class=kwrd>="Binding Test Product"</SPAN> <SPAN class=attr>Comments</SPAN><SPAN class=kwrd>="Used for testing assembly binding"</SPAN> <SPAN class=attr>InstallerVersion</SPAN><SPAN class=kwrd>="200"</SPAN> <SPAN class=attr>Compressed</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=kwrd>/&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Media</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="1"</SPAN> <SPAN class=attr>Cabinet</SPAN><SPAN class=kwrd>="PRODUCT.CAB"</SPAN> <SPAN class=attr>EmbedCab</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=kwrd>/&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Directory</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="TARGETDIR"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="SourceDir"</SPAN><SPAN class=kwrd>&gt;</SPAN>
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Directory</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ProgramFilesFolder"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="PFiles"</SPAN><SPAN class=kwrd>&gt;</SPAN>
      <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Directory</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingProductDirectory"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="ProdDir"</SPAN> <SPAN class=attr>LongName</SPAN><SPAN class=kwrd>="BindingTest"</SPAN><SPAN class=kwrd>&gt;</SPAN>
        <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Component</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingComponent"</SPAN> <SPAN class=attr>Guid</SPAN><SPAN class=kwrd>="3A505CEA-C035-4F4C-A7CD-C0CAE01F492F"</SPAN><SPAN class=kwrd>&gt;</SPAN>
          <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>File</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingExeConfigFile"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="BINDIN01.CON"</SPAN> <SPAN class=attr>LongName</SPAN><SPAN class=kwrd>="Binding.exe.config"</SPAN> <SPAN class=attr>DiskId</SPAN><SPAN class=kwrd>="1"</SPAN> <SPAN class=attr>src</SPAN><SPAN class=kwrd>="Binding.exe.config"</SPAN> <SPAN class=attr>mce_src</SPAN><SPAN class=kwrd>="Binding.exe.config"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
          <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>File</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingExeFile"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="BINDING.EXE"</SPAN> <SPAN class=attr>LongName</SPAN><SPAN class=kwrd>="Binding.exe"</SPAN> <SPAN class=attr>DiskId</SPAN><SPAN class=kwrd>="1"</SPAN> <SPAN class=attr>src</SPAN><SPAN class=kwrd>="Binding.exe"</SPAN> <SPAN class=attr>mce_src</SPAN><SPAN class=kwrd>="Binding.exe"</SPAN> <SPAN class=attr>Assembly</SPAN><SPAN class=kwrd>=".net"</SPAN> <SPAN class=attr>KeyPath</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=attr>AssemblyApplication</SPAN><SPAN class=kwrd>="BindingExeConfigFile"</SPAN> <SPAN class=attr>AssemblyManifest</SPAN><SPAN class=kwrd>="BindingExeFile"</SPAN> <SPAN class=attr>Vital</SPAN><SPAN class=kwrd>="yes"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
        <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Component</SPAN><SPAN class=kwrd>&gt;</SPAN>
        <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Component</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary1Component"</SPAN> <SPAN class=attr>Guid</SPAN><SPAN class=kwrd>="F6E5ECF8-EBD8-42C1-8BC0-9A3E7CAF985C"</SPAN><SPAN class=kwrd>&gt;</SPAN>
          <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>File</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary1DllFile"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="CLASSL01.DLL"</SPAN> <SPAN class=attr>LongName</SPAN><SPAN class=kwrd>="ClassLibrary1.dll"</SPAN> <SPAN class=attr>DiskId</SPAN><SPAN class=kwrd>="1"</SPAN> <SPAN class=attr>src</SPAN><SPAN class=kwrd>="ClassLibrary1.dll"</SPAN> <SPAN class=attr>mce_src</SPAN><SPAN class=kwrd>="ClassLibrary1.dll"</SPAN> <SPAN class=attr>Assembly</SPAN><SPAN class=kwrd>=".net"</SPAN> <SPAN class=attr>KeyPath</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=attr>AssemblyApplication</SPAN><SPAN class=kwrd>="BindingExeConfigFile"</SPAN> <SPAN class=attr>AssemblyManifest</SPAN><SPAN class=kwrd>="BindingExeFile"</SPAN> <SPAN class=attr>Vital</SPAN><SPAN class=kwrd>="yes"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
        <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Component</SPAN><SPAN class=kwrd>&gt;</SPAN>
        <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Component</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary2Component"</SPAN> <SPAN class=attr>Guid</SPAN><SPAN class=kwrd>="FCEAC462-3710-4A01-ACAB-B41F522ACD41"</SPAN><SPAN class=kwrd>&gt;</SPAN>
          <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>File</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary2DllFile"</SPAN> <SPAN class=attr>Name</SPAN><SPAN class=kwrd>="CLASSL02.DLL"</SPAN> <SPAN class=attr>LongName</SPAN><SPAN class=kwrd>="ClassLibrary2.dll"</SPAN> <SPAN class=attr>DiskId</SPAN><SPAN class=kwrd>="1"</SPAN> <SPAN class=attr>src</SPAN><SPAN class=kwrd>="ClassLibrary2.dll"</SPAN> <SPAN class=attr>mce_src</SPAN><SPAN class=kwrd>="ClassLibrary2.dll"</SPAN> <SPAN class=attr>Assembly</SPAN><SPAN class=kwrd>=".net"</SPAN> <SPAN class=attr>KeyPath</SPAN><SPAN class=kwrd>="yes"</SPAN> <SPAN class=attr>AssemblyApplication</SPAN><SPAN class=kwrd>="BindingExeConfigFile"</SPAN> <SPAN class=attr>AssemblyManifest</SPAN><SPAN class=kwrd>="BindingExeFile"</SPAN> <SPAN class=attr>Vital</SPAN><SPAN class=kwrd>="yes"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
        <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Component</SPAN><SPAN class=kwrd>&gt;</SPAN>
      <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Directory</SPAN><SPAN class=kwrd>&gt;</SPAN>
    <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Directory</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Directory</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>Feature</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingProductFeature"</SPAN> <SPAN class=attr>Title</SPAN><SPAN class=kwrd>="Binding Test Product Feature"</SPAN> <SPAN class=attr>Level</SPAN><SPAN class=kwrd>="1"</SPAN><SPAN class=kwrd>&gt;</SPAN>
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>ComponentRef</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="BindingComponent"</SPAN> <SPAN class=kwrd>/&gt;</SPAN>
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>ComponentRef</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary1Component"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
    <SPAN class=kwrd>&lt;</SPAN><SPAN class=html>ComponentRef</SPAN> <SPAN class=attr>Id</SPAN><SPAN class=kwrd>="ClassLibrary2Component"</SPAN><SPAN class=kwrd>/&gt;</SPAN>
  <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Feature</SPAN><SPAN class=kwrd>&gt;</SPAN>
  <SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Product</SPAN><SPAN class=kwrd>&gt;</SPAN>
<SPAN class=kwrd>&lt;/</SPAN><SPAN class=html>Wix</SPAN><SPAN class=kwrd>&gt;</SPAN></PRE>
<P>The key component of the installer is the use of the AssemblyApplication attribute on the File element. This attribute points to the Binding.exe.config file for the application. This is the file that .NET will look for to determine if your application is installed before calling the Windows Installer bring back any missing assemblies.</P>
<P>Compile the three assemblies, then the installer. Run the installer. No navigate to the folder C:\Program Files\BindingTest\. Run Binding.exe. It outputs the two method names. Now for the magic. Delete ClassLibrary1.dll and ClassLibrary2.dll. Go on, don't be shy. Done that? Now run Binding.exe again. Instead of an AssemblyLoadException the program should run as if nothing had happened. Check again and the two DLL's are magically back. A self repairing application. Pretty cool, eh?</P>
<P>Lastly, I built a small program to test things out called CheckInstalled.cs, as follows:</P><PRE class=csharpcode><SPAN class=kwrd>using</SPAN> System;
<SPAN class=kwrd>using</SPAN> System.IO;
<SPAN class=kwrd>using</SPAN> System.Runtime.InteropServices;
<SPAN class=kwrd>using</SPAN> System.Reflection;
<SPAN class=kwrd>using</SPAN> System.Text;

<SPAN class=rem>// After running msiexec /i WixSetup.msi the command:</SPAN>
<SPAN class=rem>//</SPAN>
<SPAN class=rem>// CheckInstalled ClassLibrary1 "C:\Program Files\BindingTest\Binding.exe"</SPAN>
<SPAN class=rem>//</SPAN>
<SPAN class=rem>// Should return the path to ClassLibrary1.dll. See one of the registry keys:</SPAN>
<SPAN class=rem>//</SPAN>
<SPAN class=rem>// HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\Managed\</SPAN>
<SPAN class=rem>//   &lt;SID&gt;\Installer\Assemblies\Global\&lt;Strong Name&gt;</SPAN>
<SPAN class=rem>// HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\Managed\</SPAN>
<SPAN class=rem>//   &lt;SID&gt;\Installer\Assemblies\&lt;File Path&gt;</SPAN>
<SPAN class=rem>// HKCU\Software\Microsoft\Installer\Assemblies\Global\&lt;Strong Name&gt;</SPAN>
<SPAN class=rem>// HKCU\Software\Microsoft\Installer\Assemblies\&lt;File Path&gt;</SPAN>
<SPAN class=rem>// HKLM\SOFTWARE\Classes\Installer\Assemblies\Global\&lt;Strong Name&gt;</SPAN>
<SPAN class=rem>// HKLM\SOFTWARE\Classes\Installer\Assemblies\&lt;File Path&gt;</SPAN>

<SPAN class=kwrd>class</SPAN> MainClass
{
  [DllImport(<SPAN class=str>"msi.dll"</SPAN>, CharSet=CharSet.Auto, SetLastError=<SPAN class=kwrd>true</SPAN>)]
  <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>extern</SPAN> <SPAN class=kwrd>uint</SPAN> MsiProvideAssembly(
    <SPAN class=kwrd>string</SPAN> assembly, <SPAN class=kwrd>string</SPAN> context, <SPAN class=kwrd>int</SPAN> mode, <SPAN class=kwrd>int</SPAN> info,
    System.Text.StringBuilder path, <SPAN class=kwrd>ref</SPAN> <SPAN class=kwrd>int</SPAN> pathSize); 
 
  <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>void</SPAN> Main(String[] args)
  {
    <SPAN class=kwrd>if</SPAN> (args.Length &lt; 1)
    {
      Console.WriteLine(
        <SPAN class=str>"USAGE: {0} &lt;assemblyName&gt; [&lt;installPath&gt;]"</SPAN>,
        Path.GetFileName(Assembly.GetExecutingAssembly().Location));
      Console.WriteLine(
        <SPAN class=str>"\nAssembly name is just a full or partial assembly name. "</SPAN> +
        <SPAN class=str>"The &lt;installPath&gt; is case sensitive"</SPAN>);
      <SPAN class=kwrd>return</SPAN>;
    }
    
    <SPAN class=kwrd>if</SPAN> (args.Length &gt; 1)
      Console.WriteLine(
        <SPAN class=str>"Checking for installation of assembly '{0}' at install path '{1}'"</SPAN>,
        args[0], args[1]);
    <SPAN class=kwrd>else</SPAN>
      Console.WriteLine(<SPAN class=str>"Checking for installation of assembly '{0}'"</SPAN>,
        args[0]);
  
    <SPAN class=kwrd>try</SPAN>
    {
      Console.WriteLine(EnsureAssemblyIsInstalled(
        args[0], args.Length &lt; 2 ? <SPAN class=kwrd>null</SPAN> : args[1]));
    }
    <SPAN class=kwrd>catch</SPAN> (SystemException e)
    {
      Console.WriteLine(<SPAN class=str>"ERROR: "</SPAN> + e.Message);
    }
  }
 
  <SPAN class=kwrd>public</SPAN> <SPAN class=kwrd>static</SPAN> <SPAN class=kwrd>string</SPAN> EnsureAssemblyIsInstalled(
    <SPAN class=kwrd>string</SPAN> assemblyName, <SPAN class=kwrd>string</SPAN> installPath)
  {
    <SPAN class=kwrd>int</SPAN> buffer = 1024;
    StringBuilder path = <SPAN class=kwrd>new</SPAN> StringBuilder(1024);  
    <SPAN class=kwrd>uint</SPAN> code = MsiProvideAssembly(assemblyName, installPath,
      0, 0, path, <SPAN class=kwrd>ref</SPAN> buffer);    
    <SPAN class=kwrd>if</SPAN> (code != 0)
      <SPAN class=kwrd>throw</SPAN> <SPAN class=kwrd>new</SPAN> SystemException(<SPAN class=str>"Windows Installer error "</SPAN> +
        code.ToString());   
      <SPAN class=kwrd>return</SPAN> <SPAN class=str>"Found '"</SPAN> + path.ToString() + <SPAN class=str>"'"</SPAN>;
  }
}
</PRE>
<P>Run this program as follows:</P><PRE class=csharpcode>CheckInstalled ClassLibrary1 "C:\Program Files\BindingTest\Binding.exe.config"</PRE>
<P>It should report that ClassLibrary1 was found. Delete it and run the application again and you'll see Windows Installer bring the file back, this time with a user interface. This UI is suppressed by the .NET runtime.</P>
<P>So next time you see something hiding underneath a rock, go and check it out. It might be nothingit could be a nasty smelly bug, or it could just be a hidden gem.</P>]]></content:encoded>
    </item>
    <item>
      <title>Refactoring NUnit Unit Tests to VSUnit</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/refactoring-nunit-unit-tests-to-vsunit</link>
      <pubDate>Tue, 03 Jan 2006 13:47:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2006/01/03/refactoring-nunit-unit-tests-to-vsunit/</guid>
      <description><![CDATA[If you have been using NUnit to write your unit tests, and you have access to Visual Studio 2005...]]></description>
      <content:encoded><![CDATA[<P>If you have been using <A title=NUnit href="http://nunit.sourceforge.net/" mce_href="http://nunit.sourceforge.net/">NUnit</A> to write your unit tests, and you have access to <A title="Visual Studio 2005 Team System" href="http://msdn.microsoft.com/vstudio/teamsystem/default.aspx" mce_href="http://msdn.microsoft.com/vstudio/teamsystem/default.aspx">Visual Studio 2005 Team System</A> you'll want to consider upgrading your NUnit tests to the VS Unit Test Framework, which I'll call VSUnit for short. There is a <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/vstsunittesting.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/vstsunittesting.asp">walkthrough</A> that you should read to get an idea of the VSUnit product features. 
<P>I recently upgraded some old unit tests to see what was involved, and it was really straightforward. First I'll make some assumptions. You have a VS 2003 solution that you've recently upgraded to VS 2005 and which contains some NUnit test projects. I'll also assume you are using C# as your development language.</P>
<P>Here's a rundown of what I did:</P>
<OL>
<LI>Add a new Test Project to your solution (Test menu -&gt; New Test -&gt; Unit Test). This is going to add a Solution Items folder containing .vsmdi and .testrunconfig files. It should look something like the image on the below in your Solution Explorer.<BR><A href="http://lyon-smith.org/photos/code-o-rama/images/11/original.aspx" target=_blank><BR><IMG style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" src="http://lyon-smith.org/photos/code-o-rama/images/11/389x333.aspx" border=0 mce_src="http://lyon-smith.org/photos/code-o-rama/images/11/389x333.aspx"></A></LI>
<LI>I usually delete the AuthoringTests.txt and ManualTest.mht files, the Additional Test Attributes #region, and all the // TODO: and /// comments.</LI>
<LI>Drag all your .cs files from your old NUnit project. This will copy them into the VSUnit project.</LI>
<LI>Back-up your old NUnit project somewhere, then remove the project from the solution and delete or rename the project directory on disk.</LI>
<LI>At this point you should also rename your new VSUnit project. If you right click Rename it, bear in mind that that it sadly won't update the directory name. You'll have to close the solution, rename the directory in Explorer and open the solution .sln file in Notepad and change the directory before TestProject1.csproj.</LI>
<LI>When you're done with the tidy-up you are ready to start converting the test code. The diagram below shows the basic conversions. Just follow the conversions, 1 through 7. li&gt;When you're done with the tidy-up you are ready to start converting the test code. The diagram below shows the basic conversions. Just follow the conversions, 1 through 7.<BR><BR><A href="http://lyon-smith.org/photos/code-o-rama/images/12/original.aspx" target=_blank><IMG style="DISPLAY: block; CURSOR: hand; TEXT-ALIGN: center" src="http://lyon-smith.org/photos/code-o-rama/images/12/500x364.aspx" border=0></A><BR>Note that VSUnit also has support for assembly wide setup and cleanup which you might want to make use of. But just focus on the conversion for now. Note the change to a static method for the class setup and cleanup, and the addition of the TestContext parameter.</LI>
<LI>When you're done with step 6, try to compile your project. If it compiles, you are ready to run it and see what happens! Select the test then <STRONG>Test -&gt; Start Selected Test Project Without Debugger</STRONG>.</LI></OL>
<P>Note that VSUnit also has support for assembly wide setup and cleanup which you might want to make use of. But just focus on the conversion for now. Note the change to a static method for the class setup and cleanup, and the addition of the TestContext parameter.</P>
<P>When you're done with step 6, try to compile your project. If it compiles, you are ready to run it and see what happens! Select the test then <STRONG>Test -&gt; Start Selected Test Project Without Debugger</STRONG>.</P>
<P>You're probably wondering about all those Asserts in your NUnit tests. The really good news is that the Assert class exists in VSUnit and the methods signatures and behavior are by in large the same. The compiler will tell you where there are problems. </P>
<P>Other things to note. You can debug your unit tests just like with NUnit by setting breakpoints in the unit test and then selecting <STRONG>Test -&gt; Start Selected Test Project With Debugger</STRONG>. The TestContext parameter also lets you do some really cool stuff when setting up and configuring your tests.</P>
<P>You can take an existing set of unit tests and easily run the methods in specific orders by adding one or more Ordered Tests to your unit test project. The only way to do this in NUnit was to alphabetize your test method names!</P>
<P>There's lots of other fun stuff available too. My favorite feature by far is to turn on coverage profiling for the tests, by going to <STRONG>Test -&gt; Edit Test Configurations</STRONG> then select <STRONG>Coverage</STRONG> and select the assemblies you want to check the coverage for. I think you'll soon agree that these two tools were made for each other.</P>]]></content:encoded>
    </item>
    <item>
      <title>Test Signing and Instrumentation</title>
      <link>https://docs.microsoft.com/archive/blogs/johnls/test-signing-and-instrumentation</link>
      <pubDate>Fri, 30 Dec 2005 19:08:00 GMT</pubDate>
      <dc:creator><![CDATA[johnls]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/johnls/2005/12/30/test-signing-and-instrumentation/</guid>
      <description><![CDATA[Boy, I seem to spend all my time in the deep dark recesses of the CLR. OK, this post is a little bit...]]></description>
      <content:encoded><![CDATA[<P><EM>Boy, I seem to spend all my time in the deep dark recesses of the CLR. OK, this post is a little bit out there; I hope someone finds it useful.</EM></P>
<P>There's a problem with doing instrumented profiling of applications in <A href="http://msdnprod/vstudio/teamsystem/developer/default.aspx" mce_href="http://msdnprod/vstudio/teamsystem/developer/default.aspx">Visual Studio 2005 Team System</A> that comes up when working with strongly named assemblies. The problem is quite simply that instrumentation breaks the strong name signature of your assemblies. And in working around this problem, I'll show you how you can actually be hiding start-up performance problems in your .NET applications.</P>
<P>Let us recap how assembly signing works in .NET. First, you create a <A href="http://en.wikipedia.org/wiki/Private_key" mce_href="http://en.wikipedia.org/wiki/Private_key">public/private encryption key pair</A> using sn.exe -k or similar. Then you take your assemblies files and you sign them. This signing can be done with the sn.exe tool or more transparently by using the /keyfile or /keycontainer command line options of the C# or VB compilers.</P>
<P>Basically what the signing process actually does is to compute a <A href="http://en.wikipedia.org/wiki/Cryptographic_hash" mce_href="http://en.wikipedia.org/wiki/Cryptographic_hash">cryptographic hash</A> from the metadata, intermedate language (IL) and other parts of your assembly file. It then takes this hash and encrypts it using the private key from your key pair. Finally this signature blob and your public key are embedded in the assembly. Now you can send your assembly out into the world safe in the knowledge that no one can mess with it.</P>
<P>Any CLR that your assembly gets presented to can now validate that your assembly has not been tampered with. This is done by first calculating the same hash that the signing process calculated. Then, the CLR takes the signature blob from the assembly and uses the public key to reverse the encryption performed by your private key. This will only work if the public key is truly the partner of the private key used to encrypt the signature. Hence the importance of keeping the private key, uh, private. If all is well, the decrypted signature and the calculated hash will match, and <A href="http://www.worldwidewords.org/qa/qa-bob1.htm" mce_href="http://www.worldwidewords.org/qa/qa-bob1.htm">Bob's your Uncle</A>!</P>
<P><STRONG>Note: </STRONG>The 16 character public key token that you see as part of the full name of a strongly named assembly is not the signature hash referred to above. It is just a hash of your assemblies public key which makes the key a little easier to work with. For example, the public key token</P><PRE class=csharpcode>7e16da7b2e1bcfa6</PRE>
<P>is merely a hash of the public key</P><PRE class=csharpcode>0024000004800000940000000602000000240000525341310004000001000100e91eb19e092741
e85e7eda996ba566d491be9a300b2c26a8ce015398c87529f76075385c960bbc4dcf24ece0d1ad
a9f312dd0a42538d490be9228148645ecf5f45aadc12404af4c07d4b8060056b4724174ecc7d65
c65ac455a7321f71adfa8be981b02e9c5bd721d588395fa212c4a79cc007cf3a60738a0777614e
15b990a5</PRE>
<P>Which would you rather type?</P>
<P>So signing allows tampering to be caught, but it doesn't prevent it. And there's a back door into the signing process using the sn tool that allows signature verification to be turned off for assemblies containing a specific public key. In VSTS performance (and coverage) instrumentation works by inserting additional calls at the <A href="http://en.wikipedia.org/wiki/Basic_block" mce_href="http://en.wikipedia.org/wiki/Basic_block">basic block</A> boundaries in your applications IL and then rewriting the application binaries to disk. We call this <EM>static </EM>instrumentation because it's done before the program runs. Thus it breaks strong names signatures by changing the hash that will be computed when the assembly is verified by the CLR.</P>
<P>OK, big deal right? What's all this got to do with performance profiling? Well, the issue is that validating assembly signatures takes time. All that cryptographic stuff is computationally expensive. So, if you turn off verification for an assembly using something like:</P><PRE class=csharpcode>sn -Vr *,b5f241dcf0cb58c4</PRE>
<P>Then you are potentially hiding a performance problem that might occur when assemblies are fully signed and signing is not disabled. Now, before we go running screaming into the hills, there's one caveat that has to do with the the global assembly cache (GAC). Putting an assembly in the GAC requires that the assembly be strongly named. The full signature verification for assemblies in the GAC is actually only performed when the assembly is inserted into the GAC, and not on subsequent loads of the assembly. So in that sense, there's actually a small performance boost to be had by putting assemblies in the GAC. But all other strongly named assemblies will incur a verification check the first time they are loaded into an AppDomain.</P>
<P>For what types of applications might turning off strong naming incur an artificial performance boost? Well, in practice this is only likely to be a problem for applications that depend on very large numbers of strongly named private assemblies, and even then it's only going to skew the applications start-up time. Applications that load and unload assemblies via additional AppDomains might also incur some penalty, but then I suspect that other factors such as memory fragmentation and disk I/O are much more likely to cause performance noise that will hide this issue.</P>
<P>So, what is test signing? Test signing is a halfway point between fully signed/fully verified and unsigned/unverified. It's a new feature in .NET 2.0. With test signing you can create a throw away test key that you can use to sign your assemblies. The CLR verification process then goes through the full set of computations to verify the assembly hash against the test public key, resulting in the same performance hit as if the assembly were fully signed. However, the assembly doesn't actually contain the public key for the test key pair, but the real public key for the assembly. In this way the strong name of the assembly is not broken. The public key for the test key pair actually lives in the registry.</P>
<P>OK, time for an example. Let's say you just generated a new project called ConsoleApplication1. Let's say that it starts of fully signed using your companies real key pair. Now we instrument:</P><PRE class=csharpcode>&gt;vsinstr ConsoleApplication2.exe
Microsoft (R) VSInstr Post-Link Instrumentation 8.0.50727.0
Copyright (C) Microsoft Corp. All rights reserved.

File to Process:
   C:\Projects\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe
Original file backed up to C:\Projects\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe.orig

Warning VSP2001 : C:\Projects\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe is a strongly
named assembly.  It will need to be re-signed before it can be executed.
Successfully instrumented file C:\Projects\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe.
Warning VSP2013 : Instrumenting this image requires it to run as a 32-bit process.  The CLR header
flags have been updated to reflect this.
</PRE>
<P>Note that the message about having to sign the assembly before it can be executed, should really say something like "before it can be strong name verified". Now you create your test key and test sign the assembly.</P><PRE class=csharpcode>&gt;sn -k TestKey.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 2.0.50727.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Key pair written to TestKey.snk
</PRE>
<P>And you'll need just the public key too, so lets get that now:</P><PRE class=csharpcode>&gt;sn -p TestKey.snk TestPublicKey.snk.
Microsoft (R) .NET Framework Strong Name Utility.  Version 2.0.50727.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key written to TestPublicKey.snk
</PRE>
<P>You need the public key token that's currently in the assembly, which remember is from the real public key pair and is part of the real strong name of the assembly.</P><PRE class=csharpcode>&gt;sn -T ConsoleApplication2.exe
Microsoft (R) .NET Framework Strong Name Utility  Version 2.0.50727.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key token is b5f241dcf0cb58c4
</PRE>
<P>Now you do the test signing:</P><PRE class=csharpcode>&gt;sn -TS ConsoleApplication2.exe TestKey.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 2.0.50727.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly 'ConsoleApplication2.exe' successfully re-signed
</PRE>
<P>And finally you register the test signing public key:</P><PRE class=csharpcode>&gt;sn -Vr *,b5f241dcf0cb58c4 TestPublicKey.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 2.0.50727.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Verification entry added for assembly '*,b5f241dcf0cb58c4'
</PRE>
<P>Sadly the message that the last step gives you is identical to the one you get for regular verification skipping registration. Go to the registry and look under:</P><PRE class=csharpcode>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\*,b5f241dcf0cb58c4</PRE>
<P>(or whatever your public key token is). You'll see a TestPublicKey value containing the public key token.</P>
<P>Now you can run your performance tests and measure the real cost of checking all those assembly strong name signatures.</P>]]></content:encoded>
    </item>
  </channel>
</rss>