Logo SharePoint Thoughts   Downloads   About   Up to Bluedog Limited
Q&A on the interface loader technique and AppDomains
Posted on 2/20/2007 4:51 PM by Maurice Prather
Update 2/20/2007
Updated path to the perf mon image - and included it in the post now the v3 allows images in rich text fields :).

One of my first posts discussed the interface loader technique which allowed you to move beyond impersonation limitations of the SharePoint OM.  The original post did not discuss the details of how a RevertToSelf or LogonUser/Impersonate could/should be used.  Instead, I brushed over the topic and left it for the reader to determine which methodology was best for their application.

Over the past couple of months, I've received several email discussing a variety of issue surrounding impersonation and the SharePoint OM.  In each case, I would always offer two alternatives: try using AppDomains or use a COM+ solution.  Most folks decided to use the AppDomain technique... and invariably some of them would come back with failures.  Good or bad, my thought process was invariably "check your impersonation code, you must not be doing it right... the article on the loader technique is accurate and works".

Well, if enough people run across problems, then it's time to double check the source to make sure all the i's have been dotted and the t's crossed.  The truth is I was somewhat right, but I had forgotten a small trick that I had used in my own code.  Without a 1-line adjustment/fix to the impersonation code, you would run into the infamous "Cannot complete this action" error message even though everything else appeared to be working correctly.

On to a Q&A style to get things working...

What's the one line fix?
After you've called completed your impersonation magic (i.e. RevertToSelf, LogonUser, Impersonate), call the Impersonate method one more time in the context of the newly established identity.  Although this seems a bit counterintuitive, calling impersonate one more time resets the state in the Context in way that ensures the impersonation "takes hold".  If you don't do this, you will eventually run into the "Cannot complete this action" error message. 

Add the following line to your impersonation library immediately after you've done the heavy lifting of calling RevertToSelf, LogonUser, or WindowsIdentity.Impersonate (IntPtr):

// If the following call is not made, the Context is somewhat out of whack...
WindowsIdentity.GetCurrent().Impersonate();

Back to basics... isn't impersonation not supported?
Yes.  In a simple, inline manner as described by KB article 892866, impersonation is not supported.  You can't do the following and expect it to work all the time.

// Running as normal user...
// Now switch to privileged user...
MethodWhichImpersonates ();

// Execute a privileged task of some type. For example...
CreateSiteForMyUnderprivilegedUser ();

Doesn't using AppDomains or COM+ contradict the KB article?
Not really.  The whole purpose of using those techniques is to remove any knowledge of the http context.  The basis for the KB article is that the SharePoint OM may perform security checks relative to the original request/context identity and not the newly established one. 

If the http context space is eliminated, the SharePoint OM operates as though it's a console application.  Yes, the starting point may have well been an http request, but the code running in secondary AppDomains or as a COM+ application has no knowledge of the context.  This is where the power of those techniques shine through.  You get all the benefits of a console app. 

How about if I use the 1-line fix to my inline code... will that make the KB article go away?
No. You're still under the bounds of the KB article.  There is no guarantee everything will work as desired when you use impersonation and remain within the http context space.

Using the AppDomain technique, what type of parameters should I pass to methods?
Simple, serializable types.  Use simple types as method parameters (i.e. string, int, custom classes).  Whatever you do, don't try to pass an Assembly or any of the context types.  If you do so, you will force the operating domains to load (and subsequently lock) assemblies which were once isolated.

Should I specify shadow copying and other advanced properties for my AppDomain?
Yes, it's probably a good idea to walk through all of the AppDomainSetup methods and properties.  The following is how I generally setup my AppDomains to ensure I don't lock the main assemblies by caching them to a unique shadow copy folder:

AppDomainSetup appDomainSetup       = new AppDomainSetup();
appDomainSetup.ApplicationBase      = applicationBase;  // this is the value of this.Page.MapPath("/")
appDomainSetup.ApplicationName      = AppDomain.CurrentDomain.SetupInformation.ApplicationName + "_MyAppName";
appDomainSetup.CachePath            = AppDomain.CurrentDomain.SetupInformation.CachePath;
appDomainSetup.ConfigurationFile    = "web.config";
appDomainSetup.DisallowCodeDownload = true;
appDomainSetup.PrivateBinPath       = "bin";
appDomainSetup.PrivateBinPathProbe  = "true";
appDomainSetup.ShadowCopyFiles      = "true";

this.appDomain = AppDomain.CreateDomain (domainName,
                                         AppDomain.CurrentDomain.Evidence,
                                         appDomainSetup);

Please review the settings to ensure they generate the behavior you desire - there's a chance you don't want to deal with shadow copied files.

