About the Timestamper class extension Sample
[C#]
TimestampClassExtension.cs
using System; using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using Timestamper.Properties; namespace Timestamper { /// <summary> /// A feature class extension for timestamping features with creation dates, modification dates, and /// the name of the user who created or last modified the feature. /// </summary> [Guid("31b0b791-3606-4c58-b4d9-940c157dca4c")] [ClassInterface(ClassInterfaceType.None)] [ProgId("Timestamper.TimestampClassExtension")] [ComVisible(true)] public class TimestampClassExtension : IClassExtension, IObjectClassExtension, IFeatureClassExtension, IObjectClassEvents, IObjectClassInfo { #region Member Variables /// <summary> /// Provides a reference to the extension's class. /// </summary> private IClassHelper classHelper = null; /// <summary> /// The extension properties. /// </summary> private IPropertySet extensionProperties = null; /// <summary> /// The name of the "created" date field. /// </summary> private String createdFieldName = Resources.DefaultCreatedField; /// <summary> /// The position of the "created" date field. /// </summary> private int createdFieldIndex = -1; /// <summary> /// The name of the "modified" date field. /// </summary> private String modifiedFieldName = Resources.DefaultModifiedField; /// <summary> /// The position of the "modified" date field. /// </summary> private int modifiedFieldIndex = -1; /// <summary> /// The name of the "user" text field. /// </summary> private String userFieldName = Resources.DefaultUserField; /// <summary> /// The position of the "user" text field. /// </summary> private int userFieldIndex = -1; /// <summary> /// The length of the "user" text field. /// </summary> private int userFieldLength = 0; /// <summary> /// The name of the current user. /// </summary> private String userName = String.Empty; #endregion #region IClassExtension Methods /// <summary> /// Initializes the extension. /// </summary> /// <param name="classHelper">Provides a reference to the extension's class.</param> /// <param name="extensionProperties">A set of properties unique to the extension.</param> public void Init(IClassHelper classHelper, IPropertySet extensionProperties) { // Store the class helper as a member variable. this.classHelper = classHelper; IClass baseClass = classHelper.Class; // Get the names of the created and modified fields, if they exist. if (extensionProperties != null) { this.extensionProperties = extensionProperties; object createdObject = extensionProperties.GetProperty(Resources.CreatedFieldKey); object modifiedObject = extensionProperties.GetProperty(Resources.ModifiedFieldKey); object userObject = extensionProperties.GetProperty(Resources.UserFieldKey); // Make sure the properties exist and are strings. if (createdObject != null && createdObject is String) { createdFieldName = Convert.ToString(createdObject); } if (modifiedObject != null && modifiedObject is String) { modifiedFieldName = Convert.ToString(modifiedObject); } if (userObject != null && userObject is String) { userFieldName = Convert.ToString(userObject); } } else { // First time the extension has been run. Initialize with default values. InitNewExtension(); } // Set the positions of the fields. SetFieldIndexes(); // Set the current user name. userName = GetCurrentUser(); } /// <summary> /// Informs the extension that the class is being disposed of. /// </summary> public void Shutdown() { classHelper = null; } #endregion #region IObjectClassEvents Methods /// <summary> /// Fired when an object's attributes or geometry is updated. /// </summary> /// <param name="obj">The updated object.</param> public void OnChange(IObject obj) { // Set the modified field's value to the current date and time. if (modifiedFieldIndex != -1) { obj.set_Value(modifiedFieldIndex, DateTime.Now); // Set the user field's value to the current user. if (userFieldIndex != -1) { obj.set_Value(userFieldIndex, userName); } } } /// <summary> /// Fired when a new object is created. /// </summary> /// <param name="obj">The new object.</param> public void OnCreate(IObject obj) { // Set the created field's value to the current date and time. if (createdFieldIndex != -1) { obj.set_Value(createdFieldIndex, DateTime.Now); } // Set the user field's value to the current user. if (userFieldIndex != -1) { obj.set_Value(userFieldIndex, userName); } } /// <summary> /// Fired when an object is deleted. /// </summary> /// <param name="obj">The deleted object.</param> public void OnDelete(IObject obj) {} #endregion #region IObjectClassInfo Methods /// <summary> /// Indicates if updates to objects can bypass the Store method and OnChange notifications for efficiency. /// </summary> /// <returns>False; this extension requires Store to be called.</returns> public Boolean CanBypassStoreMethod() { return false; } #endregion #region Public Members /// <summary> /// Changes the member variables and extension properties to store the provided field names /// as the created, modified and user fields (positions are also refreshed). Empty strings /// indicate the values should not be saved in a field. /// </summary> /// <param name="createdField">The name of the "created" field.</param> /// <param name="modifiedField">The name of the "modified" field.</param> /// <param name="userField">The name of the "user" field.</param> public void SetTimestampFields(String createdField, String modifiedField, String userField) { IClass baseClass = classHelper.Class; ISchemaLock schemaLock = (ISchemaLock)baseClass; try { // Get an exclusive lock. We want to do this prior to making any changes // to ensure the member variables and extension properties remain synchronized. schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock); // Set the name member variables. createdFieldName = createdField; modifiedFieldName = modifiedField; userFieldName = userField; // Set the positions of the fields. SetFieldIndexes(); // Modify the extension properties. extensionProperties.SetProperty(Resources.CreatedFieldKey, createdFieldName); extensionProperties.SetProperty(Resources.ModifiedFieldKey, modifiedFieldName); extensionProperties.SetProperty(Resources.UserFieldKey, userFieldName); // Change the properties. IClassSchemaEdit2 classSchemaEdit = (IClassSchemaEdit2)baseClass; classSchemaEdit.AlterClassExtensionProperties(extensionProperties); } catch (COMException comExc) { throw new Exception(Resources.FailedToSavePropertiesMsg, comExc); } finally { schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock); } } /// <summary> /// The field storing the creation date of features. /// </summary> public String CreatedField { get { return createdFieldName; } } /// <summary> /// The field storing the modification date of features. /// </summary> public String ModifiedField { get { return modifiedFieldName; } } /// <summary> /// The field storing the user who created or last modified the feature. /// </summary> public String UserField { get { return userFieldName; } } #endregion #region Private Methods /// <summary> /// This method should be called the first time the extension is initialized, when the /// extension properties are null. This will create a new set of properties with the default /// field names. /// </summary> private void InitNewExtension() { // First time the extension has been run, initialize the extension properties. extensionProperties = new PropertySetClass(); extensionProperties.SetProperty(Resources.CreatedFieldKey, createdFieldName); extensionProperties.SetProperty(Resources.ModifiedFieldKey, modifiedFieldName); extensionProperties.SetProperty(Resources.UserFieldKey, userFieldName); // Store the properties. IClass baseClass = classHelper.Class; IClassSchemaEdit2 classSchemaEdit = (IClassSchemaEdit2)baseClass; classSchemaEdit.AlterClassExtensionProperties(extensionProperties); } /// <summary> /// Gets the name of the extension's user. For local geodatabases, this is the username as known /// by the operating system (in a domain\username format). For remote geodatabases, the /// IDatabaseConnectionInfo interface is utilized. /// </summary> /// <returns>The name of the current user.</returns> private String GetCurrentUser() { // Get the base class' workspace. IClass baseClass = classHelper.Class; IDataset dataset = (IDataset)baseClass; IWorkspace workspace = dataset.Workspace; // If supported, use the IDatabaseConnectionInfo interface to get the username. IDatabaseConnectionInfo databaseConnectionInfo = workspace as IDatabaseConnectionInfo; if (databaseConnectionInfo != null) { String connectedUser = databaseConnectionInfo.ConnectedUser; // If the user name is longer than the user field allows, shorten it. if (connectedUser.Length > userFieldLength) { connectedUser = connectedUser.Substring(0, userFieldLength); } return connectedUser; } // Get the current Windows user. String userDomain = Environment.UserDomainName; String userName = Environment.UserName; String qualifiedUserName = String.Format(@"{0}\{1}", userDomain, userName); // If the user name is longer than the user field allows, shorten it. if (qualifiedUserName.Length > userFieldLength) { qualifiedUserName = qualifiedUserName.Substring(0, userFieldLength); } return qualifiedUserName; } /// <summary> /// Finds the positions of the created, modified and user fields, and verifies that /// the specified field has the correct data type. /// </summary> private void SetFieldIndexes() { // Get the base class from the class helper. IClass baseClass = classHelper.Class; // Find the indexes of the fields. createdFieldIndex = baseClass.FindField(createdFieldName); modifiedFieldIndex = baseClass.FindField(modifiedFieldName); userFieldIndex = baseClass.FindField(userFieldName); // Verify that the field data types are correct. IFields fields = baseClass.Fields; if (createdFieldIndex != -1) { IField createdField = fields.get_Field(createdFieldIndex); // If the "created" field is not a date field, do not use it. if (createdField.Type != esriFieldType.esriFieldTypeDate) { createdFieldIndex = -1; } } if (modifiedFieldIndex != -1) { IField modifiedField = fields.get_Field(modifiedFieldIndex); // If the "modified" field is not a date field, do not use it. if (modifiedField.Type != esriFieldType.esriFieldTypeDate) { modifiedFieldIndex = -1; } } if (userFieldIndex != -1) { IField userField = fields.get_Field(userFieldIndex); // If the "user" field is not a text field, do not use it. if (userField.Type != esriFieldType.esriFieldTypeString) { userFieldIndex = -1; } else { // Get the length of the text field. userFieldLength = userField.Length; } } } #endregion #region COM Registration Function(s) /// <summary> /// Registers the class extension in the appropriate component category. /// </summary> /// <param name="registerType">The class description's type.</param> [ComRegisterFunction()] [ComVisible(false)] static void RegisterFunction(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); GeoObjectClassExtensions.Register(regKey); } /// <summary> /// Removes the class extension from the appropriate component category. /// </summary> /// <param name="registerType">The class description's type.</param> [ComUnregisterFunction()] [ComVisible(false)] static void UnregisterFunction(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); GeoObjectClassExtensions.Unregister(regKey); } #endregion } }
[Visual Basic .NET]
TimestampClassExtension.vb
Imports System Imports System.Runtime.InteropServices Imports ESRI.ArcGIS.ADF.CATIDs Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geodatabase Imports Timestamper.My.Resources ''' <summary> ''' A feature class extension for timestamping features with creation dates, modification dates, and ''' the name of the user who created or last modified the feature. ''' </summary> <Guid("becd0269-32f2-4a21-9145-619a891e7862")> _ <ClassInterface(ClassInterfaceType.None)> _ <ProgId("Timestamper.TimestampClassExtension")> _ <ComVisible(True)> _ Public Class TimestampClassExtension Implements IClassExtension Implements IFeatureClassExtension Implements IObjectClassEvents Implements IObjectClassExtension Implements IObjectClassInfo #Region "Member Variables" ''' <summary> ''' Provides a reference to the extension's class. ''' </summary> Private classHelper As IClassHelper = Nothing ''' <summary> ''' The extension properties. ''' </summary> Private extensionProperties As IPropertySet = Nothing ''' <summary> ''' The name of the "created" date field. ''' </summary> Private createdFieldName As String = Resources.DefaultCreatedField ''' <summary> ''' The position of the "created" date field. ''' </summary> Private createdFieldIndex As Integer = -1 ''' <summary> ''' The name of the "modified" date field. ''' </summary> Private modifiedFieldName As String = Resources.DefaultModifiedField ''' <summary> ''' The position of the "modified" date field. ''' </summary> Private modifiedFieldIndex As Integer = -1 ''' <summary> ''' The name of the "user" text field. ''' </summary> Private userFieldName As String = Resources.DefaultUserField ''' <summary> ''' The position of the "user" text field. ''' </summary> Private userFieldIndex As Integer = -1 ''' <summary> ''' The length of the "user" text field. ''' </summary> Private userFieldLength As Integer = 0 ''' <summary> ''' The name of the current user. ''' </summary> Private userName As String = "" #End Region #Region "IClassExtension Methods" ''' <summary> ''' Initializes the extension. ''' </summary> ''' <param name="classHelper">Provides a reference to the extension's class.</param> ''' <param name="extensionProperties">A set of properties unique to the extension.</param> Public Sub Init(ByVal classHelper As IClassHelper, ByVal extensionProperties As IPropertySet) Implements IClassExtension.Init ' Store the class helper as a member variable. Me.classHelper = classHelper Dim baseClass As IClass = classHelper.Class ' Get the names of the created and modified fields, if they exist. If Not extensionProperties Is Nothing Then Me.extensionProperties = extensionProperties Dim createdObject As Object = extensionProperties.GetProperty(Resources.CreatedFieldKey) Dim modifiedObject As Object = extensionProperties.GetProperty(Resources.ModifiedFieldKey) Dim userObject As Object = extensionProperties.GetProperty(Resources.UserFieldKey) ' Make sure the properties exist and are strings. If Not createdObject Is Nothing And TypeOf createdObject Is String Then createdFieldName = TryCast(createdObject, String) End If If Not modifiedObject Is Nothing And TypeOf modifiedObject Is String Then modifiedFieldName = TryCast(modifiedObject, String) End If If Not userObject Is Nothing And TypeOf userObject Is String Then userFieldName = TryCast(userObject, String) End If Else ' First time the extension has been run. Initialize with default values. InitNewExtension() End If ' Set the positions of the fields. SetFieldIndexes() ' Set the current user name. userName = GetCurrentUser() End Sub ''' <summary> ''' Informs the extension that the class is being disposed of. ''' </summary> Public Sub Shutdown() Implements IClassExtension.Shutdown classHelper = Nothing End Sub #End Region #Region "IObjectClassEvents Methods" ''' <summary> ''' Fired when an object's attributes or geometry is updated. ''' </summary> ''' <param name="obj">The updated object.</param> Public Sub OnChange(ByVal obj As IObject) Implements IObjectClassEvents.OnChange ' Set the modified field's value to the current date and time. If modifiedFieldIndex <> -1 Then obj.Value(modifiedFieldIndex) = DateTime.Now ' Set the user field's value to the current user. If userFieldIndex <> -1 Then obj.Value(userFieldIndex) = userName End If End If End Sub ''' <summary> ''' Fired when a new object is created. ''' </summary> ''' <param name="obj">The new object.</param> Public Sub OnCreate(ByVal obj As IObject) Implements IObjectClassEvents.OnCreate ' Set the created field's value to the current date and time. If createdFieldIndex <> -1 Then obj.Value(createdFieldIndex) = DateTime.Now End If ' Set the user field's value to the current user. If userFieldIndex <> -1 Then obj.Value(userFieldIndex) = userName End If End Sub ''' <summary> ''' Fired when an object is deleted. ''' </summary> ''' <param name="obj">The deleted object.</param> Public Sub OnDelete(ByVal obj As IObject) Implements IObjectClassEvents.OnDelete End Sub #End Region #Region "IObjectClassInfo Methods" ''' <summary> ''' Indicates if updates to objects can bypass the Store method and OnChange notifications for efficiency. ''' </summary> ''' <returns>False; this extension requires Store to be called.</returns> Public Function CanBypassStoreMethod() As Boolean Implements IObjectClassInfo.CanBypassStoreMethod Return False End Function #End Region #Region "Public Members" ''' <summary> ''' Changes the member variables and extension properties to store the provided field names ''' as the created, modified and user fields (positions are also refreshed). Empty strings ''' indicate the values should not be saved in a field. ''' </summary> ''' <param name="createdField">The name of the "created" field.</param> ''' <param name="modifiedField">The name of the "modified" field.</param> ''' <param name="userField">The name of the "user" field.</param> Public Sub SetTimestampFields(ByVal createdField As String, ByVal modifiedField As String, ByVal userField As String) Dim baseClass As IClass = classHelper.Class Dim schemaLock As ISchemaLock = CType(baseClass, ISchemaLock) Try ' Get an exclusive lock. We want to do this prior to making any changes ' to ensure the member variables and extension properties remain synchronized. schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock) ' Set the name member variables. createdFieldName = createdField modifiedFieldName = modifiedField userFieldName = userField ' Modify the extension properties. extensionProperties.SetProperty(Resources.CreatedFieldKey, createdFieldName) extensionProperties.SetProperty(Resources.ModifiedFieldKey, modifiedFieldName) extensionProperties.SetProperty(Resources.UserFieldKey, userFieldName) ' Change the properties. Dim classSchemaEdit As IClassSchemaEdit2 = CType(baseClass, IClassSchemaEdit2) classSchemaEdit.AlterClassExtensionProperties(extensionProperties) Catch comExc As Exception Throw New Exception(Resources.FailedToSavePropertiesMsg, comExc) Finally schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock) End Try End Sub ''' <summary> ''' The field storing the creation date of features. ''' </summary> Public ReadOnly Property CreatedField() As String Get CreatedField = createdFieldName End Get End Property ''' <summary> ''' The field storing the modification date of features. ''' </summary> Public ReadOnly Property ModifiedField() As String Get ModifiedField = modifiedFieldName End Get End Property ''' <summary> ''' The field storing the user who created or last modified the feature. ''' </summary> Public ReadOnly Property UserField() As String Get UserField = userFieldName End Get End Property #End Region #Region "Private Methods" ''' <summary> ''' This method should be called the first time the extension is initialized, when the ''' extension properties are null. This will create a new set of properties with the default ''' field names. ''' </summary> Private Sub InitNewExtension() ' First time the extension has been run, initialize the extension properties. extensionProperties = New PropertySetClass() extensionProperties.SetProperty(Resources.CreatedFieldKey, createdFieldName) extensionProperties.SetProperty(Resources.ModifiedFieldKey, modifiedFieldName) extensionProperties.SetProperty(Resources.UserFieldKey, userFieldName) ' Store the properties. Dim baseClass As IClass = classHelper.Class Dim classSchemaEdit As IClassSchemaEdit2 = CType(baseClass, IClassSchemaEdit2) classSchemaEdit.AlterClassExtensionProperties(extensionProperties) End Sub ''' <summary> ''' Gets the name of the extension's user. For local geodatabases, this is the username as known ''' by the operating system (in a domain\username format). For remote geodatabases, the ''' IDatabaseConnectionInfo interface is utilized. ''' </summary> ''' <returns>The name of the current user.</returns> Private Function GetCurrentUser() As String ' Get the base class' workspace. Dim baseClass As IClass = classHelper.Class Dim dataset As IDataset = CType(baseClass, IDataset) Dim workspace As IWorkspace = dataset.Workspace ' If supported, use the IDatabaseConnectionInfo interface to get the username. Dim databaseConnectionInfo As IDatabaseConnectionInfo = TryCast(workspace, IDatabaseConnectionInfo) If Not databaseConnectionInfo Is Nothing Then Dim connectedUser As String = databaseConnectionInfo.ConnectedUser ' If the username is longer than the user field allows, shorten it. If connectedUser.Length > userFieldLength Then connectedUser = connectedUser.Substring(0, userFieldLength) End If Return connectedUser End If ' Get the current Windows user. Dim userDomain As String = Environment.UserDomainName Dim userName As String = Environment.UserName Dim qualifiedUserName As String = String.Format("{0}\{1}", userDomain, userName) ' If the user name is longer than the user field allows, shorten it. If (qualifiedUserName.Length > userFieldLength) Then qualifiedUserName = qualifiedUserName.Substring(0, userFieldLength) End If GetCurrentUser = qualifiedUserName End Function ''' <summary> ''' Finds the positions of the created, modified and user fields, and verifies that ''' the specified field has the correct data type. ''' </summary> Private Sub SetFieldIndexes() ' Get the base class from the class helper. Dim baseClass As IClass = classHelper.Class ' Find the indexes of the fields. createdFieldIndex = baseClass.FindField(createdFieldName) modifiedFieldIndex = baseClass.FindField(modifiedFieldName) userFieldIndex = baseClass.FindField(userFieldName) ' Verify that the field data types are correct. Dim fields As IFields = baseClass.Fields If createdFieldIndex <> -1 Then Dim createdField As IField = fields.Field(createdFieldIndex) ' If the "created" field is not a date field, do not use it. If createdField.Type <> esriFieldType.esriFieldTypeDate Then createdFieldIndex = -1 End If End If If modifiedFieldIndex <> -1 Then Dim modifiedField As IField = fields.Field(modifiedFieldIndex) ' If the "modified" field is not a date field, do not use it. If modifiedField.Type <> esriFieldType.esriFieldTypeDate Then modifiedFieldIndex = -1 End If End If If userFieldIndex <> -1 Then Dim userField As IField = fields.Field(userFieldIndex) ' If the "user" field is not a text field, do not use it. If userField.Type <> esriFieldType.esriFieldTypeString Then userFieldIndex = -1 Else ' Get the length of the text field. userFieldLength = userField.Length End If End If End Sub #End Region #Region "COM Registration Function(s)" <ComRegisterFunction(), ComVisibleAttribute(False)> _ Public Shared Sub RegisterFunction(ByVal registerType As Type) Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID) GeoObjectClassExtensions.Register(regKey) End Sub <ComUnregisterFunction(), ComVisibleAttribute(False)> _ Public Shared Sub UnregisterFunction(ByVal registerType As Type) Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID) GeoObjectClassExtensions.Unregister(regKey) End Sub #End Region End Class