I am trying to script access to an Access database for use on the command line. The Access database is secured with a workgroup file.
Dim oApp, sWGF,myWS
Set sApp = CreateObject("Access.Application")
set sWGF = "C:\Users\Alan\Documents\Secured.mdw"
oApp.DBEngine.SystemDb = sWGF
WScript.echo "Workgroup " & sWGF
WScript.echo "SystemDb " & oApp.DBEngine.SystemDb
Set myWS = oApp.DBEngine.CreateWorkspace("New","Name","Password")
This code outputs the Secured.mdw filename for the workgroup, but the default System.mdw filename for the SystemDB as the output from the two WScript.echo commands. It also fails to create the workspace saying the Name and Password are wrong (although they ARE correct for the Secured.mdw file)
There are lots of references elsewhere on the net that say you can only do this as the first thing inside an application, but that IS what I am doing.
I am not sure what I am doing wrong. Any ideas
I think you should be using DAO and Jet directly, instead of automating Access.
Dim objEngine
Dim strWorgroup
dim wrkWorkspace
Dim db
Set objEngine = CreateObject("DAO.DBEngine.36")
objEngine.SystemDB = "C:\Users\Alan\Documents\Secured.mdw"
Set wrkWorkspace = objEngine.CreateWorkspace("", "Name", "Password")
Set db = wrkWorkspace.OpenDatabase("C:\MyDatabase.mdb")
This would bypass Access itself and use the Jet database engine directly, which seems simpler to me.
In testing this, I had some difficulty setting the SystemDB as well, but it turned out I just had to make sure I was providing a VALID one for use. The first non-default one I tried didn't work, but when I made a copy of the default one and used that, it worked.
So, I'd look to see if you've got the correct filename/path for your workgroup file, if it's the right version of Jet, and if you've got the appropriate NTFS permissions to open it.
Are sApp and oApp the same thing? You're mixing up variables.
A late addition to "I had some difficulty setting the SystemDB as well":
This worked for me:
Set objEngine = CreateObject("DAO.PrivateDBEngine.36")
Related
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.
I've been asked to do some maintenance on an Access 2007 VBA database.
It has linked tables to another Access database in the same folder. It had hard-coded links to that database, so if the user copied the folder to a new folder, it tried to use the linked database in the original folder. They asked me to eliminate the danger of using the wrong linked database in that scenario.
I added code that runs when the database is opened, to make it reset the links to the database in it's own folder. If the linked database isn't there or was renamed, the user is prompted to browse to the correct database. So far so good.
But if the user cancels that dialog, I don't want to leave it connected to the wrong database. I want to set the linked tabledef's Connect property to the "correct" path even though the table is not there. Then the user will get an error that the linked table isn't there until they copy in the linked database -- rather than inadvertently use the wrong database.
When I use Resume Next to get past the error that is raised when I set the Connect property to a nonexistent database, the change doesn't stick, leaving it connected to the wrong database. So for now, I'm closing the database when that happens (after alerting the user that the linked database can't be found). That's safe in terms of not using the wrong database, but I don't think it's the ideal user experience.
So -- is it possible to set the Tabledef's Connect property to a nonexistent database?
Thanks,
Greg
is it possible to set the Tabledef's Connect property to a nonexistent database?
I don't think so. I recommend you delete all of the linked tables first, and then if there's no database to connect to, show an error saying so.
In order to relink the tables you will then need to have a local table in your frontend file that holds a list of all tables to be linked. Then you'll need to loop through that list every time you need to relink the tables, assuming that the links have all been deleted.
In order to update table links to render them invalid you need to
create a temporary copy of the back-end database (or perhaps a temporary copy of a "skeleton" back-end database with empty tables),
update the table links to point to the temporary database, and then
delete the temporary database.
For example, if I run the following code
Option Compare Database
Option Explicit
Sub MakeBadLink()
Const linkedTableName = "myLinkedTable" ' test value
Dim cdb As DAO.Database, tbd As DAO.TableDef
Dim tempFolder As String, linkedDb As String, tempDb As String
Dim fso As FileSystemObject
Set cdb = CurrentDb
linkedDb = Mid(cdb.TableDefs(linkedTableName).Connect, 11) ' remove ";DATABASE=" prefix
Set fso = New FileSystemObject
tempFolder = fso.GetSpecialFolder(TemporaryFolder) & "\"
tempDb = tempFolder & fso.GetFileName(linkedDb)
fso.CopyFile linkedDb, tempDb, True
Set tbd = cdb.TableDefs(linkedTableName)
tbd.Connect = ";DATABASE=" & tempDb
tbd.RefreshLink
Set tbd = Nothing
Set cdb = Nothing
fso.DeleteFile tempDb
Set fso = Nothing
End Sub
then any subsequent attempts to use [myLinkedTable] result in the error
Could not find file `C:\Users\Gord\AppData\Local\Temp\myDb.accdb'.
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.
I've got a quandry. I've developed an Access app and I'm getting ready to distribute it. I've just split the database. (I know, some say I should've developed it split from the start... I didn't) I've also just encrypted the backend database. In the frontend, I've linked to the backend and entered the correct password when prompted. The linked tables are now appearing in my fronend database. However, when I try to access one of the linked tables, I get a pop-up message that simply says "Not a valid password".
I've tried deleting the linked tables and relinking. I've tried updating the link. Nothing seems to work. Every search I've done assumes the links were created BEFORE the encryption happened and no password was entered. This is not the case here.
Can anyone please help?
Windows 7 - Access 2010
Multiguy
From comments
OOOOOPPPPPSSSS!!!! Ok, I found the problem. Access doesn't like the
use of other characters. I had a set of parenthesis in my password.
Removed that and all is well! :-)
– MultiGuy
I'd like to add to this; a password for a back end table connection can be updated in a VBA macro. Add the following lines and replace the name for the linked table & the password.
Dim cdb As DAO.Database
Set cdb = CurrentDb
cdb.TableDefs("Projects").Connect = "MS Access;PWD=PaSsWoRd;DATABASE=C:\Users\bob.smith\Desktop\Split\Database_NEW_be.accdb"
cdb.TableDefs("Projects").RefreshLink
I found this was useful after splitting a database into a front end and back end using the built in split functionality. I needed to encrypt the back end for security purposes and didn't want to have to recreate all the data connections - isn't that the point of using the split function?
Duplicate the above lines for each linked table and let the macro run through. The linked tables should resume working after this.
This answer solved an issue for me. So I am upvoting, and also I want to provide an enhanced version that you can run on all tables:
Public Sub RevisePasswordForLink()
Dim cdb As DAO.Database
Set cdb = CurrentDb
Dim tdf As TableDef, colTdf As TableDefs, strConnect As String
Set colTdf = cdb.TableDefs
strConnect = "MS Access;PWD=paSsWoRd;" _
"DATABASE=C:\Users\bob.smith\Desktop\Split\Database_NEW_be.accdb"
For Each tdf In cdb.TableDefs
''I believe best to skip the hidden tables ("MSys*")
If Not tdf.Name Like "MSys*" Then
''If your DB has any local tables, you can save yourself some errors
''by filtering them out (similar to hidden tables).
cdb.TableDefs(tdf.Name).Connect = strConnect
cdb.TableDefs(tdf.Name).RefreshLink
Debug.Print " " & tdf.Name
End If
Next tdf
Set cdb = Nothing
Debug.Print "FINISHED "
End Sub
I came across this thread when I was having a similar problem with Access 2013. I'd encrypted the backend successfully. Each time I opened the backend, I could successfully open the backend using the password I'd used to encrypt the backend.
So, I opened the frontend, deleted the previously linked tables, and re-linked to the newly encrypted backend. As expected, during the link process, it asked me for the password to the backend. I entered the correct password (I know it was correct, because I pasted it instead of typing it) and everything seemed to work correctly. The tables all showed up in the objects list in the frontend. However, if I tried to open a linked table, I would get a message box stating "Not a valid password."
After visiting some other threads, I tried using a password on the backend that did not have any special characters and did not have any spaces. Then relinking those in the frontend. Presto! That solved the problem.
So, try the following if you are getting the "Not a valid password" in the frontend even if you can open the backend just fine if you manually load it yourself: Eliminate any spaces or punctuation from the backend password and then delete and re-link the linked tables in the frontend.
I'm writing an MS Outlook (2003) macro which uses an ADO Connection to an Access DB (2003). I am opening a connection, getting some records into a Recordset, which I use to populate a grid (but not bind to). I then close the Recordset and Connection and set both to Nothing.
The process creates an instance of MSACCESS.EXE, and a .ldb file for the Access DB, both of which remain after I have closed the Connection, Recordset, Macro and Outlook itself. One or both of these remnants is preventing opening the Access DB until the MSACCESS.EXE process is manually killed and the .ldb file is deleted.
Similar posts say "close the connection" but that is not solving the problem.
Here's the VBA code:
Screen.MousePointer = vbHourglass
Set db = New ADODB.Connection 'Declared at module level
Set rs = New ADODB.Recordset 'Declared at module level
Dim sSQL As String
sSQL = "SELECT Customers.ContactFirstName As Name, Customers.ContactLastName As Surname, Customers.EmailName AS Email, Customers.Address, Customers.Area, Customers.Town FROM qryCustomersWithEmail ORDER BY Customers.ContactLastName ASC"
db.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & "C:\My Documents\Tables.mdb;Persist Security Info=False"
rs.Open sSQL, db, adOpenStatic, adLockReadOnly
If rs.RecordCount > 0 Then
'actions performed on recordset removed
End If
rs.Close
db.Close
Set rs = Nothing
Set db = Nothing
Screen.MousePointer = vbDefault
It's on Vista.
I'm doing this to provide my customer with an easy way to send bulk emails to everyone in his Access database. I tried by accessing Outlook from Access but the security "feature" of Outlook, which pops up a warning message for every email created, scuppered this approach.
UPDATE.
I removed PopulateFlexGrid and the problem still occurred just for opening and closing the recordset. What I previously omitted from this sample, as I considered it irrelevant is that I wrapped the code with Screen.MousePointer = vbHourglass at the start and Screen.MousePointer = vbDefault at the end. I removed this and the problem no longer occurs. Stepping through I see MSACCESS.EXE start up in TM when I call Screen.MousePointer = vbHourglass.
I also tried a version where I used DAO instead of ADO, no other difference, and it works without creating .ldb or starting up an MSACCESS.exe. This works with the Screen.Mousepointer code in there.
Can anybody explain this?
The answer to this is that the reference to
Screen.MousePointer
which i'd omitted as irrelevant, is actually a member of Access. Hence when i call it MSACCESS starts up.
I should have used
Me.MousePointer = fmMousePointerHourGlass
So it's my fault for copying some code from Access VBA to Outlook VBA and expecting it to work the same. Apologies to all of you who spent time looking at this!
What is the code of the function PopulateFlexGrid? Is it possible this is not closing the recordset passed to it?
Try removing this line and instead just looping through the code without doing anything and seeing if it still leaves the LDB. If it still leaves the LDB then the problem is not with the PopulateFlexGrid function
I agree with david saying there is something unclear here. Such a code cannot create an instance of Access or an .ldb file. This code could even run without Access being installed on the machine.
Could you open your recordset with a clientSide cursor? Using a serverSide cursor might cause the access file to be modified (not sure ...) in a way or another. To make sure that your next command does not interfeer in a way or another with access, you could even copy your record locally (to an xml file), close both records and connection, reopen the recordset with the xml file as datasource, and then populate your flexgrid.
By the way, and though it has nothing to do with your problem, it is usually preferable to split the object declaration to:
dim myObect as ADODB....
set myObject = New ADODB....