How do you use version control with Access development?

后端 未结 20 1700
慢半拍i
慢半拍i 2020-11-22 12:55

I\'m involved with updating an Access solution. It has a good amount of VBA, a number of queries, a small amount of tables, and a few forms for data entry & report gene

相关标签:
20条回答
  • 2020-11-22 13:08

    We wrote our own script in VBScript, that uses the undocumented Application.SaveAsText() in Access to export all code, form, macro and report modules. Here it is, it should give you some pointers. (Beware: some of the messages are in german, but you can easily change that.)

    EDIT: To summarize various comments below: Our Project assumes an .adp-file. In order to get this work with .mdb/.accdb, you have to change OpenAccessProject() to OpenCurrentDatabase(). (Updated to use OpenAccessProject() if it sees a .adp extension, else use OpenCurrentDatabase().)

    decompose.vbs:

    ' Usage:
    '  CScript decompose.vbs <input file> <path>
    
    ' Converts all modules, classes, forms and macros from an Access Project file (.adp) <input file> to
    ' text and saves the results in separate files to <path>.  Requires Microsoft Access.
    '
    
    Option Explicit
    
    const acForm = 2
    const acModule = 5
    const acMacro = 4
    const acReport = 3
    
    ' BEGIN CODE
    Dim fso
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    dim sADPFilename
    If (WScript.Arguments.Count = 0) then
        MsgBox "Bitte den Dateinamen angeben!", vbExclamation, "Error"
        Wscript.Quit()
    End if
    sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))
    
    Dim sExportpath
    If (WScript.Arguments.Count = 1) then
        sExportpath = ""
    else
        sExportpath = WScript.Arguments(1)
    End If
    
    
    exportModulesTxt sADPFilename, sExportpath
    
    If (Err <> 0) and (Err.Description <> NULL) Then
        MsgBox Err.Description, vbExclamation, "Error"
        Err.Clear
    End If
    
    Function exportModulesTxt(sADPFilename, sExportpath)
        Dim myComponent
        Dim sModuleType
        Dim sTempname
        Dim sOutstring
    
        dim myType, myName, myPath, sStubADPFilename
        myType = fso.GetExtensionName(sADPFilename)
        myName = fso.GetBaseName(sADPFilename)
        myPath = fso.GetParentFolderName(sADPFilename)
    
        If (sExportpath = "") then
            sExportpath = myPath & "\Source\"
        End If
        sStubADPFilename = sExportpath & myName & "_stub." & myType
    
        WScript.Echo "copy stub to " & sStubADPFilename & "..."
        On Error Resume Next
            fso.CreateFolder(sExportpath)
        On Error Goto 0
        fso.CopyFile sADPFilename, sStubADPFilename
    
        WScript.Echo "starting Access..."
        Dim oApplication
        Set oApplication = CreateObject("Access.Application")
        WScript.Echo "opening " & sStubADPFilename & " ..."
        If (Right(sStubADPFilename,4) = ".adp") Then
            oApplication.OpenAccessProject sStubADPFilename
        Else
            oApplication.OpenCurrentDatabase sStubADPFilename
        End If
    
        oApplication.Visible = false
    
        dim dctDelete
        Set dctDelete = CreateObject("Scripting.Dictionary")
        WScript.Echo "exporting..."
        Dim myObj
        For Each myObj In oApplication.CurrentProject.AllForms
            WScript.Echo "  " & myObj.fullname
            oApplication.SaveAsText acForm, myObj.fullname, sExportpath & "\" & myObj.fullname & ".form"
            oApplication.DoCmd.Close acForm, myObj.fullname
            dctDelete.Add "FO" & myObj.fullname, acForm
        Next
        For Each myObj In oApplication.CurrentProject.AllModules
            WScript.Echo "  " & myObj.fullname
            oApplication.SaveAsText acModule, myObj.fullname, sExportpath & "\" & myObj.fullname & ".bas"
            dctDelete.Add "MO" & myObj.fullname, acModule
        Next
        For Each myObj In oApplication.CurrentProject.AllMacros
            WScript.Echo "  " & myObj.fullname
            oApplication.SaveAsText acMacro, myObj.fullname, sExportpath & "\" & myObj.fullname & ".mac"
            dctDelete.Add "MA" & myObj.fullname, acMacro
        Next
        For Each myObj In oApplication.CurrentProject.AllReports
            WScript.Echo "  " & myObj.fullname
            oApplication.SaveAsText acReport, myObj.fullname, sExportpath & "\" & myObj.fullname & ".report"
            dctDelete.Add "RE" & myObj.fullname, acReport
        Next
    
        WScript.Echo "deleting..."
        dim sObjectname
        For Each sObjectname In dctDelete
            WScript.Echo "  " & Mid(sObjectname, 3)
            oApplication.DoCmd.DeleteObject dctDelete(sObjectname), Mid(sObjectname, 3)
        Next
    
        oApplication.CloseCurrentDatabase
        oApplication.CompactRepair sStubADPFilename, sStubADPFilename & "_"
        oApplication.Quit
    
        fso.CopyFile sStubADPFilename & "_", sStubADPFilename
        fso.DeleteFile sStubADPFilename & "_"
    
    
    End Function
    
    Public Function getErr()
        Dim strError
        strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
                   "From " & Err.source & ":" & vbCrLf & _
                   "    Description: " & Err.Description & vbCrLf & _
                   "    Code: " & Err.Number & vbCrLf
        getErr = strError
    End Function
    

    If you need a clickable Command, instead of using the command line, create a file named "decompose.cmd" with

    cscript decompose.vbs youraccessapplication.adp
    

    By default, all exported files go into a "Scripts" subfolder of your Access-application. The .adp/mdb file is also copied to this location (with a "stub" suffix) and stripped of all the exported modules, making it really small.

    You MUST checkin this stub with the source-files, because most access settings and custom menu-bars cannot be exported any other way. Just be sure to commit changes to this file only, if you really changed some setting or menu.

    Note: If you have any Autoexec-Makros defined in your Application, you may have to hold the Shift-key when you invoke the decompose to prevent it from executing and interfering with the export!

    Of course, there is also the reverse script, to build the Application from the "Source"-Directory:

    compose.vbs:

    ' Usage:
    '  WScript compose.vbs <file> <path>
    
    ' Converts all modules, classes, forms and macros in a directory created by "decompose.vbs"
    ' and composes then into an Access Project file (.adp). This overwrites any existing Modules with the
    ' same names without warning!!!
    ' Requires Microsoft Access.
    
    Option Explicit
    
    const acForm = 2
    const acModule = 5
    const acMacro = 4
    const acReport = 3
    
    Const acCmdCompileAndSaveAllModules = &H7E
    
    ' BEGIN CODE
    Dim fso
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    dim sADPFilename
    If (WScript.Arguments.Count = 0) then
        MsgBox "Please enter the file name!", vbExclamation, "Error"
        Wscript.Quit()
    End if
    sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))
    
    Dim sPath
    If (WScript.Arguments.Count = 1) then
        sPath = ""
    else
        sPath = WScript.Arguments(1)
    End If
    
    
    importModulesTxt sADPFilename, sPath
    
    If (Err <> 0) and (Err.Description <> NULL) Then
        MsgBox Err.Description, vbExclamation, "Error"
        Err.Clear
    End If
    
    Function importModulesTxt(sADPFilename, sImportpath)
        Dim myComponent
        Dim sModuleType
        Dim sTempname
        Dim sOutstring
    
        ' Build file and pathnames
        dim myType, myName, myPath, sStubADPFilename
        myType = fso.GetExtensionName(sADPFilename)
        myName = fso.GetBaseName(sADPFilename)
        myPath = fso.GetParentFolderName(sADPFilename)
    
        ' if no path was given as argument, use a relative directory
        If (sImportpath = "") then
            sImportpath = myPath & "\Source\"
        End If
        sStubADPFilename = sImportpath & myName & "_stub." & myType
    
        ' check for existing file and ask to overwrite with the stub
        if (fso.FileExists(sADPFilename)) Then
            WScript.StdOut.Write sADPFilename & " exists. Overwrite? (y/n) "
            dim sInput
            sInput = WScript.StdIn.Read(1)
            if (sInput <> "y") Then
                WScript.Quit
            end if
    
            fso.CopyFile sADPFilename, sADPFilename & ".bak"
        end if
    
        fso.CopyFile sStubADPFilename, sADPFilename
    
        ' launch MSAccess
        WScript.Echo "starting Access..."
        Dim oApplication
        Set oApplication = CreateObject("Access.Application")
        WScript.Echo "opening " & sADPFilename & " ..."
        If (Right(sStubADPFilename,4) = ".adp") Then
            oApplication.OpenAccessProject sADPFilename
        Else
            oApplication.OpenCurrentDatabase sADPFilename
        End If
        oApplication.Visible = false
    
        Dim folder
        Set folder = fso.GetFolder(sImportpath)
    
        ' load each file from the import path into the stub
        Dim myFile, objectname, objecttype
        for each myFile in folder.Files
            objecttype = fso.GetExtensionName(myFile.Name)
            objectname = fso.GetBaseName(myFile.Name)
            WScript.Echo "  " & objectname & " (" & objecttype & ")"
    
            if (objecttype = "form") then
                oApplication.LoadFromText acForm, objectname, myFile.Path
            elseif (objecttype = "bas") then
                oApplication.LoadFromText acModule, objectname, myFile.Path
            elseif (objecttype = "mac") then
                oApplication.LoadFromText acMacro, objectname, myFile.Path
            elseif (objecttype = "report") then
                oApplication.LoadFromText acReport, objectname, myFile.Path
            end if
    
        next
    
        oApplication.RunCommand acCmdCompileAndSaveAllModules
        oApplication.Quit
    End Function
    
    Public Function getErr()
        Dim strError
        strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
                   "From " & Err.source & ":" & vbCrLf & _
                   "    Description: " & Err.Description & vbCrLf & _
                   "    Code: " & Err.Number & vbCrLf
        getErr = strError
    End Function
    

    Again, this goes with a companion "compose.cmd" containing:

    cscript compose.vbs youraccessapplication.adp
    

    It asks you to confirm overwriting your current application and first creates a backup, if you do. It then collects all source-files in the Source-Directory and re-inserts them into the stub.

    Have Fun!

    0 讨论(0)
  • 2020-11-22 13:10

    This entry describes a totally different approach from the other entries, and may not be what you're looking for. So I won't be offended if you ignore this. But at least it is food for thought.

    In some professional commercial software development environments, configuration management (CM) of software deliverables is not normally done within the software application itself or software project itself. CM is imposed upon the final deliverable products, by saving the software in a special CM folder, where both the file and its folder are marked with version identification. For example, Clearcase allows the data manager to "check in" a software file, assign it a "branch", assign it a "bubble", and apply "labels". When you want to see and download a file, you have to configure your "config spec" to point to the version you want, then cd into the folder and there it is.

    Just an idea.

    0 讨论(0)
  • 2020-11-22 13:11

    There's a gotcha - VSS 6.0 can only accept MDB's using the add-in under a certain number of objects, which includes all local tables, queries, modules, and forms. Don't know the exact object limit.

    To build our 10 year old prod floor app, which is huge, we are forced to combine 3 or 4 separate MDBs out of SS into one MDB , which complicates automated builds to the point we don't waste time doing it.

    I think I'll try the script above to spew this MDb into SVN and simplify builds for everyone.

    0 讨论(0)
  • 2020-11-22 13:12

    Olivers answer rocks, but the CurrentProject reference was not working for me. I ended up ripping the guts out of the middle of his export and replacing it with this, based on a similar solution by Arvin Meyer. Has the advantage of exporting Queries if you are using an mdb instead of an adp.

    ' Writes database componenets to a series of text files
    ' @author  Arvin Meyer
    ' @date    June 02, 1999
    Function DocDatabase(oApp)
        Dim dbs 
        Dim cnt 
        Dim doc 
        Dim i
        Dim prefix
        Dim dctDelete
        Dim docName
    
        Const acQuery = 1
    
        Set dctDelete = CreateObject("Scripting.Dictionary")
    
        Set dbs = oApp.CurrentDb() ' use CurrentDb() to refresh Collections
        Set cnt = dbs.Containers("Forms")
        prefix = oApp.CurrentProject.Path & "\"
        For Each doc In cnt.Documents
            oApp.SaveAsText acForm, doc.Name, prefix & doc.Name & ".frm"
            dctDelete.Add "frm_" & doc.Name, acForm
        Next
    
        Set cnt = dbs.Containers("Reports")
        For Each doc In cnt.Documents
            oApp.SaveAsText acReport, doc.Name, prefix & doc.Name & ".rpt"
            dctDelete.Add "rpt_" & doc.Name, acReport
        Next
    
        Set cnt = dbs.Containers("Scripts")
        For Each doc In cnt.Documents
            oApp.SaveAsText acMacro, doc.Name, prefix & doc.Name & ".vbs"
            dctDelete.Add "vbs_" & doc.Name, acMacro
        Next
    
        Set cnt = dbs.Containers("Modules")
        For Each doc In cnt.Documents
            oApp.SaveAsText acModule, doc.Name, prefix & doc.Name & ".bas"
            dctDelete.Add "bas_" & doc.Name, acModule
        Next
    
        For i = 0 To dbs.QueryDefs.Count - 1
            oApp.SaveAsText acQuery, dbs.QueryDefs(i).Name, prefix & dbs.QueryDefs(i).Name & ".txt"
            dctDelete.Add "qry_" & dbs.QueryDefs(i).Name, acQuery
        Next
    
        WScript.Echo "deleting " & dctDelete.Count & " objects."
        For Each docName In dctDelete
            WScript.Echo "  " & Mid(docName, 5)
            oApp.DoCmd.DeleteObject dctDelete(docName), Mid(docName, 5)
        Next
    
        Set doc = Nothing
        Set cnt = Nothing
        Set dbs = Nothing
        Set dctDelete = Nothing
    
    End Function
    
    0 讨论(0)
  • 2020-11-22 13:13

    You can also connect your MS Access to the Team Foundation Server. There is also a free Express variant for up to 5 developers. Works really well!

    • English guide
    • Team Foundation Server 2012 Express

    Edit: fixed link

    0 讨论(0)
  • 2020-11-22 13:13

    I tried to help contribute to his answer by adding an export option for Queries within the access database. (With ample help from other SO answers)

    Dim def
    Set stream = fso.CreateTextFile(sExportpath & "\" & myName & ".queries.txt")
      For Each def In oApplication.CurrentDb.QueryDefs
    
        WScript.Echo "  Exporting Queries to Text..."
        stream.WriteLine("Name: " & def.Name)
        stream.WriteLine(def.SQL)
        stream.writeline "--------------------------"
        stream.writeline " "
    
      Next
    stream.Close
    

    Haven't be able to work that back into the 'compose' feature, but that's not what I need it to do right now.

    Note: I also added ".txt" to each of the exported file names in decompose.vbs so that the source control would immediately show me the file diffs.

    Hope that helps!


    0 讨论(0)
提交回复
热议问题