Copy the versioning data to a custom column on a document library

This is a rather interesting one, regarding SharePoint 2007 document libraries, Office Word 2007 and Quick Parts.

It all started when I noticed that, although you get various custom columns in the Word 2007 Document Information Panel (or simply "DIP"), you do not see the version of the current document. Consequently, if you try to add a QuickPart from Word 2007 containing the contents of the "Version" field, you will notice that the QuickPart is not available. So how can you add this information to the document in a nice way?

The solution I found (thanks to https://www.sharepointgurus.net/ ) was to add a series of event handlers to my document library. This will in turn copy the information found in the internal "Version" field to a new, exposed, "DocumentVersion" field, which you can add as a QuickPart from within Word 2007.
The first code part consists of the actual event handler. It needs to be compiled to a DLL, signed with a strong name, and added to the SharePoint server's GAC (for instance by dragging it from the project's \bin\Debug\ folder to the C:\Windows\Assembly\ folder, or by using the GACUTIL.EXE command-line tool shipped with Visual Studio). Here is the code:

 namespace CUEvHandler {
  public class TheEventHandler : SPItemEventReceiver {

    private const string FIELD_DOC_VERS = "DocumentVersion";
    private const string FIELD_ITEM_VERS = "Version";

    private void SetDocumentVersion(SPListItem item) {
      item[FIELD_DOC_VERS] = item[FIELD_ITEM_VERS];
      item.SystemUpdate(false);
    }

    private void DeleteDocumentVersionField(SPList list) {
      SPSecurity.RunWithElevatedPrivileges(delegate() {
        list.Fields[FIELD_DOC_VERS].ReadOnlyField = false;
        list.Fields[FIELD_DOC_VERS].Update();
        list.Fields[FIELD_DOC_VERS].Delete();
        if (list.Fields.ContainsField(FIELD_DOC_VERS))
          throw new ApplicationException("Failed to delete field '" + FIELD_DOC_VERS + "'.");
      }); }

    private void CreateDocumentVersionField(SPList list) {
      SPSecurity.RunWithElevatedPrivileges(delegate() {
        list.Fields.Add(FIELD_DOC_VERS, SPFieldType.Text, false);
        SPField fld = list.Fields[FIELD_DOC_VERS];
        fld.ReadOnlyField = true;
        fld.Update(); list.Update();
        if (!list.Fields.ContainsField(FIELD_DOC_VERS))
          throw new ApplicationException("Failed to create field '" + FIELD_DOC_VERS + "'.");
        foreach (SPListItem liExisting in list.Items)
          SetDocumentVersion(liExisting);
      }); }

    private void SetDocumentMetaData(SPItemEventProperties properties) {
      try {
        this.DisableEventFiring();
        SPListItem li = properties.ListItem;
        SPList list = properties.ListItem.ParentList;
        if (!list.Fields.ContainsField(FIELD_DOC_VERS))
          CreateDocumentVersionField(list);
        else
          SetDocumentVersion(li);
      }
      catch (Exception ex) {
        properties.ErrorMessage = ex.Message;
      }
      finally {
        this.EnableEventFiring();
      } }

    public override void ItemUncheckedOut(SPItemEventProperties properties) {
      SetDocumentMetaData(properties); }

    public override void ItemCheckedOut(SPItemEventProperties properties) {
      SetDocumentMetaData(properties); }

    public override void ItemCheckedIn(SPItemEventProperties properties) {
      SetDocumentMetaData(properties); }

    public override void ItemUpdated(SPItemEventProperties properties) {
      SetDocumentMetaData(properties); }

    public override void ItemAdded(SPItemEventProperties properties) {
      SetDocumentMetaData(properties); }
} }

Now there's a second part: actually adding the event receivers on the library, which should then call the code inside our DLL. I've written this as a standard Console Application, as follows:

 using System;
using Microsoft.SharePoint;

namespace CUEvHandlerLauncher {
    class ProgramLauncher {
        static void Main(string[] args) {
            SPSite mySite = new SPSite("https://srv2003-wss");
            SPWeb myWeb = mySite.OpenWeb();
            SPList myList = myWeb.Lists["DL1"];

            string assembly = "CUEvHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=547B58B0378BC40D";
            myList.EventReceivers.Add(SPEventReceiverType.ItemUncheckedOut, assembly, "CUEvHandler.TheEventHandler");
            myList.EventReceivers.Add(SPEventReceiverType.ItemCheckedOut, assembly, "CUEvHandler.TheEventHandler");
            myList.EventReceivers.Add(SPEventReceiverType.ItemCheckedIn, assembly, "CUEvHandler.TheEventHandler");
            myList.EventReceivers.Add(SPEventReceiverType.ItemUpdated, assembly, "CUEvHandler.TheEventHandler");
            myList.EventReceivers.Add(SPEventReceiverType.ItemAdded, assembly, "CUEvHandler.TheEventHandler");

            myList.Update(); myWeb.Dispose(); mySite.Dispose();
        } } }

The following links may be of additional interest:
Feature Events (https://msdn.microsoft.com/en-us/library/ms469501.aspx)
Creating a Solution (https://msdn.microsoft.com/en-us/library/aa543741.aspx)
How to: Create an Event Handler Feature (https://msdn.microsoft.com/en-us/library/ms453149.aspx)
How to: Create a Custom List Definition (https://msdn.microsoft.com/en-us/library/ms466023.aspx)