I am looking for a solution to effectively communicate between two running MS Access applications.
The approaches I tried so far is to use a common linked table and to use MSMQ service for communication. Both approaches work, but there is no way to "push" the data or command from one application to another and since MS Access doesn't support multi-threaded execution of VBA code, it is very difficult to implement polling without performance disadvantages.
Same time, VBA does support the addressof operator (from version 2000) that means we can also theoretically implement call-back functions in VBA and MS Access. But I have never seen any example how this can be used for inter-process communication and would appreciate any minimal example how I can send a string from one MS Access application to another without monitoring a shared table all the time.
You can use GetObject() to return the Access.Application object from another running db. With the application object you have access to just about everything you might need. Here's a contrived example of opening a form (but you can do a myriad of other things with the Application object):
Sub TestInterop()
Const mdbPath As String = "C:\OtherApp.mdb"
Dim OtherApp As Access.Application
Set OtherApp = GetObject(mdbPath)
OtherApp.Visible = True
OtherApp.DoCmd.OpenForm "Accounts"
End Sub
If the program is not already running, the GetObject() call will start the application (you would need to be careful if you have multiple versions of Access installed as it's difficult to know at runtime which version would actually open the .mdb). However, if GetObject() needs to start the app, it will do so with the visibility set to False, so we explicitly set it to True. If the app is already running, setting its Visibility to True will have no effect.
Consider it a wild idea, but may be put all your tables into sql express and/or sql ce and make look like a frontend to those tables?
Related
What are the differences between the following sets of Microsoft Access VBA collections..? Some of them seem to point to the same data & child objects, but through different conduits. I'm thinking the DAO objects are DBMS-specific, but Access.⃰ Code.⃰ and Access.⃰ Project.⃰ are not, and get connected through Access. There is ADODB, which is another beast entirely. Also, it seems some of the collections may not exist if there no child objects of that type.
Given the mentions of SQL features, there seem to be three scenarios to be concerned with, but it's difficult to grasp:
A standalone ACCDB file.
A split database with two or more ACCDB
files.
An Access frontend to an SQL server.
Object set #1:
Access.CodeData.AllTables
Access.CodeData.AllQueries
Access.CodeData.AllFunctions
Access.CodeData.AllDatabaseDiagrams
Access.CodeData.AllStoredProcedures
Access.CodeData.AllViews
Access.CodeProject.AllForms
Access.CodeProject.AllMacros
Access.CodeProject.AllModules
Access.CodeProject.AllReports
And of course CodeData vs. CurrentData and CodeProject vs. CurrentProject.
Object set #2:
Access.Application.CodeData.*
Access.Application.CodeProject.*
Object set #3:
Access.Application.CurrentDb.QueryDefs (DAO.QueryDefs)
Access.Application.CurrentDb.Relations (DAO.Relations)
Access.Application.CurrentDb.TableDefs (DAO.TableDefs)
The biggest difference is in the type of database you are working with. A standard Microsoft Access database (*.accdb) will primarily use the CurrentDB/CodeDB object to reference the database objects.
An ADP Project, on the other hand, is kind of a hybrid with some data on the SQL server (tables, queries, etc...) accessed through CurrentData/CodeData and other objects like forms and reports residing in the *.adp file and accessed through CurrentProject/CodeProject.
The difference between CurrentDB and CodeDB comes into play when you have multiple database files involved, such as other Access databases referenced as library databases. Those library databases might have functions to interact with the parent application, and others that reference the library.
As you should already know (since I told you here), you can store code in external databases, and refer to that code from another database.
The code in this external database might rely on certain tables, queries and forms to be present. But since it's ran on another database, they might not be.
That's where CodeDb, CodeProject and CodeData come in. They're fully equivalent to the variants starting with Current, only refer to the database where the code is stored, instead of the database which is currently open.
Differences between CurrentDb, CurrentProject and CurrentData is really asking for differences between apples, pears and oranges. They're all objects, but fulfill different functions. As ThunderFrame noted, CurrentDb is mostly DAO, and CurrentProject contains some ADO functionality (mostly just the connection), but also functions to influence the VBA part of the database, the database path, and many other functions. And CurrentData is something else entirely.
In Access, the CodeDB and CurrentDb functions return DAO.Database objects.
CodeDb is the database or add-in, within which the code is running (which might not be the same as the CurrentDb.
CurrentDb is the currently active database (not an add-in).
Access 2002/03 added ADODB, and so they added CurrentProject and CodeProject functions for returning the ADO equivalents of CurrentDb and CodeDb, but being ADO, they have very different methods and properties.
Can anyone confirm whether linking to tables in a Microsoft Access .accdr files is possible?
I have a split MS-ACCESS application with the frontend in a .accdr file and the tables in an .accdb file backend on a server. For reasons that I explain below, I need the frontend to connect to a table in a second backend file (also on the server) that has an .accdr extension.
I found no documentation that explicitly states that .accdr files are not allowed as as a backend database, however, the linked table manager does not seem to allow it, and the DoCmd.DatabaseTransfer method throws an error.
My workaround is to temporarily rename the .accdr server file as an .accdb, link to the tables in it to make the data changes, then rename it back to .accdr. But if I don't HAVE to do this, I would prefer to connect directly.
Reason:
To make sure that each client gets the most recently updated copy of the frontend .accdr, I created a batch script that first copies the frontend from the server to their local machine before launch. It's a short time penalty (the frontend is about 8 MB) each time they load the application, but the batch script makes sure that the client is ALWAYS up to date. (If performance became an issue I would of course have the batch script verify MD5 sums before downloading, but it hasn't come up yet.)
I don't have many clients connecting at the same time (only about 2 or 3 concurrent connections), but they can connect from any of thousands of computers on campus, so I found this the easiest way to make sure that no matter where they connect from, they will get a fresh copy of the FrontEnd.
Unfortunately, I made a few tables local to the frontend because they allow individual users to set parameters locally during a session. Now, of course, I have a case where an individual user needs to update these local tables so that all the other users will get this same data. (Previously, I had just been making these changes manually upon request, but this user quite reasonably needs to be able to do it herself.)
So, the local frontend .accdr file needs to connect to the server copy of the same frontend .accdr file to make a few data adjustments in a table. That's the reason. I know it's weird.
Thanks,
Damon
Turns out it is possible to use .accdr as a backend. I think i was specifying the wrong connection or something. I used this code snippet with the strConnect = path_to_mydb.accdr:
Public Sub ConnectOutput(dbsTemp As Database, _
strTable As String, strConnect As String, _
strSourceTable As String)
Dim tdfLinked As TableDef
' Create a new TableDef, set its Connect and
' SourceTableName properties based on the passed
' arguments, and append it to the TableDefs collection.
Set tdfLinked = dbsTemp.CreateTableDef(strTable)
tdfLinked.Connect = strConnect
tdfLinked.SourceTableName = strSourceTable
dbsTemp.TableDefs.Append tdfLinked
End Sub
You could easily add the tables that are local to your front-end to the backend. Just copy/paste them from one to the other, delete them from the front-end and link them to the backend version. Takes all of 5 minutes, no VBA code necessary.
Regarding the accdr as a backend question, then yes, accdr is meant to be a open by a the Runtime version of Access. Opening an .accdr file with the standard version of Access simulates the Runtime mode. The only difference between the standard version and the free Runtime version is that you can't access the design tools and the standard ribbon from a Runtime application (you need to create your own).
So renaming a accdb file into accdr only tells access to open the database in Runtime mode. It doesn't change the database itself at all.
So you can use an accdr file as a back-end without problem.
Pro tip: if you want your setup to scale a bit (easily up to 50 concurrent users) and have better performance, open a database to a dummy table from the front-end to the back-end. This will keep the connection open while the front-end is running and keep the lock file open on the database, resulting in better -and more reliable- performance.
I want to be able to use the Sendwait function to automate a MS-ACCESS application using powershell commands. Unfortunately I haven't been able to get a handle in the usual way to the actual application window. I can get a handle to MS-ACCESS itself, but attempts using Sendwait to automate the window failed.
Any reason why you need to send keys? Why not just use the methods and properties of the object in question.
Just go:
To launch Access, open a database, and then print a report, and then quit?
$Acc = New-Object –com Access.Application
$Acc.OpenCurrentDataBase("c:\t.accdb")
$Acc.docmd.OpenReport("rptCustomers")
$Acc.Quit()
So most if not all methods and properties of the Access application can be used via power-shell, and as a result, I don't think you need nor want to attempt using some type of send keys as that tends to be rather flakey and a poor way to automate things. You might want to point out what commands or what you require, but you can also call VBA code this way, and as noted, even add records etc.
We have developed a consolidation function that will be used by other processes and want to position the function in its own MDB (call it "remote") so that it can be referenced and called from "caller.mdb" when its needed. The function is designed to return an array and works great when executed called directly from within "remote." However, with "remote" properly referenced in the "caller" VBA project, when "caller" makes the call the function returns errors. We get a variety of errors such as
3078: Jet cannot find the input table or query
QUESTION. Within "remote", how does one properly set references to the db and its local objects (e.g. one table and several queries including INSERT and UPDATE queries)? CurrentDB is apparently not the answer; we have also experimented with the AccessObject and CodeData objects. "Remote" and "caller" currently reside on the same drive, so that wouldn't seem to be the problem.
Instead of CurrentDb you could use with CodeDb wich points to the mdb currently executing the code.
Set db = CodeDb
The way Access itself does this (with all the wizards, which are all programmed in Access), is to use Application.Run. It does mean the code you're calling has to be a function, though it doesn't matter what it returns. Application.Run requires no references, just a path:
Application.Run("MyCodeDatabase.MyFunction()")
Obviously, if the code database is not in the path that Access uses (which includes its own app folders (including the app-specific folders in the user's profile) and the folder where your main application front end is stored), you'll need to specify the full path.
Application.Run() is a function that returns a value, but it is typed as variant. This may or may not work with your array. It's not clear from the object browser whether or not the arguments are passed ByVal or ByRef, but if they are ByRef (which is what I'd expect), you might just pass the array in and let the function work on it and then use it after the code in the remote database has completed.
On the other hand, the arguments are probably variants, so there's not much difference between that approach and just using the structure returned by Application.Run().
Marcand gave you the answer to your immediate question. There are other problems and irritations when it comes to using add-ins or referenced Access databases. See my Add-in Tips, Hints and Gotchas page.
There are a number of differences and nuances to calling forms and functions through a reference in a another MDB or ADP. I have run into issues in both situations, and what you are referring to as the "remote" database, I refer to as a central library.
At my Tips and Tricks page at http://www.mooresw.com/tips.php, I have pages devoted to programatically changing references, getting Access to search for the referenced file instead of having a broken reference, and calling forms through a reference.
Programatically changing references is needed when you publish the database from the development environment to the user or production environment. When working in the development folder, it's fine for the program to have a reference to the central library directly, but we wouldn't want code that 20 users are running tying up the central library in our development area. (An MDB file opened through a reference gets locked just as though your users were opening it directly)
The situation of running a form in a central library (or "remote" database) where there are no links or tables can be tricky. In that situation I've chosen to open a connection to the "caller.mdb" using ADO code with a Jet connection string in the open event of the forms. Doing so provides the ability for the code in the form (or functions in the library) to gain access to the tables and queries in the calling mdb.
For further information, see my pages at the tips link above, and in particular, see:
http://www.mooresw.com/call_a_form_in_another_MDB_through_a_reference.php
which I believe is most relevant to your situation.
I have used Access 2003 to develop a frontend to a SQL Server database. Because the system was intended to use different Schemas to partition the table data, an Access Project did not work. So, instead, I am forced to take over the connection management for access - I maintain a global connection object, and assign recordsets to Forms rather than recordsources.
One issue this causes - any time someone tries to use built-in Access functionality that interacts with the recordset, the operation doesn't work, and a dialog box is displayed saying 'Data Provider could not be initialised'. I've done some research, and have been unable to find a relevant cause for this, but I suspect it's due to Access expecting a Form to have a proper recordsource property, and does not really work with assigned recordsets.
Anyone able to shed any more light on this one? Is there a way to use self-managed recordsets, AND make use of built-in functionality? Can anyone confirm this is an Access bug?
After some playing around, I found a partial solution. I was using Client-side cursors, but leaving the recordsets connected. By instead disconnecting the recordsets, the Data Provider messages went away. Of course, that introduced other issues, but that's another story...and of course, I still don't understand WHY doing this made a difference...anyone else?