Tag Archives: Microsoft SharePoint

Recycle IIS App Pool from Script

My colleague, Carlos Fernandez, sent me this CSCRIPT command for recycling the an IIS application pool:

%windir%system32cscript.exe c:windowssystem32iisapp.vbs /a "SharePoint – 80" /r

Tired of manually recycling the app pool each time you make a code change to your SharePoint web part?  No problem, add the following command to the post build events in Visual Studio.

Debugging SharePoint, which Process?

When developing 3rd party components for SharePoint you cannot avoid debugging.  Debugging usually involves attaching to a W3WP.EXE process – IIS host for a SharePoint application, from within Visual Studio. 

When presented with the attach to process dialog box, you should typically see two to three instances of W3WP.EXE, one for central administration site, maybe another for the SSP administration site, and one for your web application:


So.. which process should you attach?  Most developers (myself included) attach to all – just to be sure.  But what if you want to know exactly which process to attach? 

My colleague – Kevin Vieira gave made me aware of IISAPP.EXE – a nice built in tool to list all of IIS hosted web applications with associated PID:

W3WP.exe PID: 236   AppPoolId: SharePoint – 80
W3WP.exe PID: 244   AppPoolId: SharePoint – 5000
W3WP.exe PID: 3616   AppPoolId: SharePoint Central Administration v3

SharePoint Content Structure and Reports Limit

We ran into an interesting issue this week where all reports at the site collection level within "Content Structure and Reports" stopped working.  The log included the following error:

Query Execution threw SPException: The query cannot be completed because the number of lists in the query exceeded the allowable limit
  10/10/2008 11:38:19.16     w3wp.exe (0x09F8)                           0x07F8    CMS                               Site Management                   622h   
Unexpected    SMReportsData GetQueryResults – Query Execution threw SPException: The query cannot be completed because the number of lists in the query
exceeded the allowable limit. For better results, limit the scope of the query to the current site or list or use a custom column index to help reduce the number of lists.    

My esteemed colleague Carlos Fernandez uncovered the following fix…


The default lists limit for the query generated by these reports is a 1000, so running the reports at the site collection level caused SharePoint to exceed this amount when the site contained more than 1000 page libraries.  Populating the CAML List Type with:

<Lists BaseType="1" MaxListsLimit="0"/>

Helper Class for Avoiding Memory Leaks with SPSite and SPWeb Objects

Microsoft provides a sophisticated API/Object Model for programmatic access to the SharePoint from .NET custom code. 

Invariably, most calls to the object model begin with a call to the SPSite and SPWeb objects, which represent the site collection and web accessed accordingly.  Developers must dispose the memory of any instance of SPSite and SPWeb.  This includes calls to site.AllWebs[], web.ParentWeb etc.  The only exception is when using site and web instances from the SPContext singleton, provided by the object model.

For example, the following is code that I have sometimes observed:

   1:  SPSite site = new SPSite("http://myMOSSsite/");
   2:  SPWeb web = site.OpenWeb();
   3:  SPList list = web.Lists["MyList"];

The above code leaks memory and resources, since the developer is allocating a site and web instance and never disposing of the objects.  A better approach is the following code:

   1:  using (SPSite site = new SPSite("http://myMOSSsite/"))
   2:  {
   3:      using (SPWeb web = site.OpenWeb())
   4:      {
   5:          SPList list = web.Lists["MyList"];
   6:      }
   7:  }

The second code example guarantees the clean up of SPSite and SPWeb objects when the execution leaves scope, the problem occurs with the use of the SPList object instance.  Occasionally I have seen the above code wrapped into a method, which then returns the SPList object.  Surprisingly, SharePoint allows you to use the list object instance, even though the parent web and site objects were disposed at the end of the method.  This situation should be avoided and SharePoint will leave plenty of error messages in the logs to warn you of this fact.

A best practice is to never return SPSite and SPWeb objects from methods, since it is not immediately clear whether the caller of your method is responsible for disposing web ands site objects (the method may be using SPContext or maybe allocating a new instance).  Developers should also avoid returning SPSite and SPWeb dependent objects unless the SPWeb instance is passed to the method or the method is categorically using SPContext.

