SharePoint 2010 Sandboxed Solutions: Full Trust Proxies

Posted on 12/7/2009 @ 4:34 PM in #SharePoint by | Feedback | 7698 views

In this blog post, I will dissect every aspect of sandbox solutions as they apply to SharePoint 2010. 
The below will turn into links as newer blog posts are published.

Some of the content below uses excerpts from my book on SharePoint 2010.

________________________________________________________________

Table of Contents:

    1. The definitive guide (back to table of contents).
    2. The basics of a sandbox solution
    3. Sandbox solution architecture and restrictions
    4. Sandbox solution monitoring, management and deployment 
    5. Sandbox solution validations
    6. Sandbox solutions full trust proxies <--- you are here

________________________________________________________________

Boundaries and restrictions? You expect me to deliver a real solution by putting me under boundaries and restrictions? I think not!

The thing is, sandbox solutions are great, because they restrict the average joe developer, and they allow the farm administrator to manage and monitor and even validate. But, sometimes you need to negotiate the boundaries of what a sandbox solution can do! For instance, lets say, we are presented with a crazy requirement that through a sandbox solution, you need to create a file on the disk of the web server! To make things even more interesting, the sandbox solution packages a webpart, which presents the user with a simple textbox. The file contents will be whatever the user typed into that text box, through a web based solution, in a sandboxed solution! Yes, it's a crazy requirement, but I am using this only as an extreme example to demonstrate my point!

The solution to this problem is to write a full trust proxy!

The obvious question you may have here is, if you can get around the whole notion of sandbox solutions using full trust proxies, then why even bother with sandbox solutions? What is that boundary good for, if it can be broken!?

Well, take heart! The idea here is to break your architecture down into two major pieces,

  1. The first piece that you will deliver completely using sandbox solutions!
  2. The second piece is the API you will build using full trust proxies, that the sandbox solutions can leverage.

So the big idea here is that the architect creates an acceptable API, that is available for all sandbox solutions to use! Thus, there is still significant control on what can portions of the API are open for access. The process of opening this API using a full-trust proxy, is in fact rather simple to write. It basically involves 5 simple steps.

  1. Decide on what operation you wish to perform in the full trust proxy, and implement it as a class that inherits from Microsoft.SharePoint.Usercode.SPProxyOperation
  2. Decide on what arguments need to be passed to the SPProxyOperation that you just created, this is a serializable class you implement that inherits from Microsoft.SharePoint.Usercode.SPProxyOperationArgs
  3. Compile the above to classes in a DLL, strong name the DLL, put it in the GAC.
  4. The above steps would create a full trust proxy, which you will then need to register for use with SharePoint.
  5. Finally, you consume the proxy in a sandbox solution using the SPUtility.ExecuteRegisteredProxyOperation method.

See, I told you it's rather simple! Basically in short you create the operation, create the arguments, register the proxy with SharePoint, and use it merrily!

Lets solidify this understanding with real code. I am going to write a simple sandbox solution that displays the user a simple UI as shown below:

 

The idea here is that the user will type in some text, and hit the "Create" button. Whatever text was entered in the textbox, will be written to a file called " C:\inetpub\wwwroot\wss\VirtualDirectories\80\SampleFile.txt".

  1. Start by creating an empty SharePoint project. Create a folder within the SharePoint project, and call the folder "ProxyCode".
  2. Inside ProxyCode, add a class called "FileCreateOperation". The code for FileCreateOperation can be seen below. Note that I overrode the Execute Method. Execute returns an object. As long as my return type is serializable, I can return whatever I wish to the calling code (in this case a sandbox solution webpart, which I will write shortly!).
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using Microsoft.SharePoint.UserCode;
   6: using System.IO;
   7:  
   8: namespace SandBoxWebPartWithProxy.ProxyCode
   9: {
  10:     public class FileCreateOperation : SPProxyOperation
  11:     {
  12:         public override object Execute(SPProxyOperationArgs args)
  13:         {
  14:             if (args != null)
  15:             {
  16:                 FileArgs fileArgs = args as FileArgs;                
  17:                 FileStream fStream = 
  18:                     new FileStream(@"C:\inetpub\wwwroot\wss\VirtualDirectories\80\SampleFile.txt", 
  19:                         FileMode.CreateNew);
  20:                 fStream.Write(
  21:                     System.Text.ASCIIEncoding.ASCII.GetBytes(fileArgs.FileContents), 0, 
  22:                     fileArgs.FileContents.Length);
  23:                 fStream.Flush();
  24:                 fStream.Close() ;
  25:                 return fileArgs.FileContents;
  26:             }
  27:             else return null;
  28:         }
  29:     }
  30: }
 
3. Similary, add another class under ProxyCode called FileArgs. This will serve as the input arguments sent to the proxy. The code for FileArgs can be seen below. As you can see, the FileArgs class is decorated with the Serializable attribute.
 
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using Microsoft.SharePoint.UserCode;
   6:  
   7: namespace SandBoxWebPartWithProxy.ProxyCode
   8: {
   9:     [Serializable]
  10:     public class FileArgs : SPProxyOperationArgs
  11:     {
  12:         public string FileContents { get; set; }
  13:  
  14:         public FileArgs(string fileContents)
  15:         {
  16:             this.FileContents = fileContents;
  17:         }
  18:     }
  19: }

