• Hi All

    Please note that at the Chandoo.org Forums there is Zero Tolerance to Spam

    Post Spam and you Will Be Deleted as a User

    Hui...

  • When starting a new post, to receive a quicker and more targeted answer, Please include a sample file in the initial post.

Maintain forms and modules in multiple workbooks

David Brown

New Member
I maintain a suite of data collection sheets across 25 diverse sites. Each site has copies of the same workbook containing their own data. Each workbook has a number of forms and modules. These need regular maintenance, any change to a form means opening the workbook; deleting the old form; importing the new and then closing the file., 25 times for each change.

Is there a way to automate this process ?
 
David

Firstly, Welcome to the Chandoo.org Forums

Can you put the files on a Network Drive and either:
1. Centrally access them or
2. Distribute them to each PC using a simple script
 
Thanks for the greeting Hui, I have been lurking here for sometime and have to confess to being impressed. The files are all on a networked drive, in separate folders and I do access them centrally on the network and remotely via VPN.
 
Hi, David Brown!

After thinking a while I realized that from time to time I have to do the same process, not with 25 but with 3 or 4 sets of a a few files, so why not...?

Give a look at the uploaded file. It contains 6 files:
... Template ... : the main magic workbook
... File 1 ... thru ... File 5 ... : the workbook set

There're 2 constants for identifying which user form (ufAdmin) and which module (modAdmin) shouldn't be copied:
Global Const gksFormAdmin = "ufAdmin"
Global Const gksModuleAdmin = "modAdmin"

When opened the template an user form (ufAdmin) is displayed where you have to enter:
- source path (manually entered or dialog based)
- filename pattern (wildcards allowed)
- backup path (manually entered or dialog based)
- what to do with mismatched forms (keep or delete)
- what to do with mismatched modules (keep or delete)

You have 3 options:
- reset the form
- go (and pray, of course!, claims will be rejected and redirected to that bearded guy, if... Nietzsche dixit)
- exit

The Go option has a previous validation for:
- existing source path
- existing filenames matching pattern
- existing backup path
- writable backup path
- forms mismatching selection
- modules mismatching selection

If validation is ok, it does:
- create a subfolder in backup path with actual date & time
- export there all forms and modules (except those Admin)
- build 2 lists of copyable forms and modules
- process source path for matching files
- for each one, delete forms/modules on copyable lists or if mismatched and delete selected
- import the exported forms/modules
- clean the backup subfolder deleting exported components
- display message with result "m updated of n read"

All the removals and additions are printed in the immediate window for easy checking.

In the only (empty) worksheets you'll find 2 command buttons:
- one for a preview of what you've loaded in your actual running instance of Excel
- one for manually loading the admin user form

I usually post the code here but this time is a bit long. I'll do it anyway, but only for the validation and the go procedures:
CODE IN NEXT POSTs (3), since I reached a 10K characters post limit... twice at least. New for me.

I hope I'm not missing anything. Just advise if any issue.

Regards!
 

Attachments

  • Maintain forms and modules in multiple workbooks (for David Brown at chandoo.org).zip
    133.5 KB · Views: 7
Last edited:
Code:
Private Sub Validating(pbOk As Boolean)
    ' constants
    Const ksExt = ".txt"
    ' declarations
    Dim I As Integer, A As String
    ' start
    pbOk = True
    ' process
    '  source path
    If pbOk Then
        ' exists?
        On Error Resume Next
        ChDir gsPath(1)
        If Err.Number <> 0 Then
            Err.Clear
            MsgBox "Invalid source path", vbApplicationModal + vbCritical + vbOKOnly, "Error"
            pbOk = False
        End If
        On Error GoTo 0
    End If
    '  filename
    If pbOk Then
        ' exists?
        gsFileName = ufAdmin.txtFilename.Text
        A = Dir(gsPath(1) & gsFileName)
        If A = "" Then
            MsgBox "No files matching in source path", vbApplicationModal + vbCritical + vbOKOnly, "Error"
            pbOk = False
        End If
    End If
    '  backup path
    If pbOk Then
        ' blank?
        If gsPath(2) = "" Then gsPath(2) = gsPath(1)
        ' exists?
        On Error Resume Next
        ChDir gsPath(2)
        If Err.Number <> 0 Then
            Err.Clear
            MsgBox "Invalid backup path", vbApplicationModal + vbCritical + vbOKOnly, "Error"
            pbOk = False
        End If
        '  writable?
        If pbOk Then
            On Error Resume Next
            A = gsPath(2) & Format(Now(), "yyyymmdd_hhmmss") & ksExt
            I = FreeFile()
            Open A For Output As #I
            If Err.Number <> 0 Then
                Err.Clear
                MsgBox "Non writable backup path", vbApplicationModal + vbCritical + vbOKOnly, "Error"
                pbOk = False
            End If
            Close #I
            DoEvents
            Kill A
        End If
        On Error GoTo 0
    End If
    '  user form
    If pbOk Then
        ' any selected?
        With ufAdmin
            If .optFormKeep = False And .optFormDelete = False Then
                MsgBox "No user form option selected", vbApplicationModal + vbCritical + vbOKOnly, "Error"
                pbOk = False
            Else
                gbFormDelete = .optFormDelete
            End If
        End With
    End If
    '  module
    If pbOk Then
        ' any selected?
        With ufAdmin
            If .optModuleKeep = False And .optModuleDelete = False Then
                MsgBox "No module option selected", vbApplicationModal + vbCritical + vbOKOnly, "Error"
                pbOk = False
            Else
                gbModuleDelete = .optModuleDelete
            End If
       
        End With
    End If
    ' end