Since we’re avoiding passing around references to these potentially leaky objects, how should developers write code?

The answer is to use delegates.  The following code is a handy helper class that encapsulates the use of SPSite and SPWeb objects, and executes custom code via delegates:

   1:      /// <summary>
   2:      /// Helper class to prevent memory leakage in SharePoint Object Model calls.
   3:      /// </summary>
   4:      public static class SPHelper
   5:      {
   6:          #region Methods
   8:          /// <summary>
   9:          /// Get a SPSite reference for a given <paramref name="url">site URL</paramref> and execute
  10:          /// a given <paramref name="action">Action</paramref>.
  11:          /// </summary>
  12:          /// <param name="url">Site Url.</param>
  13:          /// <param name="action">Action delegate.</param>
  14:          public static void GetSite(string url, Action<SPSite> action)
  15:          {
  16:              if (String.IsNullOrEmpty(url))
  17:                  throw new ArgumentNullException("url");
  18:              if (null == action)
  19:                  throw new ArgumentNullException("action");
  20:              using (var site = new SPSite(url))
  21:                  action(site);
  22:          }
  24:          /// <summary>
  25:          /// Get an SPSite and SPWeb reference for a given <paramref name="url">site URL</paramref> and execute
  26:          /// a given <paramref name="action">Action</paramref>.
  27:          /// </summary>
  28:          /// <param name="url">Web Url.</param>
  29:          /// <param name="action">Action delegate.</param>
  30:          public static void GetWeb(string url, Action<SPSite, SPWeb> action)
  31:          {
  32:              if (String.IsNullOrEmpty(url))
  33:                  throw new ArgumentNullException("url");
  34:              if (null == action)
  35:                  throw new ArgumentNullException("action");
  36:              using (var site = new SPSite(url))
  37:              {
  38:                  using (var web = site.OpenWeb())
  39:                  {
  40:                      if (null == web)
  41:                          throw new SPException("Failed to open web");
  42:                      action(site, web);
  43:                  }
  44:              }
  45:          }
  47:          /// <summary>
  48:          /// Get an SPSite and SPWeb (root) reference for a given <paramref name="url">site URL</paramref> and execute
  49:          /// a given <paramref name="action">Action</paramref>.
  50:          /// </summary>
  51:          /// <param name="url">Site Url.</param>
  52:          /// <param name="action">Action delegate.</param>
  53:          public static void GetRootWeb(string url, Action<SPSite, SPWeb> action)
  54:          {
  55:              if (String.IsNullOrEmpty(url))
  56:                  throw new ArgumentNullException("url");
  57:              if (null == action)
  58:                  throw new ArgumentNullException("action");
  59:              using (var site = new SPSite(url))
  60:              {
  61:                  using (var web = site.RootWeb)
  62:                  {
  63:                      action(site, web);
  64:                  }
  65:              }
  66:          }
  68:          /// <summary>
  69:          /// Get an SPSite and SPWeb reference for a given <paramref name="url">site URL</paramref> and
  70:          /// <paramref name="username"/> and execute
  71:          /// a given <paramref name="action">Action</paramref>.
  72:          /// </summary>
  73:          /// <remarks>Username must be a user in the site.</remarks>
  74:          /// <param name="url">Site Url.</param>
  75:          /// <param name="username">Username with access to the web.</param>
  76:          /// <param name="action">Action delegate.</param>
  77:          public static void GetSecureWeb(string url, string username, Action<SPSite, SPWeb, SPUser> action)
  78:          {
  79:              if (String.IsNullOrEmpty(url))
  80:                  throw new ArgumentNullException("url");
  81:              if (String.IsNullOrEmpty(username))
  82:                  throw new ArgumentNullException("username");
  83:              if (null == action)
  84:                  throw new ArgumentNullException("action");
  85:              // Open the regular web first.
  86:              using (var site = new SPSite(url))
  87:              {
  88:                  using (var web = site.RootWeb)
  89:                  {
  90:                      // See if user is registered with the root web.
  91:                      var user = web.AllUsers[username];
  92:                      if (null == user)
  93:                          throw new SPException("No user found");
  94:                      // Open the secure site.
  95:                      using (var secureSite = new SPSite(url, user.UserToken))
  96:                      {
  97:                          using (var secureWeb = secureSite.OpenWeb())
  98:                          {
  99:                              if (null == secureWeb)
 100:                                  throw new SPException("Failed to open secure web");
 101:                              action(secureSite, secureWeb, user);
 102:                          }
 103:                      }
 104:                  }
 105:              }
 106:          }
 108:          /// <summary>
 109:          /// Get an SPSite and SPWeb (root) reference for a given 
 110:          /// <paramref name="url">site Url</paramref> and
 111:          /// <paramref name="title">web title</paramref> and execute
 112:          /// a given <paramref name="action">Action</paramref>.
 113:          /// </summary>
 114:          /// <param name="url">Site Url.</param>
 115:          /// <param name="title">Web Title.</param>
 116:          /// <param name="action">Action delegate.</param>
 117:          public static void GetWebByTitle(string url, string title, Action<SPSite, SPWeb> action)
 118:          {
 119:              if (String.IsNullOrEmpty(url))
 120:                  throw new ArgumentNullException("url");
 121:              if (String.IsNullOrEmpty(title))
 122:                  throw new ArgumentNullException("title");
 123:              if (null == action)
 124:                  throw new ArgumentNullException("action");
 125:              using (var site = new SPSite(url))
 126:              {
 127:                  using (var web = site.AllWebs[title])
 128:                  {
 129:                      if (null == web)
 130:                          throw new SPException("Web not found");
 131:                      action(site, web);
 132:                  }
 133:              }
 134:          }
 136:          /// <summary>
 137:          /// Get an SPSite and SPWeb (parent) reference for a given 
 138:          /// <paramref name="url">site Url</paramref> and
 139:          /// a given <paramref name="action">Action</paramref>.
 140:          /// </summary>
 141:          /// <param name="url">Site Url.</param>
 142:          /// <param name="action">Action delegate.</param>
 143:          public static void GetParentWeb(string url, Action<SPSite, SPWeb> action)
 144:          {
 145:              if (String.IsNullOrEmpty(url))
 146:                  throw new ArgumentNullException("url");
 147:              if (null == action)
 148:                  throw new ArgumentNullException("action");
 149:              using (var site = new SPSite(url))
 150:              {
 151:                  using (var web = site.OpenWeb())
 152:                  {
 153:                      if (null == web)
 154:                          throw new SPException("Cannot open web");
 155:                      if (web.IsRootWeb)
 156:                          throw new SPException("Url has no parent web.");
 157:                      using (var parentWeb = web.ParentWeb)
 158:                          action(site, parentWeb);
 159:                  }
 160:              }
 161:          }
 163:          /// <summary>
 164:          /// Get a SPSite reference for a given <paramref name="url">site URL</paramref> and execute
 165:          /// a given <paramref name="action">Action</paramref>. Elevate to SharePoint service account
 166:          /// before opening.
 167:          /// </summary>
 168:          /// <param name="url">Site Url.</param>
 169:          /// <param name="action">Action delegate.</param>
 170:          public static void GetSiteElevated(string url, Action<SPSite> action)
 171:          {
 172:              SPSecurity.RunWithElevatedPrivileges(() => GetSite(url, action));
 173:          }
 175:          /// <summary>
 176:          /// Get an SPSite and SPWeb reference for a given <paramref name="url">site URL</paramref> and execute
 177:          /// a given <paramref name="action">Action</paramref>. Elevate to SharePoint service account
 178:          /// before opening.
 179:          /// </summary>
 180:          /// <param name="url">Web Url.</param>
 181:          /// <param name="action">Action delegate.</param>
 182:          public static void GetWebElevated(string url, Action<SPSite, SPWeb> action)
 183:          {
 184:              SPSecurity.RunWithElevatedPrivileges(() => GetWeb(url, action));
 185:          }
 187:          /// <summary>
 188:          /// Get an SPSite and SPWeb (root) reference for a given <paramref name="url">site URL</paramref> and execute
 189:          /// a given <paramref name="action">Action</paramref>. Elevate to SharePoint service account
 190:          /// before opening.
 191:          /// </summary>
 192:          /// <param name="url">Site Url.</param>
 193:          /// <param name="action">Action delegate.</param>
 194:          public static void GetRootWebElevated(string url, Action<SPSite, SPWeb> action)
 195:          {
 196:              SPSecurity.RunWithElevatedPrivileges(() => GetRootWeb(url, action));
 197:          }
 199:          /// <summary>
 200:          /// Get an SPSite and SPWeb (root) reference for a given 
 201:          /// <paramref name="url">site Url</paramref> and
 202:          /// <paramref name="title">web title</paramref> and execute
 203:          /// a given <paramref name="action">Action</paramref>. 
 204:          /// Elevate to SharePoint service account before opening.
 205:          /// </summary>
 206:          /// <param name="url">Site Url.</param>
 207:          /// <param name="title">Web Title.</param>
 208:          /// <param name="action">Action delegate.</param>
 209:          public static void GetWebByTitleElevated(string url, string title, Action<SPSite, SPWeb> action)
 210:          {
 211:              SPSecurity.RunWithElevatedPrivileges(() => GetWebByTitle(url, title, action));
 212:          }
 214:          /// <summary>
 215:          /// Get an SPSite and SPWeb (parent) reference for a given 
 216:          /// <paramref name="url">site Url</paramref> and
 217:          /// a given <paramref name="action">Action</paramref>.
 218:          /// Elevate to SharePoint service account before opening.
 219:          /// </summary>
 220:          /// <param name="url">Site Url.</param>
 221:          /// <param name="action">Action delegate.</param>
 222:          public static void GetParentWebElevated(string url, Action<SPSite, SPWeb> action)
 223:          {
 224:              SPSecurity.RunWithElevatedPrivileges(() => GetParentWeb(url, action));
 225:          }
 227:          /// <summary>
 228:          /// Get an SPSite and SPWeb reference for a given <paramref name="url">site URL</paramref> and
 229:          /// <paramref name="username"/> and execute
 230:          /// a given <paramref name="action">Action</paramref>.
 231:          /// Elevate to SharePoint service account before opening.
 232:          /// </summary>
 233:          /// <remarks>Username must be a user in the site.</remarks>
 234:          /// <param name="url">Site Url.</param>
 235:          /// <param name="username">Username with access to the web.</param>
 236:          /// <param name="action">Action delegate.</param>
 237:          public static void GetSecureWebElevated(string url, string username, Action<SPSite, SPWeb, SPUser> action)
 238:          {
 239:              SPSecurity.RunWithElevatedPrivileges(() => GetSecureWeb(url, username, action));
 240:          }
 242:          #endregion Methods
 243:      }

