Monday, January 23, 2012

Avoid “Cannot change Hidden attribute for this field” exception when remove field from Sharepoint list

When you remove field from the Sharepoint list either from UI or programmatically you may encounter with the following exception (I faced with this problem when removed managed metadata field, but it can be also any type of field):

Cannot change Hidden attribute for this field 
   Microsoft.SharePoint.SPField.set_Hidden(Boolean value)
   Microsoft.SharePoint.Taxonomy.TaxonomyField.OnDeleting()
   Microsoft.SharePoint.SPFieldCollection.Delete(String strName)
   Microsoft.SharePoint.ApplicationPages.FieldEditPage.BtnDelete_Click(Object sender, EventArgs e)
   System.Web.UI.WebControls.Button.OnClick(EventArgs e)
   System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
   System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

This problem is caused by CanToggleHidden property of the field which was set to false during field provisioning. It has no setter so once it was provisioned you can’t change it using regular object model. The good news is that it is possible to change it using reflection:

   1: private static void setCanToggleHidden(SPField field)
   2: {
   3:     Type type = field.GetType();
   4:     MethodInfo mi = type.GetMethod("SetFieldBoolValue", BindingFlags.NonPublic | BindingFlags.Instance);
   5:     mi.Invoke(field, new object[] {"CanToggleHidden", true});
   6: }

After you will call this method, exception won’t be thrown and it will be possible to remove the field from list.

Friday, January 20, 2012

BadImageFormatException when call virtual methods from SPSecurity.RunWithElevatedPrivileges

Recently I faced with very strange problem. Consider the following console application which I made in order to reproduce the problem:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public virtual IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public override IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16:             SPSecurity.RunWithElevatedPrivileges(
  17:                 () =>
  18:                     {
  19:                         items = base.GetAll();
  20:                     });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             var items = foo.GetAll();
  32:         }
  33:     }
  34: }

If run this code BadImageFormatException will be thrown: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B).

At first I thought that I just build x86 application and double checked that Platform target is set to x64. Then goes strange thing: if you will move SPSecurity.RunWithElevatedPrivileges() outside the method Foo.GetAll() and enclose outer method call, exception will disappear and code will work as expected:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public virtual IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public override IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16: //            SPSecurity.RunWithElevatedPrivileges(
  17: //                () =>
  18: //                    {
  19:                         items = base.GetAll();
  20: //                    });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             SPSecurity.RunWithElevatedPrivileges(
  32:                 () =>
  33:                     {
  34:                         var items = foo.GetAll();
  35:                     });
  36:         }
  37:     }
  38: }

This code runs successfully. I checked stack trace of the exception from 1st example:

at ConsoleApplication.Foo.<>c__DisplayClass1.<GetAll>b__0()
at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2()
at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode)
at ConsoleApplication.Foo.GetAll() in C:\…\ConsoleApplication\ConsoleApplication\Program.cs:line 22
at ConsoleApplication.Program.Main(String[] args) in C:\…\ConsoleApplication\ConsoleApplication\Program.cs:line 40
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

As you can see problem comes from method of generated class <>c__DisplayClass1.<GetAll>b__0(). When I tried to open this method in Reflector, Reflector failed:

image

So compiler generated wrong code for us in first example? Problem seems to be related with vtable, because when I removed “virtual” keyword from base method GetAll(), code became working:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public new IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16:             SPSecurity.RunWithElevatedPrivileges(
  17:                 () =>
  18:                     {
  19:                         items = base.GetAll();
  20:                     });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             var items = foo.GetAll();
  32:         }
  33:     }
  34: }

This problem is strange, looks like a bug. But I’m not currently sure is it caused by Sharepoint code, by compiler or by something else – will update this post once will have additional info.

Saturday, January 14, 2012

One problem with cross-projects project items references in Sharepoint 2010 VS template

Often when working on Sharepoint projects there are several projects of Sharepoint 2010 VS project template in single solution. E.g. if you develop Internet and Extranet you may have 3 wsp packages (and 3 projects in VS):

  • Common – contains common functionality for Internet and Extranet
  • Internet – contains only Internet-specific functionality
  • Extranet – contains only Extranet-specific functionality

Doing like that if customer have different environments for Internet and Extranet, it will be possible to deploy Common and Internet packages on Internet environment, and Common and Extranet – on Extranet environment.

Visual studio allows you to include Sharepoint project items from different Sharepoint projects. E.g. if you have MOPages module in Common project, you can add this module into the feature from Internet or Extranet projects. It will work, however there is one side effect: in this case assembly which is build from Common project (Common.dll) will be included to the Internet.wsp and Extranet.wsp packages as well. In our example it most probably won’t cause problems, because we anyway need deploy Common package at the same time when Internet or Extranet packages will be deployed. In this case Common.dll will be installed to GAC first time from Common.wsp, and then from Internet.wsp or Extranet.wsp (depends in what order you deploy packages).

But if it may cause problems if you have some base library package in your company (e.g. Sharepoint.Base.wsp) which you use in many projects and control releases of this package (because you don’t want that changes made in newest version will break something on the existing environments of different customers). If you will include project items (PI) from Sharepoint.Base.csproj into your project (you may do it if will include Sharepoint.Base.csproj into your solution), then Internet.wsp (or Extranet.wsp) will also contain Sharepoint.Base.dll, regardless of the fact do you use C# code from it or not. I.e. as I mentioned above you may add module project item from Sharepoint.Base.csproj which doesn’t technically require adding reference to Sharepoint.Base in VS (solution will be compiled without it anyway), but Sharepoint.Base.dll will be anyway added to your wsp.

What will happen when you will deploy Internet.wsp (which contains now Internet.dll, Common.dll and Sharepoint.Base.dll)? All 3 dlls will be updated in GAC, including Sharepoint.Base.dll. It breaks policy with controlled releases – because now it will be hard to control what version of the Sharepoint.Base.dll is installed on production (because it will be updated when you deploy Internet.wsp and when you deploy Sharepoint.Base.wsp). If you will deploy latest version of the Sharepoint.Base.wsp to the production while other sites depend on old version, they may stop working. It may cause to version inconsistency and will lead to maintenance hell.

In order to avoid this problem do not make cross-projects references of project items when use Sharepoint 2010 VS project templates. Add functionality from base project by including on feature level (e.g. by adding features from base package into your onet.xml files) or use it as is. If these features contain a lot of another functionality which you don’t need in your projects, it is better to just copy necessary artifacts from it. In this case some functionality will be duplicated, but maintenance will be still controlled.

Recently Koen Vosters from MS wrote about similar problem in his blog: Unexpected DLL in a WSP package. (Hi Koen, and thanks one more time for the great help which you made for us in Finland! :) ). I hope that my post will be more popular in search engines :)) Just kidding ;)

Sunday, January 8, 2012

Camlex.NET now supports donations

Our open source project Camlex.NET and free online service for Sharepoint developers http://camlex-online.org now have Donate button. If you find it useful and would like to support development of the Camlex, you can now make donation with any reasonable amount of money via your PayPal account or by credit card.

Camlex was, is and will be free. But as we work over it in the free time and pay for the domain name of online version by ourselves, we will appreciate any donation which you will decide to make. It will help to improve Camlex in the future and make it more useful for Sharepoint developers.