End Sub
Code:
Sub JustDoIt()
    '
    ' constants
    Const ksExtFrm = ".frm"
    Const ksExtFrx = ".frx"
    Const ksExtBas = ".bas"
    Const ksAsterix = "*"
    '
    ' declarations
    Dim sForm() As String, sModule() As String, bComponents() As Boolean
    Dim iForm As Integer, iModule As Integer
    Dim iRead As Integer, iWrite As Integer, sFolder As String
    Dim I As Integer, J As Integer, A As String, bOk As Boolean
    '
    ' start
    '  validation
    Validating bOk
    If Not bOk Then Exit Sub
    '  create backup subfolder
    sFolder = Format(Now(), "yyyymmdd_hhmmss")
    MkDir gsPath(2) & sFolder
    '  template
    With ThisWorkbook.VBProject
        iForm = 0
        iModule = 0
        ReDim bComponents(.VBComponents.Count)
        For I = 1 To .VBComponents.Count
            With .VBComponents(I)
                bComponents(I) = False
                A = .Name
                Select Case .Type
                    Case vbext_ct_MSForm
                        If .Name <> gksFormAdmin Then
                            iForm = iForm + 1
                            ReDim Preserve sForm(iForm)
                            sForm(iForm) = A
                            bComponents(I) = True
                            .Export gsPath(2) & sFolder & Application.PathSeparator & _
                                A & ksExtFrm
                        End If
                    Case vbext_ct_StdModule
                        If .Name <> gksModuleAdmin Then
                            iModule = iModule + 1
                            ReDim Preserve sModule(iModule)
                            sModule(iModule) = A
                            bComponents(I) = True
                            .Export gsPath(2) & sFolder & Application.PathSeparator & _
                                A & ksExtBas
                        End If
                End Select
            End With
        Next I
    End With
    '
    ' process
    iRead = 0
    iWrite = 0
    If bOk Then
        A = Dir(gsPath(1) & gsFileName)
        Do Until A = ""
            If A <> ThisWorkbook.Name Then
                ' count
                iRead = iRead + 1
                ' backup
                FileCopy gsPath(1) & A, _
                        gsPath(2) & sFolder & Application.PathSeparator & A
                ' open
                On Error Resume Next
                Workbooks.Open gsPath(1) & A, False
                If Err.Number <> 0 Then
                    Err.Clear
                Else
                    ' main
                    iWrite = iWrite + 1
                    '  delete old components
                    With ActiveWorkbook.VBProject
                        For I = .VBComponents.Count To 1 Step -1
                            With .VBComponents(I)
                                A = .Name
                                Select Case .Type
                                    Case vbext_ct_MSForm
                                        For J = 1 To iForm
                                            If A = sForm(J) Then Exit For
                                        Next J
                                        If J <= iForm Or J > iForm And gbFormDelete Then
                                            ' delete form
                                            Debug.Print ActiveWorkbook.Name, "delete form", A
                                            ActiveWorkbook.VBProject.VBComponents.Remove _
                                                ActiveWorkbook.VBProject.VBComponents(I)
                                        End If
                                    Case vbext_ct_StdModule
                                        For J = 1 To iModule
                                            If A = sModule(J) Then Exit For
                                        Next J
                                        If J <= iModule Or J > iModule And gbModuleDelete Then
                                            ' delete module
                                            Debug.Print ActiveWorkbook.Name, "delete module", A
                                            ActiveWorkbook.VBProject.VBComponents.Remove _
                                                ActiveWorkbook.VBProject.VBComponents(I)
                                        End If
                                End Select
                            End With
                        Next I
                    End With
                    '  add new components
                    With ThisWorkbook.VBProject
                        For I = 1 To .VBComponents.Count
                            With .VBComponents(I)
                                If bComponents(I) Then
                                    A = .Name
                                    Select Case .Type
                                        Case vbext_ct_MSForm
                                            ' add form
                                            Debug.Print ActiveWorkbook.Name, "add form", A
                                            ActiveWorkbook.VBProject.VBComponents.Import _
                                                gsPath(2) & sFolder & Application.PathSeparator & _
                                                A & ksExtFrm
                                        Case vbext_ct_StdModule
                                            ' add module
                                            Debug.Print ActiveWorkbook.Name, "add module", A
                                            ActiveWorkbook.VBProject.VBComponents.Import _
                                                gsPath(2) & sFolder & Application.PathSeparator & _
                                                A & ksExtBas
                                    End Select
                                End If
                            End With
                        Next I
                    End With
                    ' save
                    ActiveWorkbook.Close True
                End If
                On Error GoTo 0
            End If
            ' cycle
            A = Dir()
        Loop
        ' cleanup
        On Error Resume Next
        Kill gsPath(2) & sFolder & Application.PathSeparator & ksAsterix & ksExtFrm
        Kill gsPath(2) & sFolder & Application.PathSeparator & ksAsterix & ksExtFrx
        Kill gsPath(2) & sFolder & Application.PathSeparator & ksAsterix & ksExtBas
        On Error GoTo 0
        ' msg
        MsgBox CStr(iWrite) & " files updated from " & CStr(iRead) & " files matching", _
            vbApplicationModal + IIf(iWrite = iRead, vbInformation, vbExclamation) + vbOKOnly, _
            "Summary"
    End If
    '
    ' end
    Unload ufAdmin
    Beep
    '