Can the AppDomain technique be used in a partially trusted environment?
Yes, your web part code (or whever you implement this technique) does not require Full trust.  This is a good homework assignment - can you tell me what set of permissions are needed to make the technique work?  :-)

How does the AppDomain technique look like with a debugger attached?
If you attach a debugger to the process, the output should look like the following color-coded section.  I've trimmed a good bit of the output to reduce the overall size of the post, but the general outline is still there.  The gray highlighted section is the normal information regarding your virtual server's primary AppDomain.  VS will list all the assemblies loaded in the active w3wp process.  When you commit an action which fires up a new AppDomain, the yellow highlighted section will represent all the actions taken by your new AppDomain.  Note that "DomainX" is created, then the necessary assemblies are loaded into that AppDomain.

In a perfectly working and synchronous world, the two section (gray and yellow) should be completely separate and not intermixed.  This is easy to witness on a developer's machine because you can isolate server usage.

'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
Auto-attach to process '[5772] w3wp.exe' on machine 'WESTGATE' succeeded.
'DefaultDomain': Loaded '\gac\system.web\1.0.5000.0__b03f5f7f11d50a3a\system.web.dll', No symbols loaded.
'DefaultDomain': Loaded '\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'DefaultDomain': Loaded '\gac\system.enterpriseservices\1.0.5000.0__b03f5f7f11d50a3a\system.enterpriseservices.dll', No symbols loaded.
'DefaultDomain': Loaded '\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll', No symbols loaded.
'/LM/W3SVC/1285842265/Root-3-127716846597562722': Loaded 'p3odzxx_', No symbols loaded.
'/LM/W3SVC/1285842265/Root-3-127716846597562722': Loaded '\gac\microsoft.sharepoint.intl\11.0.0.0__71e9bce111e9429c\microsoft.sharepoint.intl.dll', No symbols loaded.
'/LM/W3SVC/1285842265/Root-3-127716846597562722': Loaded 'RegexAssembly3132_0', No symbols loaded.
'/LM/W3SVC/1285842265/Root-3-127716846597562722': Loaded 'RegexAssembly3132_0.dll', No symbols loaded.
'Domain4': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'demo': Loaded '\microsoft.net\framework\v1.1.4322\temporary asp.net files\root\13e9a3d7\ec3137a3_testApp\assembly\dl2\bf0ad338\ee227dea_90bdc501\bluedoglimited.webparts.scratch.dll', Symbols loaded.
'demo': Loaded '\gac\system.web\1.0.5000.0__b03f5f7f11d50a3a\system.web.dll', No symbols loaded.
'demo': Loaded '\gac\microsoft.sharepoint\11.0.0.0__71e9bce111e9429c\microsoft.sharepoint.dll', No symbols loaded.
'demo': Loaded '\gac\system.xml\1.0.5000.0__b77a5c561934e089\system.xml.dll', No symbols loaded.
'demo': Loaded '\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'demo': Loaded '\microsoft.net\framework\v1.1.4322\temporary asp.net files\root\13e9a3d7\ec3137a3_testApp\assembly\dl2\3926c5e3\27119fd6_8dbdc501\bluedoglimited.webparts.syndication.dll', No symbols loaded.
'demo': Loaded 'yzqk0nww', No symbols loaded.
'demo': Loaded 'vqusmshf', No symbols loaded.
The program '[5772] w3wp.exe: demo' has exited with code 0 (0x0).

Most importantly, though, note the very last line - "w3wp.exe: demo" has exited.  If you don't see your custom AppDomain exiting (in this example, my AppDomain was called "demo"), you've got a memory leak ... so be sure you always properly dispose of the your custom AppDomain. Technically, garbage collection will likely take care of reclaiming your memory, but don't wait - just do it yourself.

What does all of this look like in perfmon?
The following image shows what occurs when a postback is executed that fires off method which creates a new AppDomain and creates a site for an anonymous user. 

Note how the AppDomain count goes up by 1, process executes, then the count goes down by 1.  Correspondingly, available memory decreases and then returns to its original level.

Ok... how about a demo?
The following page contains a web part that allows an anonymous user to create a new site, get some data about a user, and get a list of sites.  It took me less than 10 minutes to create this web part.  In fact, I spent more time touching up the UI than I did coding up the interface loader.  :)

AppDomain - Interface Loader - Demo Page

Swing by check it out... then go crank your own custom apps that do all sorts of neat things!

-Maurice

