Reconcile_SDE_versions_in_batch_mode
frmBatchReconcile.frm

' Copyright 1995-2004 ESRI

' All rights reserved under the copyright laws of the United States.

' You may freely redistribute and use this sample code, with or without modification.

' Disclaimer: THE SAMPLE CODE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 
' WARRANTIES, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
' FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESRI OR 
' CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
' OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
' SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
' INTERRUPTION) SUSTAINED BY YOU OR A THIRD PARTY, HOWEVER CAUSED AND ON ANY 
' THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ARISING IN ANY 
' WAY OUT OF THE USE OF THIS SAMPLE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF 
' SUCH DAMAGE.

' For additional information contact: Environmental Systems Research Institute, Inc.

' Attn: Contracts Dept.

' 380 New York Street

' Redlands, California, U.S.A. 92373 

' Email: contracts@esri.com

Option Explicit

'API calls to set the cursor to the hourglass
Private Declare Function SetCursor Lib "user32" (ByVal hCursor As Long) As Long
Private Declare Function LoadCursorByIndex Lib "user32" Alias "LoadCursorA" (ByVal hInstance As Long, ByVal lpCursorIndex As Long) As Long
Private Const IDC_WAIT = 32514&

Private Const k_sDefaultVersion As String = "SDE.DEFAULT"

'Reference to the workspace the user originally selected
Private m_pVersionedWorkspace As IVersionedWorkspace

Private Sub Form_Load()
  lstSdeConnectionInfo.AddItem "SERVER:"
  lstSdeConnectionInfo.AddItem "INSTANCE:"
  SetControlState
End Sub

'Ensures that controls are only enabled when they should be
Private Sub SetControlState()
  'cmdStart isn't enabled unless you're connected to a workspace
  'and a version is selected
  cmdStart.Enabled = CBool(Not m_pVersionedWorkspace Is Nothing) And _
                     CBool(Not tvwVersions.SelectedItem Is Nothing)
                     
  'cmdRefresh isn't enabled unless you're connected to a workspace
  cmdRefresh.Enabled = CBool(Not m_pVersionedWorkspace Is Nothing)
  
  'Can't delete unless have posted first..
  chkDeleteOnPost.Enabled = CBool(chkPost.Value = 1)
End Sub

Private Sub chkPost_Click()
  SetControlState
End Sub

Private Sub tvwVersions_Click()
  SetControlState
End Sub

Private Sub cmdClose_Click()
  Unload Me
End Sub

'Reload the Version Tree to account for versions changing
Private Sub cmdRefresh_Click()
  LoadVersionTree m_pVersionedWorkspace
End Sub


'Get connected to an SDE Database through the GxDialog
Private Sub cmdBrowse_Click()
  On Error GoTo ErrorHandler
  
  Dim pGXFilter As IGxObjectFilter
  Dim pGXDialog As IGxDialog
  Dim pGXEnum As IEnumGxObject
  Dim pGxObject As IGxObject
  Dim pGxDatabase2 As IGxDatabase2
  Dim pPropset As IPropertySet
  Dim vProp As Variant
  
  'Create a GxDialog and GXFilter for workspaces
  Set pGXDialog = New GxDialog
  Set pGXFilter = New GxFilterWorkspaces

  'Set up the GxDialog
  With pGXDialog
    .Title = "Browse for SDE Database"
    .StartingLocation = "Database Connections"
    .ButtonCaption = "OK"
    .AllowMultiSelect = False
    Set .ObjectFilter = pGXFilter
  End With
  
  'Open the GxDialog and get the selected SDE Workspace
  pGXDialog.DoModalOpen Me.hWnd, pGXEnum
  
  If Not pGXEnum Is Nothing Then
    Set pGxObject = pGXEnum.Next
    
    If Not pGxObject Is Nothing Then
      If TypeOf pGxObject Is IGxDatabase2 Then
        Set pGxDatabase2 = pGxObject
        If pGxDatabase2.IsEnterpriseGeodatabase Then
          If pGxDatabase2.IsConnected Then
            Set m_pVersionedWorkspace = pGxDatabase2.Workspace
            
            On Error Resume Next
            Set pPropset = pGxDatabase2.WorkspaceName.ConnectionProperties
            lstSdeConnectionInfo.Clear
            lstSdeConnectionInfo.AddItem "SERVER: " & pPropset.GetProperty("SERVER")
            lstSdeConnectionInfo.AddItem "INSTANCE: " & pPropset.GetProperty("INSTANCE")
            lstSdeConnectionInfo.AddItem "DATABASE: " & pPropset.GetProperty("DATABASE")
            lstSdeConnectionInfo.AddItem "USER: " & pPropset.GetProperty("USER")
            
            'Load all the Versions into the treeview
            LoadVersionTree m_pVersionedWorkspace
          End If
        End If
      End If
    End If
  End If
  
  Exit Sub