Using the above helper class, developers need never allocate web or site objects.  Furthermore, since each method call above cleans up memory after execution, developers never need to worry about too many web or site instances lying around waiting for garbage collection (also greeted with a nice error message in the log).

Users with Full Control Cannot Create Pages (MOSS 2007)

I had a client call me up with a MOSS 2007 permission problem.  The complaint was that users in a site owners group, with "full control" permission level, were unable to create a page anywhere in the WCM site collection.

Turns out that somewhere along the line the approvers group was deleted by mistake, which inadvertently removed read access to the Master Page Gallery.  Adding the approvers group back to the MPG with read access fixed the issue.

I cannot take credit because I found the answer here.

SharePoint InfoPath Submission as Anonymous User

Creation of user submitted forms within web sites typically involves some HTML and JavaScript magic; to post data to a back end business storage or processing system.  ASP.NET relies on form HTTP POST exclusively to implement the page post back mechanism, familiar to all ASP.NET developers.  Developers manipulate form posted data using server-side code, invoked after form data is submitted to the web server.  In ASP.NET this code usually exists as code behind or code inline VB.NET or C#, using the Framework to access posted data via a dedicated request object.  So what about SharePoint?

Microsoft's answer to form submission in SharePoint is InfoPath Services.  Microsoft Office InfoPath 2007 supports web browser enabled forms within Microsoft Office SharePoint 2007 ("MOSS") Enterprise using a dedicated InfoPath engine running in MOSS – InfoPath Server.  Unfortunately, if you're using Windows SharePoint Services, or any version of MOSS below Enterprise then InfoPath Services is unavailable to you, and your left with the alternative of hosting ASP.NET forms using custom developed web parts or user controls.

