First link-only post on this blog, it was supposed to happen sooner or later, I guess... Here is a list of technology posters you can download, my favorites are the keyboard shortcut ones! By the way, thanks to Paulo for collecting the links!
Microsoft® Visual Basic® 2008 Default Keybindings
Visual C# 2008 Default Keybindings
Visual C++ 2008 Default Keybindings
Microsoft® Visual C#® 2005 Default Keybindings
Microsoft® Visual Basic® 2005 Default Keybindings
Microsoft® Visual C++® 2005 Default Keybindings
Microsoft .NET Framework 3.5 Commonly Used Types and Namespaces
Windows Presentation Foundation
.NET Compact Framework Versions (on OpenNETCF)
Microsoft® Silverlight™ 1.1 Developer Reference Poster
Smart Client Poster
Microsoft® Office InfoPath® 2007 Managed Object Model
Developer Map for SharePoint® Products and Technologies
Windows Server 2008 Active Directory Feature Components
Windows Server 2008 Feature Components
Microsoft Windows Server 2003 Active Directory Component Jigsaw
Exchange Server 2007 Edge Transport Server Role Architecture
Exchange Server 2007 Hub Transport Server Role Architecture (RTM)
Exchange Server 2007 Hub Transport Server Role Architecture (SP1)
BizTalk Server 2006 R2 Capabilities
BizTalk Server 2006 R2 Runtime Architecture
BizTalk Server 2006 R2 Scale-Out Configurations
Upgrade Project Server with Windows SharePoint Services
Upgrade Project Server without Windows SharePoint Services
Upgrade large SharePoint Portal Server 2003 intranet portals to SharePoint Server 2007
Upgrade SharePoint Portal Server 2003 to SharePoint Server 2007
Project Server 2003 Setup Flow
Microsoft CRM 3.0 Logical Database Diagrams
Microsoft CRM 1.2 Logical Database Diagram
PnP Overview Poster
MSIT SOI System Poster
MSIT SOI Runtime Poster
Carlo
Yesterday I was working on a sample project got from a customer, when Visual Studio 2005 showed this dialog:
Ok I thought, let's fix the application mappings. I did, but got the following:
What? Are you kidding me? Of course IIS is installed, I'm using it every day...
But wait a minute, I'm running on Vista x64 and this rings a bell to me... Let's use the x64 Visual Studio command prompt and give it another try:
Much better... and now Visual Studio does not complain anymore!
Every now and then we get calls from customer having troubles with ASP.NET deployment, usually in cluster/NLB environment but sometimes also in single servers; what they usually have in common is the complexity of the web application, maybe made of dozens (if not hundreds) of pages, controls etc... Typical questions are:
There are a few variations to the above, but you got the point. I think this might be due to a misunderstanding (of should I say the documentation is not clear enough?) about deployment capabilities of ASP.NET and what is recommended for complex/full loaded applications; the point is that ASP.NET supports hot deployment meaning that whenever you update a "core" component (the content of the /bin folder, for instance) the runtime finishes serving queued requests and then reload the application to reflect your changes. But this also means that a number of things are happening, (roughly) including recompiling the the new assemblies, re-creating the AppDomain to host the application, go through security checks and load the modules into the AppDomain.
You can easily imagine that for a complex application those steps might require some time during which we application is not able to respond to incoming user's requests. If we then consider a cluster/NBL environment, where every request from your users might be served by a different server... if you're in the middle of your deployment a user might be "bounced" from a server with the new dlls already installed and another with the old ones still in place, bad thinks will happen then...
A few days ago one of those cases came in, and the customer was reporting a deadlock problem in his w3wp.exe, with the following entry in his event log:
Event Type: Warning Event Source: W3SVC-WP Event Category: None Event ID: 2262 Date: 18/10/2007 Time: 15:46:10 User: N/A Computer: <computername> Description: ISAPI 'c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll' reported itself as unhealthy for the following reason: 'Deadlock detected'. Event Type: Warning Event Source: W3SVC Event Category: None Event ID: 1013 Date: 18/10/2007 Time: 15:47:43 User: N/A Computer: <computername> Description: A process serving application pool 'DefaultAppPool' exceeded time limits during shut down. The process id was '5780'.
We captured a dump as described in How to generate a dump file when ASP.NET deadlocks in IIS 6.0, and the first thing to note is that almost all the threads in the process were waiting on a stack exactly like the following:
[HelperMethodFrame: 06f9e968] System.Threading.Monitor.Enter(System.Object) System.Web.Compilation.CompilationLock.GetLock(Boolean ByRef) System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(System.Web.VirtualPath, Boolean, Boolean, Boolean) System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(System.Web.HttpContext, System.Web.VirtualPath, Boolean, Boolean, Boolean) System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(System.Web.VirtualPath, System.Web.HttpContext, Boolean, Boolean) System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(System.Web.VirtualPath, System.Type, System.Web.HttpContext, Boolean, Boolean) System.Web.UI.PageHandlerFactory.GetHandlerHelper(System.Web.HttpContext, System.String, System.Web.VirtualPath, System.String) System.Web.UI.PageHandlerFactory.System.Web.IHttpHandlerFactory2.GetHandler(System.Web.HttpContext, System.String, System.Web.VirtualPath, System.String) System.Web.HttpApplication.MapHttpHandler(System.Web.HttpContext, System.String, System.Web.VirtualPath, System.String, Boolean) System.Web.HttpApplication+MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef) System.Web.HttpApplication.ResumeSteps(System.Exception) System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object) System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest) System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest) System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32) [ContextTransitionFrame: 06f9ee00] [GCFrame: 06f9ee50] [ComMethodFrame: 06f9efa8]
As you can see we are in the middle of a massive compilation, most likely triggered by the application update the customer mentioned.
In the meantime there was one AppDomains which was shutting down because it reached its allowed compilation limit before recycling (again, this sound really like a consequence of the massive compilation going on in the meantime in other threads):
HttpRuntime 0x143fcc24: _shutDownStack: at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace() at System.Web.HttpRuntime.ShutdownAppDomain() at System.Web.Hosting.HostingEnvironment.ShutdownThisAppDomainOnce() at System.Web.Hosting.HostingEnvironment.InitiateShutdownWorkItemCallback(Object state) at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state) _shutDownMessage: Recompilation limit of 15 reached HostingEnvironment caused shutdown _shutdownInProgress: 1 _requestQueue: 0x144255a0 _appDomainAppPath: C:\Inetpub\wwwroot _appDomainAppId: /LM/W3SVC/235991876/Root
Imagine a situation where while the server is still receiving incoming request from the clients, the application's files are being update and the runtime needs to go through the initialization steps: it's a risky situation where it's easy to find yourself stuck in the middle of something, likely with performance and locking problems and eventually with a hang or even worse a deadlock.
Despite the fact that ASP.NET supports “hot deployment” (i.e. you can override application files and the runtime will automatically load the new version at next request), but this is not recommended in “hot” production environments and in particular in a cluster/NBL configuration, where there is always the risk that a request is served first by the updated server, while the next one might be served by the second server which is not updated yet, causing unpredictable behaviors; in such situations it’s always a good idea to schedule your deployments in a “maintenance window” where you can temporarily take offline the application, update everything you need and put it back online when done (if you’re just updating application’s files should be a matter of a couple of minutes, just the time needed for the transfer and restart the services).
Depending on your needs you may also want to edit your web.config and increase the number of compilations allowed before recycling the AppDomain by setting a higher value for numRecompilesBeforeAppRestart (I suggest to not set it higher than 50)
<compilation tempDirectory = "" debug = "false" strict = "false" explicit = "true" batch = "true" urlLinePragmas = "false" batchTimeout = "900" maxBatchSize = "1000" maxBatchGeneratedFileSize = "1000" numRecompilesBeforeAppRestart = "15" defaultLanguage = "vb" assemblyPostProcessorType = "" />
A few days ago I had the chance to work on a case not concerning ASP.NET or IIS, but rather an interaction with Visual Studio designer with classes from System.CodeDom namespace. The customer was developing a custom control meant for other developers to use it in their projects, and upon adding the control to the form, the former had to automatically modify the some codebehind files for example to add some custom methods and register itself in the InitializeComponent method to add events etc... The problem was that we were able to add new methods to the form, but despite our attempts nothing was added to InitializeComponent
Here's the chunk of code we were using:
private void AddCode(Form form) { IDesignerHost host = null; host = (IDesignerHost)form.Site.GetService(typeof(IDesignerHost)); //Add method "Form1_Load" on Form1 //--------------------------------------------------------------------------- CodeMemberMethod member = new CodeMemberMethod(); member.Name = "Form1_Load"; member.Parameters.Add(new CodeParameterDeclarationExpression("System.Object", "sender")); member.Parameters.Add(new CodeParameterDeclarationExpression("System.EventArgs", "e")); CodeSnippetExpression sn; sn = new CodeSnippetExpression("MessageBox.Show(\"Hello world\")"); member.Statements.Add(sn); member.Attributes = MemberAttributes.Private; CodeTypeDeclaration typedecl = (CodeTypeDeclaration)form.Site.GetService(typeof(CodeTypeDeclaration)); typedecl.Members.Add(member); //--------------------------------------------------------------------------- //This code will add the following line to the "InitializeMethod" method // this.Load += new System.EventHandler(this.Form1_Load); //--------------------------------------------------------------------------- member = new CodeMemberMethod(); foreach (CodeTypeMember typememb in typedecl.Members) { if (typememb.Name == "InitializeComponent") { member = (CodeMemberMethod)typememb; } } CodeDelegateCreateExpression createDelegate1; createDelegate1 = new CodeDelegateCreateExpression(new CodeTypeReference("System.EventHandler"), new CodeThisReferenceExpression(), "Form1_Load"); CodeAttachEventStatement attach = new CodeAttachEventStatement(new CodeThisReferenceExpression(), "Load", createDelegate1); member.Statements.Add(attach); typedecl.Members.Add(member); //--------------------------------------------------------------------------- //Add and remove a label because otherwise the code to add the method seems to stay "inactive, //while in this way it works //--------------------------------------------------------------------------- Label lbl = (Label)host.CreateComponent(typeof(Label)); host.DestroyComponent(lbl); //--------------------------------------------------------------------------- }
Interestingly beside InitializeComponent Visual Studio was showing the yellow line which means that the code has been changed and still needs to be saved, so something was actually happening... so I tried to add the event in another method and it worked fine; an also adding a MessageBox inside the foeach loop to display LinePragma.FileName and LinePragma.LineNumber showed we were working on the appropriate code file and code line (the InitializeComponent declaration)... So there is a problem with InitializeCompoent itself?
Well, yes.
I remember I read about it long time ago when I was getting my hand dirty with the emerging .NET technology (it was back in 2001 I think), but then I moved to web applications and this went somewhere in the back of my mind... the InitializeComponent is a very important method for Visual Studio, which protects it from custom manipulations which might compromise good working of the designer, and this is actually explained in a KB article: Code comments that you add in the InitializeComponent method are lost when you add a control in Visual Studio .NET or in Visual Studio 2005.
And that was actually the exact situation we where into: for some reason the code was not added as expected unless we were adding and removing a control to the From (a Label in our case) and this simple trick had the effect to somehow activate the code (I guess that way we were calling some specific method otherwise we were not touching), but his also had the side effect to put us in the situation described in the article. And even if our was were working fine, sooner or later we would have stumbled in this kind of Visual Studio protection anyway, because a developer would have certainly added other controls to the form anyway...
So we had two problems here:
To resolve point one we decided to create an InitializeComponent2 method still inside Form1.Designer.cs (to have it consistent with the default InitializeComponent added by Visual Studio) so we were now able to add our events and custom properties, and add a call to our InitializeComponent2 in Form1 construction, right below the standard InitializeComponent() call, and this way we had our event working as expected .
When using design-time controls, it's important to know what happens behind the scenes when you drop a control on your design surface—or for controls such as a Form, what happens when you create a Form that is inherited from another Form you created.
When an object is opened in the design environment, the class that the object is inherited from (not the newly created class) is constructed by Visual Studio. Remember, a control that is created fires its constructor, which also fires the InitializeComponent() method within the base class's constructor. Once the derived class is constructed, that magically named method within your newly created class, InitializeComponent(), is parsed line-by-line by Visual Studio. The only thing magic about this method is its name. Visual Studio just knows to look for this method.
In Visual Basic .NET, if Visual Studio can't understand the line of code, the line is removed (a nice way of saying eaten). The Visual Basic team felt it was more important to maintain the design-time environment. The C# team felt it was more important to maintain the code, so if Visual Studio can't parse InitializeComponent() you'll get the text of the exception presented to you. This includes inherited Forms, or controls placed on the design surface of another component class control.
Any property that is part of your base object is set by the base object and its constructor. When the InitializeComponent() method runs, the values are changed to the values you have set on the property sheet. In Visual Studio, the property sheet is just a graphical representation of the InitializeComponent() method. If your base class performs some sort of functionality in the constructor, be careful; it will be executed in the design environment as well.
You do have some control over this, however. Any class that derives from the Component class has a property called DesignMode that is set to true when the code is executing within the constructs of the Visual Studio designer. So you have the option to wrap code within an if statement. There's one more trick, however. The DeisgnMode property isn't set to true in the constructor. Remember, there's no real magic here. Visual Studio creates your object as it parses the InitializeComponent() method. Once the object is constructed, Visual Studio keeps track of the objects it creates, and simply says:
newlyCreatedObject.DesignMode = true
The argument intrigued me, so I was curious to further explore the capabilities offered by the CodeDom namespace; doing some researches on the Internet you can quite easily find articles which describes how to create a class from crash, create the source file and compile it on the fly to have the resulting assembly and this is quite a standard usage of CodeDome, here are just a couple of references:
But I've not been able to find good articles about how to customize the current code graph, the one we're currently using (instead of creating a completely new class/component from scratch), so I made some tests myself with conflicting results (something's working the way I like, something don't...). Here's what I've come up with, it's still not perfect (continue reading to find out what's wrong) but even if far from being perfect at least it works:
private void AddCode(Form form) { //Hook to the designer IDesignerHost host = (IDesignerHost)form.Site.GetService(typeof(IDesignerHost)); //Add method "Form1_Load" on Form1 CodeMemberMethod member = new CodeMemberMethod(); member.Name = "Form1_Load"; member.Parameters.Add(new CodeParameterDeclarationExpression("System.Object", "sender")); member.Parameters.Add(new CodeParameterDeclarationExpression("System.EventArgs", "e")); CodeSnippetExpression sn = new CodeSnippetExpression("MessageBox.Show(\"Hello world\")"); member.Statements.Add(sn); member.Attributes = MemberAttributes.Private; CodeTypeDeclaration typedecl = (CodeTypeDeclaration)form.Site.GetService(typeof(CodeTypeDeclaration)); typedecl.Members.Add(member); //--------------------------------------------------------------------------- //Create an InitializeComponent2() method member = new CodeMemberMethod(); member.Name = "InitializeComponent2"; typedecl.Members.Add(member); //--------------------------------------------------------------------------- //Find the constructor and add a call to my "InitializeComponent2()" method //CodeConstructor ctor = new CodeConstructor(); CodeConstructor ctor = null; foreach (CodeTypeMember typememb in typedecl.Members) { if (typememb.Name == ".ctor") { ctor = (CodeConstructor)typememb; break; } } CodeMethodInvokeExpression invokeExpression = new CodeMethodInvokeExpression(); invokeExpression.Method = new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), "InitializeComponent2"); ctor.Statements.Add(invokeExpression); typedecl.Members.Add(ctor); //--------------------------------------------------------------------------- //This code will add the following line to the "InitializeMethod2" method // this.Load += new System.EventHandler(this.Form1_Load); member = new CodeMemberMethod(); foreach (CodeTypeMember typememb in typedecl.Members) { if (typememb.Name == "InitializeComponent2") { member = (CodeMemberMethod)typememb; } } CodeDelegateCreateExpression createDelegate1 = new CodeDelegateCreateExpression( new CodeTypeReference("System.EventHandler"), new CodeThisReferenceExpression(), "Form1_Load"); CodeAttachEventStatement attachStatement1 = new CodeAttachEventStatement(new CodeThisReferenceExpression(), "Load", createDelegate1); member.Statements.Add(attachStatement1); typedecl.Members.Add(member); //--------------------------------------------------------------------------- //Add and remove a label because otherwise the code to add the method seems to stay "inactive", //while in this way it works Label lbl = (Label)host.CreateComponent(typeof(Label)); host.DestroyComponent(lbl); //--------------------------------------------------------------------------- }
Debugging design time controls is not part of my daily job (I've never saw one, in web development), so while working on this case I had to figure out out to debug one of those... At the beginning I just needed to check a couple of variable values so to make things simple I just added a couple of MessageBox.Show() where I needed (like I used to fill my old ASP pages with Response.Write() statements long time ago). As you can guess this is not a practical approach. And at the same time I could not imagine to be the only person facing this problem, so there must be some article outside on the Internet dealing with this issue... and here is it, just in case you need it: Walkthrough: Debugging Custom Windows Forms Controls at Design Time.
Essentially what needs to be done is set the control's project as the startup progect, change the debug start action for the control you need to debug, setting it to "Start external program" and pointing it to the Visual Studio executable (default path is C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe), then set a breackpoint where you want to stop in its code and press F5 to start debugging. A new instance of Visual Studio is created, and from it you can open once again the Solution which contains the control you want to debug; now simply do what you need to run the control (add it to a new form for example) and hit the breakpoint (within the first Visual Studio instance, of course). Here we are, now you can step through, inspect variables and do whatever you usually do while debugging.
Note that the breakpoint icon in the first instance of Visual Studio will display the "breakpoint not hit" icon and tooltip until you cause the code to run, through the second Visual Studio instance
To have a well designed component I wanted to be able to create the InitializeComponent2 method where I wanted (possibly in the Designer.cs file to have it together with the standard InitializeComponent created by Visual Studio), remove the "#line" entries in the class constructor to have the code clean and easy to read and avoid the ugly trick to ad/remove a label to the form for activate the code. Unfortunately those are three problems I've still not resolved .
Creating a new method in Form1.cs is not a big deal, nor is adding some lines of code to it; but problems arise when trying to interact with the class constructor. This is not a protected method as we saw for InitializeComponent(), but no matter what I tried, there are always a few "#line" statements added:
public Form1() { #line 17 "C:\Temp\TestForMS\TestForMS\TestForm\Form1.cs" this.InitializeComponent(); #line default #line hidden this.InitializeComponent2(); }
Interestingly those nasty "#line" does not appear if we create a new code constructor, I guess that's because in that case we're working with a new object we created and have "full control" on it; for example creating a constructor overload and adding a call to my InitializeComponent2() produces perfectly clean code. Based on this assumption (still to verify, anyway) I tried to replace the class constructor with a new object created for this purpose, but when adding it to the class code graph still produces those lines... . Those are C# Preprocessor Directives and luckily the compiler does not complain, so for the moment I had to give up and live with them... ; but that's just for now, hopefully I'll find a way to get rid of them.
Ideally I wanted to create my InitializeComponent2() method to the Form1.Designer.cs file, but the default behavior is to add it to the Form1 class (in Form1.cs); the nice thing is that we have a LinePragma property which "Gets or sets the line on which the type member statement occurs", it contains a FileName and LineNumber property to clearly identify where the statement is executed. So I thought to use it to instruct the CodeDom to add my method in the code file I wanted with the following code (and quite a few variations of it):
//Create an InitializeComponent2() method member = new CodeMemberMethod(); member.Name = "InitializeComponent2"; foreach (CodeTypeMember typememb in typedecl.Members) { if (typememb.Name == "InitializeComponent") { member.LinePragma = new CodeLinePragma(); member.LinePragma.FileName = typememb.LinePragma.FileName; break; } } typedecl.Members.Add(member); //---------------------------------------------------------------------------
Despite my attempts, InitializeComponent2() is always created in Form1.cs... still something to look into .
Finally, the ugly workaround (or should I say "alternative solution"? ) of adding and removing the fake label to the form to activate the changes made to the code; I guess this invokes a sort of refresh mechanism, so I tried to dig into those calls with Reflector because I wanted to find the property to set or the method to call to activate the refresh without adding the label, but again with no luck... third (and hopefully last) point to clarify.
CodeDom offers some interesting capabilities to control developers but also has some limitations, here's a list (likely not complete):
Back to the sample treated in this post, I'll try to resolve those 3 open problems I mentioned above, but if any of you have some details to add (or a solution to share ) please leave a comment, I'll be happy to correct the post!
I got a couple of cases about this problem recently... Imagine this scenario: you install the .NET Framework 3.0 on your client, and then browse an ASP.NET based web site; you get a 406 HTTP return code from the web server, which means "Client browser does not accept the MIME type of the requested page" (see IIS status codes). Uninstalling the .NET Framework 3.0 corrects the problem, and you're finally able to successfully browse the site.
The problem proved itself in two different ways and apparently for two different reasons, but the underlying cause was actually the same. The 406 return code also means that any of the configuration limits has been reached, and digging into IIS logs we found that the problem was actually due to the length of the "Accept" header which has a limit of 256 bytes. Installing the .NET Framework 3.0 you receive support for a few additional file formats, here is how it looks the Accept header on Windows Vista (where the .NET Framework 3.0 is preinstalled):
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
In this case there was a custom ISAPI extension which was actually filtering the incoming headers, and was blocking our request because it exceeded the configured length: to resolve the problem the customer simply increased the size limit configured within that specific ISAPI to let the requests to through. Anyway another solution was to reduce the number of Accept types, just the standard ones.
The second customer was using a third party web server which we then discovered was able to accept a relatively short User-Agent string; the customer did not have any "power" on the web server to reconfigure it, so we only possibility left was reduce the header as much as possible on the client. The nice thing is that the User-Agent string is stored in a couple of registry key and even if that's not an ideal solution it was the only possibility we had, we decided to manually tweak those values; the first we looked at was HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform. On the right side you have a list of strings added to the User-Agent string which will be appended after the platform string (as the Post Platform name suggests); in case you need to add a custom string before the platform string, you can add a Pre Platform key. Just in case you're curious (or you're really desperate trying to reduce your User-Agent as much as possible) the platform part of the string is stored in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings > User Agent. For further infos have a look at Understanding User-Agent strings and Internet Explorer 7 User-Agent string.
If you need to tweak the User-Agent string be very careful and test it closely before deploying into production, since you might break client-side scripts which needs to identify the specific browser or platform version you're using; ASP.NET also relies on the User-Agent string to identify the browser and produce markup compliant with that specific platform (does it support javascript? embedding videos? is a mobile browser? does it support CSS? etc...). See Determining Browser Capabilities in ASP.NET and Control adapters.
Maybe some of you already know this trick, but if you don't... here it is
I've been running a Vista x64 as my main machine in office for a few months now, and when I had to analyze a Process Monitor trace received from a customer, but if the log was coming from a x86 machine (that's still the most common for customer calls we see today) I was not able to open it on my desktop, and always had to rely on my laptop (where I run Windows XP Pro) or on my second desktop (Windows 2003).
I took this for granted for a while, but then this morning I thought to have a look at the command line options (procmon /?) and got a nice surprise:
Tried it, and (of course) I was finally able to open the 32bit trace on my 64bit machine..