If ASP.NET pages are hit ONCE in both sites (pools/appdomains) to 'bootstrap' the CLR, all is well and FormsAuthTickets will be sharable and jointly decryptable.

.NET 1.1 and .NET 2.0 are installed on the systems

Both ASP.NET applications/vdirs are configured to use .NET 1.1 in IIS

PROBLEM: If the site that creates the FormsAuthenticationTickets instead has the CLR 'bootstrapped' into the process/pool/domain by hitting a Classic ASP page that calls the COM-interop DLL, it causes later FormsAuthentication crypto to produce tickets that can't be decrypted by the other application.

QUESTION: What's going on? Both sites have web.config files with synchronized machinekeys (the machinekey is used by FormsAuth).

PARTIAL ANSWER: Turns out that if the Classic ASP page (that uses the .NET object that is used via COM) is hit first then .NET 2.0 gets loaded up inside that IIS Managed Application even if the ASP.NET application is set to use .NET 1.1!

Am I missing something? Does this mean that I can't have two classic ASP apps that use .NET objects via Interop, one using 1.1 and one using 2.0 on the same computer? Seems like it.

The issue is that ASP.NET doesn't start up the CLR because it's not hit first. The Classic ASP page is hit first, and apparently can't control the CLR version it gets! By the time someone hits an ASP.NET page, the CLR is already loaded up with a different version.

HERE'S THE KICKER: I don't yet understand why (on Win2k3, in our tests) the problem only happens when the anonymous identity for the web site is a domain account. If the anonymous identity was left as the default IUSR_xxxx, the CLR loaded is still 1.1 when a CCW is created. (!)

OUR WORKAROUND: We just let .NET 2.0 load up anyway and run our 1.1 application. We have to synchronize the machinekeys in BOTH CLR versions' machine.config files though.

Kudos to Peter Wong for figuring this stuff out. Now the question is - is there a good workaround or better explanation? My guess is we'll hear "functions as designed" but it seems to me that even though you can't indicate in the COM Registration stuff in the Registry what CLR Version to use (it's listed in the Registry, but the values are ignored) there should be some way.

Repro attached. Unzip this file into a c:\inetpub\wwwroot\wong test and make an IIS Application (isolated, or in its own AppPool in IIS). Get Process Explorer from SysInternals and hit x.asp after a fresh reset.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

"However, one of the ASP.NET sites also has some Classic ASP pages in it. Still, no worries, right?"

A good question to ask here would be, "Is the amount of time and effort spent debugging this weird problem greater than or less than the amount of time that would have been spent porting the classic ASP pages to ASP.NET?"

Ooh, fun corner case. Because of course the rule is that when calling into a .NET object via COM interop, the newest version of the runtime is always loaded. It just so happens to be running under IIS this time, I guess.

>If the anonymous identity was left as the default IUSR_xxxx, >the CLR loaded is still 1.1

Now that's pretty weird. Any chance the IUSR_ account doesn't have permissions to the .NET 2.0 directories? Probably not.

I know this isn't a real fix on the problem, but is there a reason you can't simply split the asp files and the asp.net files into seperate directories? You can then apply a seperate application pool to each directory. Since application pools use seperate threads and, thus, seperate CLR instances, you should therefore be fine.

John Christensen

Wednesday, August 23, 2006 12:28:52 AM UTC

We can't split the ASP.NET and ASP app up, because the ASP app is "officially in charge" while the ASP.NET app is actually a "plug-in"...

Sigh...all good points and good info. FAD.

Scott Hanselman

Wednesday, August 23, 2006 2:30:30 AM UTC

Scott, we ran into the same problem when a VB6 client called a .NET component (that was built under v1.1) on a machine that had v2.0 and v1.1 of the framework. How we fixed it? We the requiredRuntime element within our app.config for the VB6 client. This fixed the issue and appears to be the "easiest" workaround. You can see my full explanation here: http://www.lozanotek.com/archive/2006/03/14/7928.aspx

We've had the same issue. We have tried the requiredRuntime option in the web.config file and that hasn't worked. We are getting ready to call PSS to see if there is anything that can be done.

Here's a thread where we've talked about different things to try. None have worked for us, but just in case they are helpful for someone else troubleshooting:http://forums.iis.net/thread/1363620.aspx

If we come up with a solution, I'll post back in the comments here.

Mick

Friday, August 25, 2006 12:07:52 PM UTC

You could call CorBindToRuntimeEx to force a load of .NET 1.1. You could either do this in a C++ COM component, which you could create first thing on your .ASP pages, or in a tiny ISAPI filter.

Another way: Have the first request to an .ASP page redirect to an .ASPX page (to force a load of .NET 1.1) and then back to the .ASP page. You could make sure that this only happens once via a flag stored in .ASP application state.

Greg

Wednesday, August 30, 2006 1:30:50 PM UTC

Greg is right above. I called PSS and they gave me an ISAPI dll that does just what Greg mentioned. Basically, it loads when the web app first loads and makes sure the 1.1 framework is loaded. Here is the function by itself:

They sent me a full solution file if anyone is interested. Scott, I can send it to you if you want to link to it on this blog entry.

Mick

Wednesday, August 30, 2006 5:41:22 PM UTC

Forgot to fix one thing above. This line: LPWSTR pszFlavor = L"wks"; should be LPWSTR pszFlavor = L"svr"; if you are using a multiprocessor server.

Mick

Wednesday, August 30, 2006 10:22:13 PM UTC

Nice stuff Mick. I'll update the post and make a new one as well. That's kind of what I was thinking but you've saved us a day. I have the skeleton of an ISAPI filter and I was going to take Greg's advice. A filter is the first place to jump in an influence, and GetFilterVersion is only called once, so it's a clever hack.

Scott Hanselman

Thursday, September 07, 2006 4:15:05 PM UTC

How about adding a "dllhost.exe.config" file into your c:\winnt\system32 directory? Classic ASP pages will run using the DLLHost if the protection is Medium or High. I think this also applies to IIS5+.

In IIS6.0, you can set different Application Pools, although I think for classic ASP pages, the DLLHost.exe.config will still work? I know the processModel is different in IIS6.0, so I'm not entirely sure about this solution under IIS6.0...

Mike

Thursday, September 07, 2006 4:34:04 PM UTC

The dllhost thing will work, but it affects EVERYTHING that usesdllhost, which isn't cool. It also doesn't allow for mixing 2.0 and 1.1.

Here's how we solved it:http://www.hanselman.com/blog/SOLVEDHowToForceIISToLoadACertainVersionOfTheNETCLR.aspx

True. But, if you have classic ASP using some COM Interop DLL's built with Framework 1.1, then only those older classic ASP should be using dllhost. Or, is this assumption incorrect? If I have ASP.NET sites running on Framework 2.0, won't those still use 2.0, or are those then forced to use 1.1? Jeeez, I guess they are...

Mike

Comments are closed.

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