SharePoint Internals – Hristo Pavlov’s Blog

23 October, 2008

The trial period for this product has expired

Filed under: SharePoint — Tags: , , — hristopavlov @ 7:00 am

This is an error that we discovered was happening in one of the legitimate licensed MOSS boxes today. We were getting it when trying to check in or publish a page. The exact error message was was:

The trial period for this product has expired.   at Microsoft.SharePoint.Publishing.Internal.CodeBehind.CreatePagePage.HandleUnexpectedException(PublishingPage newPage, Exception exception)
   at Microsoft.SharePoint.Publishing.Internal.CodeBehind.CreatePagePage.NewPageItemSave(String pageName, PageLayout pageLayout)
   at Microsoft.SharePoint.Publishing.Internal.CodeBehind.CreatePagePage.CreateStandardPage(String pageName)
   at Microsoft.SharePoint.Publishing.Internal.CodeBehind.CreatePagePage.ButtonCreatePage_Click(Object sender, EventArgs e)
   at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
   at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
   at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Doing a search didn’t reveal much but there were other people experiencing the same problem. Some of them saying that they were getting an error when trying  to create a publishing site from the SharePoint GUI but no error when doing it via STSADM. Our further tests showed that there was an OfficeServerSettings.dll involved: 

[DllNotFoundException: Unable to load DLL ‘OfficeServerSettings.dll’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
   Microsoft.Office.Server.Administration.NativeMethods.GetExpirationTime(Int64& expirationTime) +0
   Microsoft.Office.Server.Administration.Licensing.GetExpirationFileTimeFromRegistry() +156

[LicensingException: An error was encountered getting expiration info.]
   Microsoft.Office.Server.Administration.Licensing.GetExpirationFileTimeFromRegistry() +580

We found people suggesting various things, including uninstalling AV updates and changing the registry permissions:

http://joelsef.blogspot.com/2007/12/trial-period-for-this-product-has.html

http://blogs.mgtechgroup.com/markc/archive/2007/08/09/MOSS-quottrial-period-for-this-product-has-expiredquot-error.aspx

http://mindsharpblogs.com/ben/archive/2006/09/23/1299.aspx

http://blogs.msdn.com/joelo/archive/2008/01/02/evaluation-version-expired-but-not-really.aspx

What worked for us was adding permissions to the HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\Office Server\12.0 key to the WSS_WPG and WSS_ADMIN_WPG groups.

20 October, 2008

A Very Fast Method to Get the Site Collection’s Web Structure

Filed under: SharePoint — Tags: , , , , , , , , — hristopavlov @ 5:09 am

This is how you can get the full structure a SharePoint site collection without the need of creating memory consuming and slow SPWeb objects.

Before all let me just mention that actually the fastest possible way would be if you connect directly to the Content Database and run a query in the Webs table for the given site collection Id.

Other than this all ways to get the structure using the SharePoint object model will have to rely on the unmanaged COM objects implemented in OWSSVR.DLL to get the info. The question is how we can do this without actually creating an SPWeb object. The standard way to get the structure would be to create a new SPSite and then to loop through all subwebs to get the job done but this takes a looong time and wastes memory.

The code below uses an SPSite object to initialize the information about all the webs in the site, with an unmanaged call to GetAllWebsOfSite() of OWSSVR, and then reads the returned info from the private fields of the SPSite.AllWebs member without creating SPWeb objects. You will (probably) need to have access to all those sites for this method to work. Here is the code:

public class SPWebEntry

{

    private string m_Url;

    private string m_Title;

    private Guid m_Id;

 

    internal List<SPWebEntry> SubWebsList = new List<SPWebEntry>();

    internal SPWebEntry ParentWebEntry = null;

 

    internal SPWebEntry(string url, string title, Guid id)

    {

        m_Url = url;

        m_Title = title;

        m_Id = id;

    }

 

    public string Url

    {

        get { return m_Url; }

    }

 

    public string Title

    {

        get { return m_Title; }

    }

 

    public Guid Id

    {

        get { return m_Id; }

    }

 

    public IEnumerable<SPWebEntry> SubWebs

    {

        get { return SubWebsList; }

    }

 

    public SPWebEntry ParentWeb

    {

        get { return ParentWebEntry; }

    }

}

 

public class SPQuickSiteStructure

{

    private SPWebEntry m_RootWeb;

 

    public SPWebEntry RootWeb

    {

        get { return m_RootWeb; }

    }

 

    public SPQuickSiteStructure(string siteCollectionUrl)

    {

        using (SPSite site = new SPSite(siteCollectionUrl))

        {

            // This will make an unmanaged call to get all webs including their Guid and Titles but will not create any SPWeb objects

            string[] allSubWebUrls = site.AllWebs.Names;

 

            FieldInfo fi = typeof(SPWebCollection).GetField(“m_strTitles”, BindingFlags.Instance | BindingFlags.NonPublic);

            string[] allSubWebTitles = (string[])fi.GetValue(site.AllWebs);

 

            fi = typeof(SPWebCollection).GetField(“m_guidWebIds”, BindingFlags.Instance | BindingFlags.NonPublic);

            Guid[] allSubWebGuids = (Guid[])fi.GetValue(site.AllWebs);

 

            m_RootWeb = new SPWebEntry(

                    allSubWebUrls[0],

                    allSubWebTitles[0],

                    allSubWebGuids[0]);

 

            SPWebEntry currWeb = RootWeb;

 

            for (int i = 1; i < allSubWebUrls.Length; i++)

            {

                SPWebEntry web = new SPWebEntry(

                    allSubWebUrls[i],

                    allSubWebTitles[i],

                    allSubWebGuids[i]);

 

                while (

                    currWeb != null &&

                    (web.Url.Length < currWeb.Url.Length ||

                     web.Url.Substring(0, currWeb.Url.Length) != currWeb.Url))

                {

                    currWeb = currWeb.ParentWeb;

                }

 

                web.ParentWebEntry = currWeb;

                if (currWeb != null) currWeb.SubWebsList.Add(web);

                currWeb = web;

            }

        }

    }

}

To use it, simply create a new SPQuickSiteStructure object passing the site collection url and then do a standard loop through the SubWebs property starting from the SPQuickSiteStructure.RootWeb.

One thing to watch out for is the security. If you are going to show those sites to a user you’ll need to check whether the user is allowed to see the sites.

Thanks to Ishai for pointing out that the web urls are actually exposed by the public property Names, something that I missed initially. So the SPSite.AllWebs.Names gives you the site relative urls of all the webs but if you also want the web Titles and the web Guids you still need to go with the above code. Also the Names are returned as a flat list rather than a tree structure and you’ll need to build the structure yourself if you are not using the SPQuickSiteStructure class.

10 October, 2008

SPSite.LastSecurityModifiedDate

Filed under: SharePoint — Tags: , , , — hristopavlov @ 6:55 am

This is a great property in the SharePoint object model that I discovered today, that indicates the time of the last security related change done to an object in the site collection. This could be a change to any securable object (web, list or list item) or modification to the permission level rights etc. It will work regardless of whether the subwebs and lists use unique permissions or inherited permissions. Adding new objects that don’t inherit permissions also means adding permissions and will result in an update of the flag. The same applies for braking/restoring permission inheritance.

You can use the SPSite.LastSecurityModifiedDate and the SPSite.LastContentModifiedDate properties as indicators of when something has changed for example to invalidate any custom built caching mechanism as we did in out current project and this lead to exceptional performance improvement.

The values of those fields are coming directly from the content database from the Sites.LastSecurityChange and Sites.LastContentChange fields.

Blog at WordPress.com.