re: Q&A on the interface loader technique and AppDomains
Maurice,
Thanks for the great info on creating a secondary app domain. I have implemented your sample code. It works great when I run it as the admin. However, I'm getting the following exception when I try to run as any other user:
Exception has been thrown by the target of an invocation.
Any ideas? I've tried to look around for anybody else trying this technique and having the same problems, but have not found any help so far. For impersonation I am using the RevertToAppPool code found on Todd Bleeker's blog.
Thanks for any help.
Phil @ 3/6/2006 10:59 AM
re: Q&A on the interface loader technique and AppDomains
Maurice,
I'm still having trouble. The following line of code from your sample is where the trouble is at:
using (RemoteInterfaceFactory remoteFactory = new RemoteInterfaceFactory (Page.MapPath("/")))
This line is producing the following exception when I run it as a non-admin account only:
Exception has been thrown by the target of an invocation.
 
Any suggestions or hints would be appreciated. Thanks.
Phil @ 3/7/2006 8:33 AM
re: Q&A on the interface loader technique and AppDomains
Maurice,
Sorry for the constant nagging. I did some more digging. I found out that the exception being thrown when my code tries to load the RemoteMethods object is actually a System.NotSupportedException
This does not occur when the code is running as the server's admin or when running as the account used to start the portal appdomain. I'm really in a jam here. Any hints or help would be appreciated. Thanks.
 
Phil @ 3/7/2006 3:21 PM
re: Q&A on the interface loader technique and AppDomains
I am getting the same error.  It appears to be a permissions/security error as if I change the web.config from WSS_Medium to Full the code runs without a hitch.  Any suggestions?  if so send drop me a line @ mbollhoefer@kaplan.edu
Anonymous User @ 9/19/2006 10:36 AM
re: Q&A on the interface loader technique and AppDomains
Hi Maurice,
 
Any chance you could post the code for the demo web part you have setup on your site - it would be useful to be able to debug through it and see how it's supposed to be done :)
 
If you could email me the code at chris@blancoworld.co.uk I'd really appreciate it.
 
Many thanks in advance,
 
Chris
Chris White @ 2/21/2007 1:12 AM
re: Q&A on the interface loader technique and AppDomains
Maurice:
 
Thanks so much for the article, very informative!!  Just one thing that I can't seem to figure out.  For many reasons, I need to do this from the \bin folder of the site, no GAC allowed.  Any suggestions?
 
John
John @ 3/1/2007 11:43 AM
re: Q&A on the interface loader technique and AppDomains
John,
 
Your assembly can live in the Bin, but you will need to grant the assembly the required permissions in order to complete the tasks.  The default set (WSS_Minimal) is insufficient.  As I've mentioned in the articles, the exercise of determining the set of permissions was left to the end user (it is somewhere between WSS_Minimal and Full).
 
-Maurice
Maurice Prather @ 3/20/2007 6:26 PM
re: Q&A on the interface loader technique and AppDomains
Chris,
 
Minus the impersonation (or revert to self) code, the AppDomain articles list out the code required accomplish something using this approach.  I'm not sure what else I could provide.
 
-Maurice
 
Maurice Prather @ 3/20/2007 6:29 PM
Code Sample Please
I'am a complete newbee to Sharepoint and this really is above my head. I've managed to create a Webpart that uses the SP Object Model. On deploying this webpart, I've come across the "annoying authentication dialogs", and found out that users that have read access permisions on the Site, can't access the Webpart. I've managed to copypaste your code and have it compile, but it's giving me a "System.Security.SecurityException" on "System.AppDomain.get_Evidence()" at this.appDomain = AppDomain.CreateDomain ("SecondaryAppDomain", AppDomain.CurrentDomain.Evidence); As you made a working Demo, could you publish the source code in your download section? That would probably be a great help to me. Cheers
GUI Junkie @ 6/13/2007 8:31 AM
re: Q&A on the interface loader technique and AppDomains
GUI Junkie,
 
If you follow the posts, you have the code that is required.  The posts were authored (i.e. copy/paste) from working code.  The only parts I left out where the impersonation/reverttoself sections and the code access security lockdowns.
 
I'm not really sure what else to offer in terms of code. 
 
-Maurice
 
Maurice Prather @ 6/18/2007 10:53 AM
re: Q&A on the interface loader technique and AppDomains
I implemented the same approach, and found out that my Document Library event handlers completely and uttery cease firing when the file updating/handling is done in such separate AppDomain. go figure.
kert @ 12/8/2007 11:26 AM
RSS feed
Microsoft Certified Master
MVP Logo
Follow me on Twitter!
Keyword Search
 
View by category
 

Disclaimer:
The contents of this site represent thoughts and opinions of the authors , not those of anyone else - such as past, present and future employers.  This a forum of the exchange of ideas centered on SharePoint technologies.  It is not a support channel.  :)

Copyright © 2004-2010 Maurice Prather, Inc. All rights reserved.