End Sub
 
It also has a quick and dirty analyzer of what's loaded, this is the code:
Code:
Sub ThisGuyIsMoreLazyThanI_QuickAndDirtyView()
    ' constants
    ' declarations
    Dim I As Integer, J As Integer
    ' start
    ' process
    '
    Debug.Print "By VBAProjects"
    With Application.VBE
        I = .VBProjects.Count
        For J = 1 To I
            With .VBProjects(J)
                Debug.Print J; I, .Name, .Description,
                On Error Resume Next
                Debug.Print .Filename,
                If Err.Number <> 0 Then
                    Debug.Print "<file not saved yet>",
                    Err.Clear
                End If
                On Error GoTo 0
                If Not CBool(.Protection) Then
                    Debug.Print .VBComponents.Count;
                Else
                    Debug.Print "<protected>";
                End If
                Debug.Print
            End With
        Next J
    End With
    Debug.Print
    '
    Debug.Print "By Workbooks"
    With Application
        I = .Workbooks.Count
        For J = 1 To I
            With .Workbooks(J)
                Debug.Print J; I, .Name,
                On Error Resume Next
                Debug.Print .FullName,
                If Err.Number <> 0 Then
                    Debug.Print "<file not saved yet>",
                    Err.Clear
                End If
                On Error GoTo 0
                If .HasVBProject Then
                    If .VBProject.Protection = vbext_pp_none Then
                        Debug.Print .VBProject.VBComponents.Count;
                    Else
                        Debug.Print "<protected>";
                    End If
                Else
                    Debug.Print 0;
                End If
                Debug.Print
            End With
        Next J
    End With
    Debug.Print
    '
    Debug.Print "ThisWorkbook"
    With ThisWorkbook.VBProject
        I = .VBComponents.Count
        For J = 1 To I
            With .VBComponents(J)
                Debug.Print J; I; .Name; .Type
            End With
        Next J
    End With
    ' end
End Sub
 
That looks very interesting. I will set up some test files and work through this hopefully early next week. Thank you for your input much appreciated, something to which I have struggled to find a solution
 
Hi, David Brown!
Files 1 thru 5 are for testing purposes, you should check them first, then run the update from the template and check them after. I'd suggest you to play with the keep/delete options.
It's easy, just 1 line in the modules and 1 label in the forms.
Regards!
 
Thanks for your help SirJB7, that is exactly the guidance that I was looking for. I have been going around and around with syntax and coming up blank for a while. I have adapted your code to meet my needs but the time involved was far reduced by your assistance.

Again, many thanks.
 
Hi, David Brown!
Glad you solved it. Thanks for your feedback and for your kind words too. And welcome back whenever needed or wanted.
Regards!
PS: If your adapted code works differently (i.e., my code doesn't do the job on the uploaded test files, that's to say it doesn't work fine) it'd be nice if you post it so as people who read this could have an alternative.
 
Back
Top