ErrorHandler:
  MsgBox Err.Description
End Sub

'Load tvwVersions with all the Versions in m_pVersionedWorkspace
Private Sub LoadVersionTree(ByVal pVersionedWorkspace As IVersionedWorkspace)
  tvwVersions.Nodes.Clear
  AddVersionToTree pVersionedWorkspace.DefaultVersion.VersionInfo, Nothing
  SetControlState
End Sub

'Recursive function that first adds the parent VersionInfo to tvwVersions and
'then calls itself recursively for each child VersionInfo
Private Sub AddVersionToTree(ByVal pVersionInfo As IVersionInfo, ByVal pParentNode As Node)
  Dim pEnumVersionInfo As IEnumVersionInfo
  Dim pNode As Node
  
  'Add the parent VersionInfo to the TreeView
  If pParentNode Is Nothing Then
    Set pNode = tvwVersions.Nodes.Add(, , pVersionInfo.VersionName, pVersionInfo.VersionName)
  Else
    Set pNode = tvwVersions.Nodes.Add(pParentNode.Index, tvwChild, pVersionInfo.VersionName, pVersionInfo.VersionName)
  End If
  
  'Loop through calling this function for each child VersionInfo
  'Add each VersionInfo to the Treeview of all versions
  Set pEnumVersionInfo = pVersionInfo.Children
  Set pVersionInfo = pEnumVersionInfo.Next
  Do Until pVersionInfo Is Nothing
    AddVersionToTree pVersionInfo, pNode
    Set pVersionInfo = pEnumVersionInfo.Next
  Loop
  
  pNode.Expanded = True
  
  Set pEnumVersionInfo = Nothing
  Set pNode = Nothing
End Sub

'Starts Reconcile/Post/Delete or Reconcile/Post or Reconcile depending
'on what options are selected
Private Sub cmdStart_Click()
  On Error GoTo ErrorHandler
  
  Dim pVersion As IVersion
  Dim sVersionName As String
  
  'Set the cursor to be the Hourglass until the processing is finished
  SetCursor LoadCursorByIndex(0, IDC_WAIT)
  sVersionName = tvwVersions.SelectedItem.Text
  
  'This returns an error if the version no longer exists.
  Set pVersion = m_pVersionedWorkspace.FindVersion(sVersionName)
  
  If (chkPost.Value = 1) Then
    'Reconcile and Post all versions
    ReconcileVersions pVersion.VersionInfo.Children, pVersion.VersionName, _
                      True, CBool(chkDeleteOnPost.Value = 1)
  End If
  
  'Reconcile all versions that are still out there to push changes down
  'We do this even after Posting to ensure all non-deleted versions get
  'updated with the latest changes to the ancestor versions
  ReconcileVersions pVersion.VersionInfo.Children, pVersion.VersionName, _
                    False, False
  
  OutputMessage "Finished"
  
  Set pVersion = Nothing
  
  Exit Sub
ErrorHandler:
  MsgBox "Start Reconcile: " & Err.Description
End Sub

