If you’re using the multi-tenant features of SharePoint, you will want tenants to use the sandbox. But, you will quickly find limitations. For example, developers cannot call a web service, read data from a external database, or write to the event log. One solution is for the farm administrator to deploy a full trust proxy that developers can use. Microsoft has a good description
here, but there are no good examples. So I created one.
Let’s create a full trust proxy that will allow developers to write to the event log. Sandbox code is executed in
SPUWorkerProcess.exe (see diagram below). The Code Access Security (CAS) policy in this process does not allow developers to access the event log. Therefore, we will write a full trust proxy that will marshal the call to
SPUWorkerProcessProxy.exe which can access the event log.
Figure 1: http://msdn.microsoft.com/en-us/library/ff798433.aspx
Create the FullTrustProxy
Start by creating a new “Empty SharePoint Project” that is deployed as a “farm solution”. Remember this is the solution that the farm administrator deploys. The tenant developer’s code will be deployed as a sandbox solution.
Next, add a new class called EventLogProxyArgs that inherits from
Microsoft.SharePoint.UserCode.SPProxyOperationArgs. This is the class that will hold the data that needs to be marshaled from
SPUWorkerProcess.exe to
SPUWorkerProcessProxy.exe. Therefore it needs to be marked with a
Serializable attribute. The code is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.UserCode;
namespace BrianBeach.FullTrustProxy.Diagnostics
{
[Serializable]
class EventLogProxyArgs : SPProxyOperationArgs
{
private string source;
private string message;
public string Source
{
get { return source; }
set { source = value; }
}
public string Message
{
get { return message; }
set { message = value; }
}
}
}
|
Now, add a another class called
EventLogProxy that inherits from
Microsoft.SharePoint.UserCode.SPProxyOperation. You will need to implement the
Execute method. The execute method is passed a copy of the
EventLogsProxyArgs we created above. The code is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.UserCode;
namespace BrianBeach.FullTrustProxy.Diagnostics
{
class EventLogProxy : SPProxyOperation
{
public override object Execute(SPProxyOperationArgs args)
{
string source = ((EventLogProxyArgs)args).Source;
string message = ((EventLogProxyArgs)args).Message;
System.Diagnostics.EventLog.WriteEntry(source, message);
return null;
}
}
}
|
Create the Feature
Start by adding a new farm feature and fill out the title and description.
Now add an event receiver. The event receiver will register the proxy with SharePoint. The code is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.UserCode;
using Microsoft.SharePoint.Administration;
namespace BrianBeach.FullTrustProxy.Diagnostics.Features.Feature1
{
[Guid("2f2d1b78-c30b-4197-ba44-06052b97448c")]
public class Feature1EventReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPUserCodeService service = SPUserCodeService.Local;
string assembly = "BrianBeach.FullTrustProxy.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba8f794a3acc2269";
string type = "BrianBeach.FullTrustProxy.Diagnostics.EventLogProxy";
SPProxyOperationType operation = new SPProxyOperationType(assembly, type);
service.ProxyOperationTypes.Add(operation);
service.Update();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPUserCodeService service = SPUserCodeService.Local;
string assembly = "BrianBeach.FullTrustProxy.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba8f794a3acc2269";
string type = "BrianBeach.FullTrustProxy.Diagnostics.EventLogProxy";
SPProxyOperationType operation = new SPProxyOperationType(assembly, type);
service.ProxyOperationTypes.Remove(operation);
service.Update();
}
}
}
|
Deploy and Test
You’re ready to deploy your package. Tenant developers can call the proxy from the sandbox. Here is an example:
1
2
3
4
5
6
7
|
string assembly = "BrianBeach.FullTrustProxy.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e6c3143148ad819b";
string type = "BrianBeach.FullTrustProxy.Diagnostics.EventLogProxy";
EventLogProxyArgs args = new EventLogProxyArgs();
args.Source = “SP2010 Sandbox”;
args.Message = “A message from the sandbox!!!”;
SPUtility.ExecuteRegisteredProxyOperation(assembly, type, args);
|
Now, if you’re like me, the thought of writing seven lines of code to write one line to the log is crazy. Therefore, I added one more class to my solution with a helper method tenant developers can call.
Add a class called EventLog. You will need to mark this class with the
AllowPartiallyTrustedCallersAttribute attribute so that users can call it from the sandbox.
NOTE: I also marked the EventLogProxy and EventLogProxyArgs classes as internal so that developers would not be confused by them. The code is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Utilities;
[assembly: System.Security.AllowPartiallyTrustedCallersAttribute()]
namespace BrianBeach.FullTrustProxy.Diagnostics
{
public class EventLog
{
public static void WriteEntry(string Source, string Message)
{
string assembly = "BrianBeach.FullTrustProxy.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e6c3143148ad819b";
string type = "BrianBeach.FullTrustProxy.Diagnostics.EventLogProxy";
EventLogProxyArgs args = new EventLogProxyArgs();
args.Source = Source;
args.Message = Message;
SPUtility.ExecuteRegisteredProxyOperation(assembly, type, args);
}
}
}
|
At this point a developer can write to the event log with a single line of code as follows:
1
|
EventLog.WriteEntry(“SP2010 Sandbox”, “A message from the sandbox!!!”);
|
Finally, you can download the solution
here.
you will likely run into issues. If you need to check which proxies are registered, you can use the following PowerShell script.
1
2
|
$Service = [Microsoft.SharePoint.Administration.SPUserCodeService]::Local
$service.ProxyOperationTypes | Format-List
|