InfoPath Services work well – non-developers can create forms within InfoPath 2007 on their desktop and then publish these forms to a "forms library" in MOSS.  The problem comes about when hosting InfoPath forms in Publishing Web site collections with anonymous user access turned on.  Typically, site owners like to access form submitted data in a SharePoint list, for future processing, which is a problem when a user submitting form data is not authenticated.  Any good publishing site in MOSS utilizes the lock-down feature so anonymous users cannot access back-end lists, which includes write access.  With lock-down enabled, InfoPath Server is unable to submit anonymous user form data, and usually throws up an error.   Fortunately there exists a clean solution, using web service submission, detailed below in this blog post…

Getting Started

Before we get into the specifics of implementation be sure that your version of MOSS 2007 is Enterprise and that InfoPath Services is turned on.  Within SharePoint Central Administration, under the Applications Management tab, click Configure InfoPath Forms Services under the InfoPath Forms Services section.


Make sure that the top two check boxes are checked to allow hosting of forms.

Within the Site Settings of the top level site collection of the SharePoint site hosting InfoPath Services, click Site Collection Features. Make sure that "Office SharePoint Server Enterprise Site Collection features" is enabled.

The Forms Library

Next, create a "Form Library" within the host site collection – I suggest hosting at the top level site collection to make life easier.  For the purpose of this blog post I created a form library called "Test Forms."  You can use the default forms library installed by SharePoint, although it's best to create your own library to prevent overwriting existing published templates in the default library.