'This is a recursive function that
'If posting:
'1-Reconciles child versions of the versions passed in through pEnumVersionInfo
'2-Reconciles the versions passed in through pEnumVersionInfo
'If NOT posting:
'1-Reconciles the versions passed in through pEnumVersionInfo
'2-Reconciles child versions of the versions passed in through pEnumVersionInfo
Private Function ReconcileVersions(ByVal pEnumVersionInfo As IEnumVersionInfo, _
                                   ByVal sParentVersionName As String, _
                                   ByVal bPost As Boolean, _
                                   ByVal bDeleteOnPost As Boolean) As Boolean
  On Error GoTo ErrorHandler
  
  Dim pVersionInfo As IVersionInfo
  Dim pVersionEdit As IVersionEdit
  
  ReconcileVersions = True
  
  Set pVersionInfo = pEnumVersionInfo.Next
  Do Until pVersionInfo Is Nothing
    If bPost Then
      'Reconcile child versions first to ensure current version get all of the changes of all the child versions
      If Not ReconcileVersions(pVersionInfo.Children, pVersionInfo.VersionName, bPost, bDeleteOnPost) Then
        ReconcileVersions = False
        OutputMessage "Could not reconcile all children of " & pVersionInfo.VersionName
      Else
        'Finished reconciling Children of version, now reconcile current version
        OutputMessage "Changing to " & pVersionInfo.VersionName
        Set pVersionEdit = m_pVersionedWorkspace.FindVersion(pVersionInfo.VersionName)
        
        If Not ReconcileVersion(pVersionEdit, sParentVersionName, bPost, bDeleteOnPost) Then
          ReconcileVersions = False
        End If
      End If
    Else '(not bPost)
      'Reconcile version before child versions to ensure the children
      'get all the changes in ancestor lineage
      OutputMessage "Changing to " & pVersionInfo.VersionName
      Set pVersionEdit = m_pVersionedWorkspace.FindVersion(pVersionInfo.VersionName)
      
      'Reconcile pVersionInfo
      If ReconcileVersion(pVersionEdit, sParentVersionName, bPost, bDeleteOnPost) Then
        'Finished reconciling current version, Reconcile children of current version
        ReconcileVersions pVersionInfo.Children, pVersionInfo.VersionName, bPost, bDeleteOnPost
      End If
    End If
    Set pVersionInfo = pEnumVersionInfo.Next
  Loop
  
  Set pEnumVersionInfo = Nothing
  
  Exit Function
ErrorHandler:
  MsgBox "ReconcileVersions: " & Err.Description
  ReconcileVersions = False
  Exit Function
  Resume
End Function

'This function tries to Reconcile/Post/Delete, Reconcile/Post, or Reconcile the
'Version passed in as pVersionEdit with it's parent version passed in as sParentVersionName.
'If sucessful, the changes are saved in the version.  If there are conflicts,
'The changes introduced in reconcile are NOT saved.
Private Function ReconcileVersion(ByVal pVersionEdit As IVersionEdit, _
                                  ByVal sParentVersionName As String, _
                                  ByVal bPost As Boolean, _
                                  ByVal bDeleteOnPost As Boolean) As Boolean
  Dim pVersion As IVersion
  
  ReconcileVersion = True
  Set pVersion = pVersionEdit
  
  SetImage pVersion, 1
  
  'check to make sure nobody has it locked.
  If IsLocked(pVersionEdit) Then
    ReconcileVersion = False
  Else
    StartEditing pVersionEdit
    On Error GoTo AbortOperation
    
    OutputMessage "Reconciling " & pVersion.VersionName & " with " & sParentVersionName
    If pVersionEdit.Reconcile(sParentVersionName) Then
    
      'There were conflicts, get out without saving
      OutputMessage "Found conflicts reconciling " & pVersion.VersionName & " with " & sParentVersionName
      StopEditing pVersionEdit, False
      ReconcileVersion = False
      
    Else 'No Conflicts in reconcile
      If pVersionEdit.CanPost And bPost Then
      
        'Post and save edits
        OutputMessage "Posting " & pVersion.VersionName
        pVersionEdit.Post sParentVersionName
        StopEditing pVersionEdit, True
        
        'Delete version if specified
        If bDeleteOnPost Then
          DeleteVersion pVersionEdit
        End If
                    
      Else
        'Not posted, but save edits
        StopEditing pVersionEdit, True
      End If
    End If
  End If
  
  If ReconcileVersion Then
    SetImage pVersion, 3
  Else
    SetImage pVersion, 2
  End If
  
  Set pVersion = Nothing
  
  Exit Function
