我参与了更新Access解决方案.它具有大量的VBA,大量查询,少量表格以及一些用于数据输入和报告生成的表单.它是Access的理想候选者.
我想对表设计,VBA,查询和表单进行更改.如何通过版本控制跟踪我的更改?(我们使用Subversion,但这适用于任何风格)我可以将整个mdb粘贴在subversion中,但这将存储二进制文件,我将无法告诉我只更改了一行VBA代码.
我想过将VBA代码复制到单独的文件中并保存它们,但我可以看到它们很快就与数据库中的内容不同步了.
我们在VBScript中编写了自己的脚本,它使用Access中未记录的Application.SaveAsText()来导出所有代码,表单,宏和报表模块.在这里,它应该给你一些指示.(注意:有些消息是德语的,但你可以很容易地改变它.)
编辑:总结以下各种评论:
我们的项目假设一个.adp文件.为了使用.mdb/.accdb工作,您必须将OpenAccessProject()更改为OpenCurrentDatabase().(OpenAccessProject()
如果它看到.adp扩展名,则更新使用,否则使用OpenCurrentDatabase()
.)
decompose.vbs:
' Usage:
' CScript decompose.vbs
' Converts all modules, classes, forms and macros from an Access Project file (.adp) to
' text and saves the results in separate files to . 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
如果需要可单击的命令,而不是使用命令行,请创建名为"decompose.cmd"的文件
cscript decompose.vbs youraccessapplication.adp
默认情况下,所有导出的文件都会进入Access-application的"Scripts"子文件夹..adp/mdb文件也会复制到此位置(带有"stub"后缀)并删除所有导出的模块,使其非常小.
您必须使用源文件签入此存根,因为大多数访问设置和自定义菜单栏无法以任何其他方式导出.如果您确实更改了某些设置或菜单,请确保仅对此文件提交更改.
注意:如果您的应用程序中定义了任何Autoexec-Makros,则在调用分解时可能必须按住Shift键以防止它执行并干扰导出!
当然,还有反向脚本,从"源" - 目录构建应用程序:
compose.vbs:
' Usage:
' WScript compose.vbs
' 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
同样,这与伴侣"compose.cmd"一起包含:
cscript compose.vbs youraccessapplication.adp
如果您这样做,它会要求您确认覆盖当前的应用程序并首先创建备份.然后,它会收集源目录中的所有源文件,并将它们重新插入到存根中.
玩得开心!
它似乎是Access中非常有用的东西:
msdn的此链接说明了如何为Microsoft Access安装源代码管理加载项.它作为Access Developer Extensions for Access 2007的一部分免费下载,并作为Access 2003的单独免费插件提供.
我很高兴你问这个问题,我花时间去查阅,因为我也想要这个能力.上面的链接提供了有关此内容的更多信息以及指向加载项的链接.
更新:
我安装了Access 2003的加载项.它只能用于VSS,但它允许我将Access对象(表单,查询,表,模块等)放入存储库.当您编辑回购中的任何项目时,系统会要求您检查它,但您不必这样做.接下来,我将检查它如何处理在没有加载项的系统上打开和更改.我不是VSS的粉丝,但我确实喜欢在存储库中存储访问对象的想法.
Update2:
没有加载项的计算机无法对数据库结构进行任何更改(添加表字段,查询参数等).起初我认为如果有人需要这可能是一个问题,因为如果Access没有加载加载项,没有明显的方法从源代码控制中删除Access数据库.
我发现运行"compact and repair"数据库会提示您是否要从源代码管理中删除数据库.我选择了是,并且能够在没有加载项的情况下编辑数据库.上面链接中的文章还说明了如何设置Access 2003和2007以使用Team System.如果您能找到SVN的MSSCCI提供程序,那么很有可能您可以使用它.
Olivers回答岩石,但CurrentProject
参考不适合我.基于Arvin Meyer的类似解决方案,我最终从他的出口中间撕下了胆量并用它替换了它.如果您使用的是mdb而不是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
Oliver发布的撰写/分解解决方案很棒,但它存在一些问题:
这些文件被编码为UCS-2(UTF-16),这可能导致版本控制系统/工具将文件视为二进制文件.
这些文件包含许多经常更改的错误 - 校验和,打印机信息等.如果您想要干净的差异或需要在项目上合作,这是一个严重的问题.
我打算自己解决这个问题,但发现已经有一个很好的解决方案:在GitHub上进行timabell/msaccess-vcs-integration.我已经测试了msaccess-vcs-integration,它确实很有效.
2015年3月3日更新:该项目最初由bkidwell在Github上维护/拥有,但它被转移到timabell - 上面链接到项目相应更新.bkidwell的原始项目中有一些分叉,例如ArminBra和matonb,不应使用AFAICT.
与Olivers的分解解决方案相比,使用msaccess-vcs-integration的缺点是:
它明显变慢了.我确定速度问题可以修复,但我不需要将我的项目导出到经常出现的文本...
它不会创建存根Access项目,并删除导出的东西.这也可以修复(通过采用分解脚本中的代码),但同样重要 - 不那么重要.
无论如何,我的明确建议是msaccess-vcs-integration.它解决了我在导出文件上使用Git时遇到的所有问题.
我们开发了自己的内部工具,其中:
模块:导出为txt文件,然后与"文件比较工具"(免费软件)进行比较
表单:通过undocument application.saveAsText命令导出.然后可以看到2个不同版本之间的差异(再次"文件比较工具").
宏:我们没有任何宏可以比较,因为我们只有"autoexec"宏,一行启动主VBA程序
查询:只是存储在表中的文本字符串:见下文
tables:我们编写了自己的表比较器,列出了记录和表结构的差异.
整个系统非常智能,允许我们生成Access应用程序的"运行时"版本,这些版本是从txt文件(模块和使用undocument application.loadFromText命令重新创建的表单)和mdb文件(表)自动生成的.
听起来可能很奇怪,但它确实有效.
根据这篇文章的想法和一些博客中的类似条目,我写了一个适用于mdb和adp文件格式的应用程序.它将所有数据库对象(包括表,引用,关系和数据库属性)导入/导出到纯文本文件.使用这些文件,您可以使用任何源版本控件.下一版本将允许将纯文本文件导回数据库.还将有一个命令行工具
您可以从以下网址下载应用程序或源代码:http://accesssvn.codeplex.com/
问候
复活一个旧线程,但这是一个很好的.我已经为我自己的项目实现了两个脚本(compose.vbs/decompose.vbs),并遇到了旧的.mdb文件的问题:
当它到达包含代码的表单时,它会停止:
NoSaveCTIWhenDisabled =1
Access说它有一个问题,这就是故事的结局.我运行了一些测试并试图解决这个问题并发现这个线程在最后解决了:
无法创建数据库
基本上(如果线程死机),你获取.mdb并对新的.accdb格式执行"另存为".然后源安全或撰写/分解的东西将起作用.我还必须玩10分钟才能获得正确的命令行语法(de)compose脚本才能正常工作,所以这里的信息也是如此:
编写(比如你的东西位于C:\ SControl中(创建一个名为Source的子文件夹来存储提取的文件):
'(to extract for importing to source control) cscript compose.vbs database.accdb '(to rebuild from extracted files saved from an earlier date) cscript decompose.vbs database.accdb C:\SControl\Source\
而已!
我遇到上述问题的Access版本包括Access 2000-2003".mdb"数据库,并通过在运行compose/decompose脚本之前将它们保存为2007-2010".accdb"格式来解决问题.转换后,脚本工作得很好!