The Web Service

This is the tricky part of this operation.  Rather than submitting directly to SharePoint from InfoPath we're going to submit via a custom WCF Web Service, hosted in SharePoint.  Be sure to install .NET 3.0 (ideally .NET 3.5 also) on the SharePoint server. 

Check that the WCF extensions are installed into IIS – the SVC file extension should map as follows:


Create a standard class library, create the following interface definition:

   1:     [ServiceContract(Namespace = "http://contoso.net/Schemas/WCF/InfoPathService/")]
   2:      public interface IInfoPathService
   3:      {
   4:          /// <summary>
   5:          /// Upload InfoPath form data to a library in SharePoint.
   6:          /// </summary>
   7:          /// <param name="formData">Form Data.</param>
   8:          [OperationContract]
   9:          void PostInfoPathForm(string formData);
  10:      }

Create a class that implements the above interface and includes the following methods:

   1:          public void PostInfoPathForm(string formData)
   2:          {
   3:              // Use elevated privileges so anonymous users can upload through
   4:              // this service.
   5:              SPSecurity.RunWithElevatedPrivileges(delegate { UploadToDocLibrary(formData); });
   6:          }
   8:          private static void UploadToDocLibrary(string formData)
   9:          {
  10:              try
  11:              {
  12:                  // With the form data submitted to this web service, we now
  13:                  // need to find the location for submitting list data.
  14:                  XmlDocument xmlDoc = new XmlDocument();
  15:                  xmlDoc.LoadXml(formData);
  16:                  // Get the XSN location
  17:                  XmlProcessingInstruction pi = 
  18:                      (XmlProcessingInstruction)xmlDoc.SelectSingleNode("/processing-instruction("mso-infoPathSolution")");
  19:                  if (null != pi && !String.IsNullOrEmpty(pi.Value))
  20:                  {
  21:                      Match m = Regex.Match(pi.Value, "href="(.+?)"");
  22:                      if (m.Success && m.Groups.Count > 1)
  23:                      {
  24:                          string xsnLoc = m.Groups[1].Value;
  25:                          if (!xsnLoc.StartsWith("http", StringComparison.OrdinalIgnoreCase) || !xsnLoc.ToLower().Contains("/forms/"))
  26:                              throw new Exception("XSN location is not a published InfoPath document library.");
  27:                          // Open the site and web, try to get the list.
  28:                          using (SPSite site = new SPSite(xsnLoc))
  29:                          {
  30:                              using (SPWeb web = site.OpenWeb())
  31:                              {
  32:                                  web.AllowUnsafeUpdates = true;
  33:                                  string libLoc = xsnLoc.Substring(0, xsnLoc.LastIndexOf("/Forms/"));
  34:                                  // Upload form data to the library.
  35:                                  SPFolder folder = web.GetFolder(libLoc);
  36:                                  if (null == folder)
  37:                                      throw new Exception("Cannot find the InfoPath document library root folder.");
  38:                                  UTF32Encoding encoder = new UTF32Encoding();
  39:                                  byte[] data = encoder.GetBytes(formData);
  40:                                  folder.Files.Add(String.Format("{0}/{1}.xml", libLoc, Guid.NewGuid()), data);
  41:                                  web.AllowUnsafeUpdates = false;
  42:                              }
  43:                          }
  44:                      }
  45:                  }
  46:              }
  47:              catch (Exception ex)
  48:              {
  49:                  throw new Exception("Failed to upload form data", ex);
  50:              }
  51:          }

