I'm working with two Access 2010 databases. One is kept on our company file server and the second one is saved locally on several PC's. I would like to store my VBA code in the network database and use that file as a reference library for the local copies. However, with that configuration, the network file is locked for editing as long as the local copy is open. Using VBA, is it possible to break the link between the two files without closing the local file?
In an attempt to find a workaround, I set up a test environment as follows:
Created two blank Access 2010 database files in C:\DB Test\
Local DB.accdb
Network DB.accdb
Added module LocalCode to Local DB.accdb
Added module RemoteCode to Network DB.accdb
Added a reference to Microsoft Visual Basic for Applications Extensibility 5.3 in Local DB.accdb
Added a reference to C:\DB Test\Network DB.accdb in Local DB.accdb
This reference added Network DB to the projects list of my VBA editor as though the file were open.
Added the following procedure to the LocalCode module in Local DB.accdb
Public Sub ClearDBReference()
Dim DBFile As String
Dim Proj As VBIDE.VBProject
Dim Ref As Access.Reference
DBFile = "C:\DB Test\Network DB.accdb"
For Each Ref In Application.References
If Ref.FullPath = DBFile Then
' Successfully removes the library
' reference to the network database
Application.References.Remove Ref
Exit For
End If
Next
For Each Proj In Application.VBE.VBProjects
If Proj.FileName = DBFile Then
' Run-time error '440': Method 'Remove'
' of object '_VBProjects' failed
Application.VBE.VBProjects.Remove Proj
End If
Next
Set Ref = Nothing
Set Proj = Nothing
End Sub
When I executed ClearDBReference, it successully removed the library reference to C:\DB Test\Network DB.accdb but was unable to remove the project for Network DB. It's as though there were a ghost link between the two files but I'm uncertain what is causing it or what to try next.
Not entirely sure I understand your scenario, but it sounds like you're only referring to the Front-End (FE) app/database?
If so, you can create a stub app for local machines, which just copies the FE to the user's machine each time it's run, opens the real FE app, and then the stub app shuts.
That way the 'master' on the server is never locked. Of course, you wouldn't modify this file, you'd work on a dev copy, then replace the master on the server.
ps, the 'master' should be an accde file.
Is there any reason why both DB's can't sit on the server?
A slightly different scenario, but we always put the Front End and Backend on the server with accdb's - that way there's no issues that arrive from network dropouts. Also improves performance and removes the need to backup files on the local PC.
Related
How can I use VBA to restart Microsoft Access 2007 from within the same DB file..??
I'm developing a DB that will eventually be packaged & distributed with the Runtime. For development I'm changing various UI settings, and renaming the file back & forth between *.accdb and *.accdr. But I would like to use a fully programmatic method, which I would then assign to a button or keystroke.
But as anyone who's tried can tell you, one cannot easily use VBA to restart Access from within the same DB. I tried the code here: MS Access: how to compact current database in VBA, but I received the error message "You cannot compact the open database by running a macro or Visual Basic code."
I have various half-baked ideas how to "bounce" the restart off a VBS script, CMD file, or another Accdb file, but I wanted to ask who else may have done this successfully..??
What variety of ways have others done this with success..??
I had the following procedure to copy new version of frontend and then open. Unfortunately, had to abandon when IT tightened security and can no longer programmatically copy files. Don't ask me why it works, it was found code. Previously, I had VBA call a VBScript which would do the copy and reopen.
Private Sub Form_Load()
'Check for updates to the program on start up - if values don't match then there is a later version
If Me.tbxVersion <> Me.lblVersion.Caption Then
'because administrator opens the master development copy, only run this for non-administrator users
If DLookup("Permissions", "Users", "UserNetworkID='" & Environ("UserName") & "'") <> "admin" Then
'copy Access file
CreateObject("Scripting.FileSystemObject").CopyFile _
gstrBasePath & "Program\Install\MaterialsDatabase.accdb", "c:\", True
'allow enough time for file to completely copy before opening
Dim Start As Double
Start = Timer
While Timer < Start + 3
DoEvents
Wend
'load new version - SysCmd function gets the Access executable file path
'Shell function requires literal quote marks in the target filename string argument, apostrophe delimiters fail, hence the quadrupled quote marks
Shell SysCmd(acSysCmdAccessDir) & "MSAccess.exe " & """" & CurrentProject.FullName & """", vbNormalFocus
'close current file
DoCmd.Quit
End If Else
'tbxVersion available only to administrator to update version number in Updates table
Me.tbxVersion.Visible = False
Call UserLogin End If
End Sub
I use the utility provided here: http://blog.nkadesign.com/2008/ms-access-restarting-the-database-programmatically/
In a nutshell, when you run this, Access will quit. However, before it quits it will create a small batch file in the same folder as the database with a couple of commands in. Access then quits, and the batch file will then wait for the removal of the lock file (laccdb file). Once that's removed (as it should be if you're the only user in the database), the batch file then restarts the application.
There is also an option to compact the database on close, too. It works incredibly well for me in my environment, and I have successfully used this for probably somewhere in the region of 5 years.
I have a frontend Microsoft Access .mde database that connects to two backends simultaneously. I also give the user the option to link to a sandbox mode backend that resides on their local computer. While I'm developing the frontend, I stay linked to my local version of the sandbox so that I don't mess with the two backends. Sometimes, I want to reset my sandbox to match the data in the backends. The sandbox is a little different in that I combine the two backends together into one. (One of the backends is just log data, so I delete that data after it's in my new sandbox.) I have the process of copying the files and data fairly automated through VBA.
I want to also automate setting the permissions on the new sandbox file after it is copied. The sandbox file should have much more lenient permissions since its data really doesn't matter. I keep getting the error "Object not set." after trying to obtain the Tables collection in VBA in the sandbox file from my developer frontend. I have deduced that I am able to see the Containers and corresponding Documents in the frontend developer file, but I am not able to see them from the frontend developer file when I try to access them programmatically.
I'm assuming that this must be some built-in security feature of Access. Perhaps Microsoft doesn't want to allow a file to modify the security of a different file. Here is some of my code to help you see what I'm doing.
All of these databases utilize the same user-level security .mdw file residing on the server. The database was originally created in Access 2000, but I have since upgraded to Access 2010. However, the I maintain the original Access 2000 file format to be able to utilize the built-in multi-user security.
Public Function CorrectSandbox()
Dim appBackend As Application
Dim appLog As Application
Dim appSandbox As Application
Dim tdf As TableDef
Dim i As Long
Dim doc As DAO.Document
Dim ctr As DAO.Container
'copy the files
mFile.DeleteFile LOCAL_SANDBOX_PATH
mFile.CopyFile PRODUCTION_PATH, LOCAL_SANDBOX_PATH
'copy the log tables
Set appSandbox = OpenSandbox()
Set appLog = OpenLog()
For Each tdf In appLog.CurrentDb().TableDefs
If Left$(tdf.Name, 1) = "_" Then
appSandbox.DoCmd.TransferDatabase acImport, "Microsoft Access", GetAdminPath(eLive), acTable, tdf.Name, tdf.Name, True
'Set permissions for the object accordingly
Set ctr = appSandbox.CurrentDb().Containers!Tables
Set doc = ctr.Documents(tdf.Name) '*** ERROR: Object not set
doc.UserName = "User"
doc.Permissions = 852222
End If
Next tdf
appLog.Quit
appSandbox.Quit
End Function
I'm trying to input some code that hides "Design View" as an option for our internal application unless a certain permission requirement is met.
The following code works with one exception:
On Error Resume Next
If Not GetUserInfo("ADMIN_PERMIS") = 1 Then
Dim cb As CommandBar
Dim cbCtl As CommandBarControl
For Each cb In CommandBars
If cb.type = msoBarTypePopup Then
For Each cbCtl In cb.Controls
If cbCtl.Caption = "&Design View" Then
cbCtl.enabled = True
cbCtl.visible = False
Else
cbCtl.visible = True
End If
Next
End If
Next
Set cb = Nothing: Set cbCtl = Nothing
End If
The one problem with this is that it disables Design View not only for the current database, but also for any other access database that is launched. I'm looking for a way to try and apply this code in such a way that it only affects the Access database I have the code in and not in every single instance of it.
I recommend converting the database into a compiled, executable only .accde file (File --> Save & Publish --> Make ACCDE). Doing this will prevent any design or code changes in the application. Keep a development version in the normal .accdb format. Make your changes there and then compile into the .accde version for each update.
Since your team updates the database often, you could benefit from using Peter De Baets' database starter. The database starter makes a local copy of the front end of the database, allowing uses to continue to work while design changes are being made. After the production accde front end file is updated, the users will automatically copy the new file the next time they open the database. In my office I have found that I can push out a quick fix and simply email everyone saying "Close and reopen the database guys!".
All these answers are great. If you are interested in the simplest method, I found the form holds the key, albeit in a strange spot.
In the forms properties->Other Tab->Shortcut Menu =No
I'm having the following issue. I've got a .mde that needs to use another .mde for some functions. That referenced .mde has to be on a unique location for every user. I thought I could drop it on our P:\ drive (which exists for every user but leads to a different folder). The only issue I got there access saves the UNC path and which gets translated to \\[server]\[user]\mylib.mde instead of P:\mylib.mde
So I thought I would use this guide:
http://smsconsulting.spb.ru/shamil_s/topics/testrefs.htm
When I use this it works. Except that I can't close Access without crashing Access.
Some more detailed information about our environment. We're running on Win7 with office 2010 except for Access, which is Access 2003.
Anyone got an idea why this does not work?
EDIT: After some extra testing I've noticed that I Access does not crash when the reference .mde is located on my local hard drive, it only crashes when it's on a network drive.
I have not tested with adding to an an mde only to an mdb, but it may be worth trying this:
Dim ref As Reference
On Error Resume Next
References.AddFromFile "C:\docs\Some.mde"
''Just checking
For Each ref In References
s = s & vbCrLf & "Name: " & ref.Name
Next
I'm trying to open a series of Excel spreadsheets using an instance of Excel created inside of a module in an Access database. I can get the files to open properly; however, the actual call to make Excel start takes quite a while, and to open the files takes even longer. The location of the files doesn't matter (same time to open on a local HDD as a network drive).
In an attempt to figure out what was taking so long, I added a timer to the logging module. Opening the files takes approximately 2m30s, during which the host application (Access) is entirely unresponsive to user input); the rest of the script executes in less than 10 seconds.
I'm using the standard Excel.Workbooks.Open call as follows
Set OpenSpreadsheet = Excel.Workbooks.Open(Name, 2, False)
Using Debug.Print methods around this line says it can take up to 2 1/2 minutes for this one line to execute.
Is there anything I can do to make the Excel files open quicker?
EDIT: When opening, UpdateLinks is False and ReadOnly is True; all other options are left to their defaults.
First idea: Can you use a jet driver with an ODBC connection to Excel, instead of opening it in an Excel object? Might be much faster.
Second idea: Make sure to create and instantiate the Excel application object just once at the beginning of the routine, then use the Excel.Workbooks.Open() and Excel.ActiveWorkbook.Close() for each spreadsheet. That way you're not "re-launching" the MS Excel application each time.
To draw out the second of #BradC's well-advised recommendations, if you need to use Excel in more than one procedure, create a self-initializing global function. I always use late binding for automating Office apps.
Public Function Excel(Optional bolCleanup As Boolean = False) As Object
Static objExcel As Object
If bolCleanup Then
If Not objExcel Is Nothing Then
Set objExcel = Nothing
Exit Function
End If
End If
If objExcel Is Nothing Then
Set objExcel = CreateObject("Excel.Application")
End If
Set Excel = objExcel
End Function
Then you can use it in code without needing to initialize it, and the single instance will remain available to any code that needs to use Excel.
And when you shut down your app, you'd call Excel(True) to clean up.
I do this with Outlook and Word all the time. However, there are some COM apps that it works poorly with, such as PDF Creator, which doesn't take kindly to this kind of treatment (you end up in an endless loop with it shutting down and re-initializing itself if you try to destroy the instance this way).
Another approach depends on your setup and process.
In my case I need read-only access to a set of Excel documents stored on SharePoint. My code is currently like:
For each path in paths
set wb = Workbooks.open(path,false)
next
In this case, each workbook is individually downloaded each time a workbook is opened. It would be significantly more efficient if the files were downloaded asyncronously and after the download is complete, the rest of the process executes on the local disk.
My idea is to use CopyFileEx() and pass a callback function. Excel will then download the Excel documents to disk asynchronously and call a VBA function regarding progress. When all files are completed, we can launch the next part of the process, which opens the local workbooks, scans them and then removes them from the local drive.
I'll post code later if I manage to implement it.