How do I create copies of CurrentDb using VBA - ms-access

I need to create copies of the CurrentDB using VBA (approx. 12 copies). The copies need to be clones of the master database containing all the same forms, queries, etc. except only a limited dataset.
DoCmd.CopyDatabaseFile seems to be made for this, but only works if you are using it to copy the DB to an MS SQL Server. As MS states on their website:
Copies the database connected to the current project to a Microsoft
SQL Server database file for export.
docmd.TransferDatabase only exports the data itself, but not the structure, forms, etc.
Code I have found on the web and adapted doesn't work and throws an error on the .CopyFile line saying:
Run-time error 52: Bad file name or number
This is the code
Sub CopyDB()
Dim external_db As Object
Dim sDBsource As String
Dim sDBdest As String
sDBsource = "\\group\bsc\groups\L\BDTP\Compliance\ComplianceData\Compliance Group Reporting.accdb"
sDBdest = "\\group\bsc\groups\L\BDTP\Compliance\ComplianceData\Compliance Group Reporting_2.accdb"""
Set external_db = CreateObject("Scripting.FileSystemObject")
external_db.CopyFile sDBsource, sDBdest, True
Set external_db = Nothing
End Sub
How can I fix the above line? Alternatively is there a direct command in Access to create a copy? The "create backup" function would be tailor made for this, but I can not find it in VBA.

Looks like you have an extra quote in sDBdest accdb"""
And for database copy you can also use
FileCopy sDBsource, sDBdest
Instead of Scripting object

Related

Need to Change Linked Tables in MS Access DB with Username, Password and System DB

I have a front end DB, which needs to link to different back end DBs. To give you perspective, it relates to stand alone MDB files. The software in question builds a DB per company.
At the moment I am writing code within one of these MDB files.
For scalability I am now creating a new DB which will link to each MDB via code, and therefore my questions are as follows
How do I change the linked table location via code / VB so that the user can select the company / DB they want to work on
How do I do this with passing a username and password which is the same for all of the companies / DBs
And as per below we need to verify the username and password via the systemDB for it to open successfully.
As an FYI, this is how we open the DB on a standalone basis-
"C:\Program Files (x86)\Microsoft Office\root\Office16\MSACCESS.EXE" "C:\temp\SAMPLE.mdb" /WRKGRP "C:\ProgramData\SOFTWARE\SYSTEM.mdw" /user:username /pwd:password
This is not a problem at all and is absolutely able to be accomplished given the clarification that you are using a single MDW file.
To clarify Microsoft Access Workgroup Security is essentially a "session" security model that applies directly to the front-end MDB file as you open it.
Your example command line means that Microsoft Access will open the SAMPLE.MDB front-end file using the workgroup file you specified.
Once Microsoft Access has opened SAMPLE.MDB under that workgroup file, you cannot change to another workgroup file within that "session" without closing Microsoft Access and re-opening under the new workgroup file.
FYI - it IS possible to open via code, a table in another MDB using another workgroup file within that connection, but in this manner, the table is only usable in code as a RecordSet (for example), you can't make it a linked table.
Anyway, back to your real issue. How to link a different back-end set of tables for each Company.
My recommendation would be to add a few fields to your Company table that defines the filename and location of each back-end file. For example:
Notice that the location can be a UNC path, or a mapped drive path. Or maybe you don't need to define a location in the table explicitly. Maybe all of the back-ends are in the same folder, or in a definable dynamic location like \Dallas\Dallas.mdb, \NewYork\NewYork.mdb, etc. As long as you can determine the location of each back-end in some manner, then you are fine.
Now, since you will likely have "global" front-end tables, maybe also some "global" linked back-end tables i.e. Common.mdb, and your company-specific back-end tables, I would recommend having a front-end table that defines the name of each of the tables that is involved only in the company-specific files. That way, we can easily loop through just those table names and make the link changes.
For the linking code, let's say that you have prompted the User for which Company they want, and you pass the CompanyID to a re-linking function:
Public Function ChangeCompanyLinks(CompanyID As Long) As Boolean
Dim db As DAO.Database
Dim ldb As DAO.Database
Dim tdf As DAO.TableDef
Dim rstCompany As DAO.Recordset
Dim rstTables As DAO.Recordset
Dim mssql As String
Dim dbFullPath As String
Dim retVal As Boolean
Set db = CurrentDb()
retVal = False
mssql = "SELECT * FROM [tblCompany] WHERE [CompanyID] = " & CompanyID
Set rstCompany = db.OpenRecordset(mssql, dbOpenSnapshot)
If Not rstCompany.BOF Then
dbFullPath = rstCompany("DBLocation") & "\" & rstCompany("DBName")
If Dir(dbFullPath) = rstCompany("DBName") Then
'NOTE: By opening a temporary constant link to the back-end during
' relinking, the relinking runs faster
Set ldb = OpenDatabase(dbFullPath)
mssql = "SELECT * FROM [tblLinkedTables] WHERE [FileType] = ""Company"""
Set rstTables = db.OpenRecordset(mssql, dbOpenSnapshot)
Do While Not rstTables.EOF
Set tdf = db.TableDefs(rstTables("TableName"))
tdf.Connect = ";DATABASE=" & dbFullPath
tdf.RefreshLink
rstTables.MoveNext
Loop
rstTables.Close
ldb.Close
retVal = True
Else
MsgBox "Unable to Locate Company File"
End If
End If
rstCompany.Close
ChangeCompanyLinks = retVal
Set rstCompany = Nothing
Set rstTables = Nothing
Set ldb = Nothing
Set tdf = Nothing
db.Close
Set db = Nothing
End Function
Obviously you will want to add error handling, and customize this a bit to fit your situation, but this code will re-link the specified tables to a new back-end.
Please note that if you eventually change to have your back-end tables in SQL Server (which I highly recommend), the re-linking code would need to be modified a bit. See this answer for more details on that.