The above code runs as a privileged user, thus getting around the anonymous user posting problem, and submits the form data to the SharePoint on the users behalf.  The code above assumes that the InfoPath forms library exists at the same location as the XSN, which typically lives in the "Forms" folder of the library.

Compile the class library and add the assembly to the GAC.

Create an SVC file, named InfoPathService.svc, as follows, replace the qualified name for the assembly in the GAC, and the service namespace.

   1:  <%@ Assembly Name="Fully qualified assembly name"%> 
   2:  <%@ ServiceHost Service="My.Namespace.InfoPathService" %>

Create a web.config file as follows:

   1:  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   2:  <configuration>
   3:    <system.serviceModel>
   4:      <services>
   5:        <service behaviorConfiguration="InfoPathService.service1Behavior" name="My.Namespace.InfoPathService">
   6:          <endpoint binding="basicHttpBinding" contract="My.Namespace.IInfoPathService" bindingNamespace="http://contoso.net/Schemas/WCF/InfoPathService/" />
   7:        </service>
   8:      </services>
   9:      <behaviors>
  10:        <serviceBehaviors>
  11:          <behavior name="InfoPathService.service1Behavior">
  12:            <serviceMetadata httpGetEnabled="true" />
  13:            <serviceDebug includeExceptionDetailInFaults="false" />
  14:          </behavior>
  15:        </serviceBehaviors>
  16:      </behaviors>
  17:    </system.serviceModel>
  18:  </configuration>

Copy the InfoPathService.svc and web.config file to subdirectory: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12ISAPIMyWebServices

Confirm that the service spins up using the address of your server, e.g:



With the web service installed and functioning inside SharePoint, it's now time to create a form in InfoPath 2007, and publishing it to the SharePoint forms library.  Remember I created a form library at /Test Forms earlier.

Open InfoPath 2007 on your desktop and design a new form (I'm using a modified expense report).


Click the Design Checker and then change the Compatibility Settings.  Input the URL of your SharePoint site collection (not the form library).


Under Tools, Submit Options, change the submit options to use the web service we created, as follows:


When creating the data connection, be sure to submit the entire form data to the web service as a string:


Publishing the Form

Publish the form using the Publish command under the File menu.  Publish to SharePoint server:


When prompted for the location use the URL of site that contains the for library you defined.

Publish to a document library and enable browser form completion as follows:


Select the form library we created earlier:


Finally, map the fields to columns in the SharePoint list:


Testing the Form

Before attempting to create a new form in the form document library (using the New command from the menu), make sure that the library is configured to open forms in the browser (form library settings, advanced settings).

Presto! The form should render in the browser:


Try filling out the form and submitting.  The submitted data should flow via the web service and into SharePoint.

SharePoint Training

Need some SharePoint (MOSS 2007) training?  Want to take advantage of MOSS 2007 development under Visual Studio 2008 and SQL Server 2008?  Never been to Norway?

Then check out Sahil Malik’s training program this September 1, 2008.  Details (here).

If Norway is a little too far to travel, I can also personally vouch for Ted Patterson training, of which Sahil is also providing a training session September 22, 2008.  Details (also here).


This is one of those blog posts that I cannot take credit, but want to keep the information handy for a later time.

The SPSecurityTrimmedControl shipped with WSSv3 is a very powerful control. It basically allows you to conditionally display content depending on the user’s permission.

Thanks to Waldek for his post on conditional security trimming of page layout content in SharePoint.