4. Now, usually you would create the proxy as a separate DLL. But for this example, I will keep things simple and also go ahead and add a webpart to the project. So, go ahead and add a new FileCreateWebPart to your project. The code for the FileViewWebPart can be seen below. As is evident from the code below, I am using the SPUtility.ExecuteRegisteredProxyOperation method to call code in the full trust proxy. Thus a registered proxy (which we will register next), is available for any sandbox to use - it is not split per site collection!

   1: using System;
   2: using System.ComponentModel;
   3: using System.Runtime.InteropServices;
   4: using System.Web.UI;
   5: using System.Web.UI.WebControls;
   6: using System.Web.UI.WebControls.WebParts;
   7: using Microsoft.SharePoint;
   8: using Microsoft.SharePoint.WebControls;
   9: using Microsoft.SharePoint.Utilities;
  10:  
  11: namespace SandBoxWebPartWithProxy.FileCreateWebpart
  12: {
  13:     [ToolboxItemAttribute(false)]
  14:     public class FileCreateWebpart : WebPart
  15:     {
  16:         private TextBox fileContents = new TextBox();
  17:         private Button createFileButton = new Button() { Text="Create" };
  18:         private Label results = new Label();
  19:  
  20:         public FileCreateWebpart()
  21:         {
  22:             createFileButton.Click += (object sender, EventArgs e) =>
  23:             {
  24:                 results.Text = 
  25:                     SPUtility.ExecuteRegisteredProxyOperation(
  26:                     "SandBoxWebPartWithProxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=64b818b3ff69ccfa",
  27:                     "SandBoxWebPartWithProxy.ProxyCode.FileCreateOperation", 
  28:                     new ProxyCode.FileArgs(fileContents.Text)).ToString();
  29:             };
  30:         }
  31:  
  32:         protected override void CreateChildControls()
  33:         {
  34:             Table layoutTable = new Table();
  35:             layoutTable.Rows.Add(new TableRow());
  36:             layoutTable.Rows[0].Cells.Add(new TableCell());
  37:             layoutTable.Rows[0].Cells.Add(new TableCell());
  38:             layoutTable.Rows.Add(new TableRow());
  39:             layoutTable.Rows[1].Cells.Add(new TableCell() { ColumnSpan = 2 });
  40:  
  41:             layoutTable.Rows[0].Cells[0].Controls.Add(fileContents);
  42:             layoutTable.Rows[0].Cells[1].Controls.Add(createFileButton);
  43:             layoutTable.Rows[1].Cells[0].Controls.Add(results);
  44:  
  45:             this.Controls.Add(layoutTable);
  46:  
  47:             base.CreateChildControls();
  48:         }
  49:  
  50:         protected override void RenderContents(HtmlTextWriter writer)
  51:         {
  52:             base.RenderContents(writer);
  53:         }
  54:     }
  55: }

FINALLY, build and put the DLL in the GAC, and register the proxy with SharePoint! (Please see How to register your full-trust proxy with SharePoint 2010).

You are ready to go! But I want to share one more tip with you! Your proxy runs in the SPUserCodeService.exe. So everytime you make changes to your project and redeploy the DLL to GAC, you will need to restart the "SPUserCodeV4" service. You can stop the service by using "net stop SPUserCodeV4", and start it again by using "net start SPUserCodeV4".

Now with your webpart compliled, and deployed, go ahead and add it to a webpart zone! Type in some text, and click on the "Create" button. You should see a sample file get created at the path you specified in step #2 above. If you need help debugging, remember that the webpart itself runs under SPUserWorkerProcess.exe, but the full-trust proxy runs under SPUserCode.exe. So remember to attach to the right process!

Sound off but keep it civil:

Older comments..


On 7/13/2010 12:17:58 PM 2mp said ..
Hi,

I am trying out the example above but receiving the following error.

"This code is calling a privileged proxy operation that is not available on this farm.


Please contact your farm administrator."

The proxy registration looks ok. Any idea/suggestion on how to troubleshoot this problem.

Thanks.

/2mp


On 8/12/2010 6:57:25 AM Tsvetoslav Kovachev said ..
Is it possible to use IScriptControls (ajax-enabled controls) in a sand-box solution?


On 1/3/2011 2:11:59 PM Alaa Sahmarani said ..
Please I need help.

I'm reading your great Book "Building Solutions for sharepoint 2010". I downloaded the source code from Apress,and put the DLL in the GAC and then I ran the command line command to register the DLL. But still I got the Error : This code is calling a privileged proxy operation that is not available on this farm. Please contact your farm administrator."

Could you kindly advise.


Thank you


On 1/27/2011 6:37:35 AM Victor said ..
Hi gents. I had the same problem ("This code is calling a privileged proxy operation..."). And for me it was solved by registrating Full-Trust proxy class to SPUserCodeService.Local.ProxyOperationTypes-collection.

var userCodeService = SPUserCodeService.Local;


var proxyOpertaionType = new SPProxyOperationType(


proxyAssemblyName,


proxyTypeName );


userCodeService.ProxyOperationTypes.Add( proxyOpertaionType );


userCodeService.Update();

This can be done in the feature event receiver for example.


On 6/19/2012 7:25:54 AM NGK Prasad said ..
Even after Registering the Class to UserCodeService also, Error retained for me. I Have implemented the registering process through event reciever.


On 2/13/2013 3:44:25 PM Sean said ..
Anyone solve this problem yet? I am running into the same issues and have followed all guidance above. Still getting the error on SPUtility.ExecuteRegisteredProxyOperation