Why does my database object show no Recordsets?

Why does my database object show no Recordsets? And why does it not show a Connection? The key bit of code is:
Public Sub PrintRecords()
Dim dbCurr As DAO.Database
Set dbCurr = CurrentDb
Dim rsCourses as Recordset
Set rsCourses = CurrentDb.OpenRecordset(“Courses”)
End Sub
Here is my screenshot, where the database has tables (upper left), but the CurrentDB object has a name but no Recordsets (in the locals window below.)
For what it’s worth, this is in Access 2013, following this tutorial to manipulate the database, stopping on the error “Run-time error ‘3078’: The Microsoft Access database engine cannot find the input table or query”. But the problem seems to be deeper than that.
You have typographic quotes in there, use straight ones "".
And use your assigned database object:
Set rsCourses = dbCurr.OpenRecordset("Courses")
And please don't post only screenshots of code, copy & paste the code itself as text.

VB does not see Table after DoCmd.TransferDatabase using OpenRecordset

I am using Do.CMD.TransferDatabase to copy in two tables from a second access database. The TransferDatabase command runs, and I see the table in the Access list of tables, but the VB code doesn't see it.
I suspect I've messed up the internal list of tables because I see the tables (
myTable
myTable1
myTable2)
If I leave a table there on purpose, Access seems to "see" the table enough to not over-write it and Access writes a copy, but then the same code does not see the existing table to openit.
Code snippets (sanitized):
{
Dim ws As Workspace
Dim myDB As Database
Set myDB = DBEngine.Workspaces(0).Databases(0)
DoCmd.TransferDatabase acImport, "Microsoft Access", filename, acTable,"myTable", "myTable", False
Dim OriginalInformation As Recordset
Set OriginalInformation = myDB.TableDefs("myTable").OpenRecordset
The code stops on the last line and give the error that the item is not found in the collection.
How do I copy in a table and then look at the contents within the same VB function?
The answer is to use myDB.TableDerrs.Refresh between the import and using the
myDB.TableDefs("myTable").OpenRecordset

VB6- Open Access DB or one of it's forms with ADO object

My application written with visual basic 6 and it has an Access database. I want to add a VB form and open the database in this form to make db edits the DB in that. I have this code for oppenning:
Dim db As DAO.Database
Set db = DBEngine.workspaces(0).opendatabase("c:\ss.mdb")
I have a form inside that database. This form makes the data insertion process faster. I want to open this access form with my application.
How i should do this??
Note: I have this code that uses Microsoft access 14 object library.
Dim appaccess As Access.Application, dbstr As String
On Error Resume Next
Set appaccess = New Access.Application
Set appaccess = CreateObject("Access.Application")
dbstr = "c:\ss.mdb"
'Or dbstr="c:\my documents\yourfile.mdb
'put the correct path here.
appaccess.OpenCurrentDatabase dbstr
appaccess.DoCmd.OpenForm "aa", acLayout
appaccess.Visible = True
But when i run this code the form appears and after a while it gets disapear. besides using access object library creates some access version conflicts. So, Although it is not necessary but i prefer to do that with ADO object. Anyway, i' looking for e method to solve my problem.
Thank you for your help
ADO provides methods to interact with various data sources. Although you can use it for Access db files, it doesn't provide methods to utilize Access forms.
With your current approach, you use CreateObject to set the object variable appaccess to a new Access application instance, then open your form within that instance. However, when the variable goes out of scope, the Access instance closes and the form disappears.
You might revise your VB6 code to keep the variable in scope until you're finished with the Access instance. Unfortunately, I don't know how to fit that change into the rest of your code, and I've never used VB6.
Alternatively, you could use the Shell Function to start the Access instance, then use GetObject() to set your object variable to that instance.
With the Shell() approach, you would need to supply the full path to MSACCESS.EXE. You can find the folder where it's located by reading the registry. Here's a VBScript sample which does that, and I'm hoping you can adapt it easily for VB6.
Option Explicit
Dim MSAccFolder
Dim RegKey
Dim WSHShell
RegKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\" _
& "CurrentVersion\App Paths\MSACCESS.EXE\Path"
Set WSHShell = WScript.CreateObject("WScript.Shell")
MSAccFolder= WSHShell.RegRead(RegKey)
WScript.Echo "MS Access Folder: " & MSAccFolder
Set WSHShell = Nothing
One thing is that you're using DAO in that first chunk of code rather than ADO.
Maybe that doesn't matter though because there's no reference to db in your second chunk of code.
So the first chunk of code looks to be unecessary.
I don't know why you have 2 set statements for appaccess, you don't need the second one.
Also I'd suggest that you comment out the 'on error resume next' statement so you can see which line is causing the error while debugging.
If you're going to use VB, you should abandon the Access Automation stuff and create your form in VB. Then use ADO to open and work with the Access file. This is much more efficient than using VB to automatically open an Access form.

Is there a way to execute VBA code when specific tables and fields in a Microsoft Access database are updated?

I have a program that uses a Microsoft Access database for its back-end. I need to have some VBA code (that calls a web service) execute whenever specific tables/fields are updated by the program. I see this working just like a trigger in SQL Server.
Is it possible to monitor for and act upon changes like this in Access?
Update
The program in question does not run inside of Access (i.e. not a VBA app), it simply uses an MDB file as its back-end storage. Unfortunately I don't have access to the program's code as it is a closed third party application.
This question is old, but the answers are no longer correct. Access 2010 added data macro events that can be run when data is inserted, updated or deleted. The following events are available while using either the table datasheet view or table design view (events are attached directly to table and not through the form macro button):
After Delete Macro Event
After Insert Macro Event
After Update Macro Event
Before Change Macro Event
Before Delete Macro Event
More information is located here:
https://msdn.microsoft.com/en-us/library/office/dn124692.aspx
https://support.office.com/en-us/article/Create-a-data-macro-b1b94bca-4f17-47ad-a66d-f296ef834200
Access the GUI environment vs Jet the database format are separate things.
If you are using an Access database as a backend - it's just the JET functionality you can work with. Access the GUI (which includes VBA) runs on the client machine and there is no automated trigger functionality.
If your program is the only program using the Access file, then it should know when a table is being updated and execute some code in place of a trigger.
Otherwise, you need another application/service running all the time that is checking the access file tables for updates (maybe you have some update_date type of field on your tables?).
When an Access database file gets written to, it's date/time stamp changes. I suppose you could try using a file monitor to detect changes to the file, and then examine the file to see what has changed.
It would help if the Access database has LastModified date/time columns in the tables.
If you are using Jet (i.e. the data is stored in an MDB file back end) then the only places you can run code would be in the After Update Event in a Form. The problem here of course is if the data is changed without using the form then the event will not fire.
If you are using MS Access 2003 then to run a Web Service you can download the Microsoft Office 2003 Web Services Toolkit Click Here to download
If you are stuck in VBA it gets a little rough. One way to go would be to have a form with timer in it (you could have it open invisibly. The timer could check the table, say once a minute (or whatever interval seems suitable) for changes in record count, and verify the table still exists. (code below)
But personally this isn't what I would recommend that you do. Access is notorious for corruption. When used as a simple back end you are fairly safe most of the time, but to have it running a monitor, means the file is always open. This is basically playing Russian Roulette with your database. At minimum I would link to your database from another Access file and monitor the linked tables, that way if your monitor crashes, you don't take the production DB with you. Finally, make sure that you don't query too often, as I'd hate to see you be the sole cause of the website timing out:)
Option Explicit
Private m_lngLstRcrdCnt_c As Long
Private Sub Form_Open(Cancel As Integer)
Const lngOneMinute_c As Long = 60000
Me.TimerInterval = lngOneMinute_c
End Sub
Private Sub Form_Timer()
Const strTblName_c As String = "Foo"
Const strKey_c As String = "MyField1"
Dim rs As DAO.Recordset
Dim lngRcrdCnt As Long
If TableExists(strTblName_c) Then
Set rs = CurrentDb.OpenRecordset("SELECT Count(" & strKey_c & ") FROM " & strTblName_c & ";", dbOpenSnapshot)
If Not rs.EOF Then lngRcrdCnt = Nz(rs.Fields(0&).Value, 0&)
rs.Close
If lngRcrdCnt <> m_lngLstRcrdCnt_c Then
m_lngLstRcrdCnt_c = lngRcrdCnt
'Number of records changed, do something.
End If
Else
'Table is deleted, do something.
m_lngLstRcrdCnt_c = -1
End If
End Sub
Private Function TableExists(ByVal name As String) As Boolean
Dim tdf As DAO.TableDef
On Error Resume Next
Set tdf = CurrentDb.TableDefs(name)
If LenB(tdf.name) Then 'Cheap way to catch broken links.
Set SafeGetTable = tdf
End If
End Function