AbortOperation:
  StopEditing pVersionEdit, False
  ReconcileVersion = False
  OutputMessage "Error in ReconcileVersion: " & Err.Description
End Function

'Helper function to get the name of a Version's parent
Private Function GetParentVersionName(ByVal pVersion As IVersion) As String
  Dim pVersionInfo As IVersionInfo
  Dim pParentVersionInfo As IVersionInfo
  
  If pVersion.HasParent Then
    Set pVersionInfo = pVersion.VersionInfo
    Set pParentVersionInfo = pVersionInfo.Parent
    GetParentVersionName = pParentVersionInfo.VersionName
  End If
  
  Set pVersionInfo = Nothing
  Set pParentVersionInfo = Nothing
End Function

'Helper function to identify is a version is locked by someone else
Private Function IsLocked(ByVal pVersion As IVersion) As Boolean
  On Error GoTo ErrorHandler
  
  Dim pEnumLockInfo As IEnumLockInfo
  Dim pLockInfo As ILockInfo
  
  Set pEnumLockInfo = pVersion.VersionLocks
  Set pLockInfo = pEnumLockInfo.Next
  Do Until pLockInfo Is Nothing
    OutputMessage pVersion.VersionName & " is already in use by " & pLockInfo.UserName
    Set pLockInfo = pEnumLockInfo.Next
    IsLocked = True
  Loop
  
  Set pEnumLockInfo = Nothing
  
  Exit Function
ErrorHandler:
  IsLocked = True
  OutputMessage "IsLocked: " & Err.Description
End Function

'Helper sub to display messages tracking progress
Private Sub OutputMessage(ByVal sMessage As String)
  Debug.Print sMessage
  StatusBar.SimpleText = sMessage
  StatusBar.Refresh
End Sub

'Helper function to start editing
Private Sub StartEditing(ByVal pWorkspaceEdit As IWorkspaceEdit)
  pWorkspaceEdit.StartEditing False
  pWorkspaceEdit.StartEditOperation
End Sub

'Helper function to Stop Editing
Private Sub StopEditing(ByVal pWorkspaceEdit As IWorkspaceEdit, ByVal bSave As Boolean)
  If bSave Then
    pWorkspaceEdit.StopEditOperation
    pWorkspaceEdit.StopEditing True
  Else
    pWorkspaceEdit.AbortEditOperation
    pWorkspaceEdit.StopEditing False
  End If
End Sub

'Helper function to delete the version both from the VersionedWorkspace
'as well as to remove it from tvwVersions
Private Sub DeleteVersion(ByVal pVersion As IVersion)
  On Error GoTo ErrorHandler
  
  Dim sVersionName As String
  
  sVersionName = pVersion.VersionName
  pVersion.Delete
  tvwVersions.Nodes.Remove sVersionName
  tvwVersions.Refresh
  
  Exit Sub
ErrorHandler:
  If Err.Number = -2147217144 Then
    OutputMessage "Could not delete " & pVersion.VersionName & " because of lack of permissions"
    Exit Sub
  Else
    OutputMessage "ERROR in DeleteVersion: " & Err.Description
  End If
End Sub

Private Sub SetImage(ByVal pVersion As IVersion, ByVal Image As Variant)
  On Error Resume Next
  tvwVersions.Nodes.Item(pVersion.VersionName).Image = Image
  tvwVersions.Refresh
End Sub