Open a new database and close the old one - vba

Greetings all: I have been struggling for 2 days to figure this out and have tried various techniques with frustrating results. I'm trying to start up a different database and close/quit the calling (first) database. I've created 2 button-click subs, one works very well and the second (which appears to be identical) flashes the new Db and then immediately returns to the calling DB. Here's the code that works well:
Dim objLeaveLookup As Object
Dim objOtherMenu As Object
Set objOtherMenu = GetObject("M:\QueryData\DBA Maintenance.mdb")
Set objLeaveLookup = GetObject("M:\QueryData\DBA_LookUp.mdb")
' Open Maintenance menu & quit DBA_Lookup
objOtherMenu.DoCmd.OpenForm "frm_MaintMenu"
objLeaveLookup.Application.Quit
and here is the code flashes the different Db, then frustratingly immediately returns to the original Db:
Dim objLeaveLookup As Object
Set objOtherMenu = GetObject("M:\QueryData\PurchMenu.mdb", "Access.Application").Application
Set objLeaveLookup = GetObject("M:\QueryData\DBA_LookUp.mdb", "Access.Application").Application
' Open Purchasing menu & quit DBA_LookUp
objOtherMenu.DoCmd.OpenForm "frm_PurMenu"
objLeaveLookup.Application.Quit
Any help to get the desired results will be GREATLY appreciated.

You may use FollowHyperlink.
See here: https://www.devhut.net/2018/01/21/ms-access-vba-open-another-database/

Related

MS Access: "Cannot open any more databases."

The Access error, "Cannot open any more databases.", has been discussed several times on StackOverflow[1, 2, 3, 4]. There's also an interesting discussion of the error on Bytes.com, in which Allen Brown and David Fenton weigh in on what can cause the error and argue about whether the limit behind it is about connections or table handles. None of the causes or solutions in those places apply to my situation, as far as I can tell.
I've got a complex database that was working well, but then started throwing this error. I've boiled down the VBA code and data structure to the following bare bones:
Option Explicit
Public Function TestFunction(ID_test_1 As Integer, nID_test_2 As Integer) As Variant
Dim oCurDb_Ftn As DAO.Database, oTestData As DAO.Recordset, sSQL As String, vTestCode As Variant
Set oCurDb_Ftn = CurrentDb()
sSQL = "SELECT * FROM t_test_2 WHERE ID_test_2 = " & nID_test_2 & ";"
Set oTestData = oCurDb_Ftn.OpenRecordset(sSQL)
vTestCode = oTestData![bValue]
oTestData.Close
Set oTestData = Nothing
Set oCurDb_Ftn = Nothing
TestFunction = vTestCode
End Function ' TestFunction
The numbers in the tables are arbitrary. The fields ID_test_n are primary keys. The code above is the entire contents of TestModule. The query TestQuery is:
SELECT t_test_1.ID_test_1, TestFunction(ID_test_1,3) AS [Test code]
FROM t_test_1;
When the query is opened, it at first appears to be okay. But if I scroll down through it, it throws the "cannot open any more" error:
I've discovered one way that I can get rid of the error. If I remove the first parameter of the function TestFunction(), so that it's definition becomes:
Public Function TestFunction(nID_test_2 As Integer) As Variant
...
and the call to it in the query becomes just:
TestFunction(3)
then I can scroll down and up through the query sheet multiple times without error. Those changes are possible because in the bare-bones code of the function, there is no reference to ID_test_1. But in the actual database, that parameter is passed for a reason and omitting it is not an option. Still, it is mysterious to me that whatever is causing the error does not happen if that parameter is not passed.
Can anyone see what's going on here, why I'm getting that error, and how to fix it without excluding parameters from the function?
Environment: Windows 10 Pro 64-bit, Access 2019.
This may be due to a bug in an Office update, about Jan 26, 2022. See Reddit post
Try system restore to roll back the update; or...
Go into Access, File Options, Trust center and add the local front end directory and then check the box to then add the backend data location as a trusted location.

Can I use VBA in Access to determine if a user has multiple instances of a database open?

I am looking for a way to determine if a user current has multiple instances of an Access database open on their machine. The purpose of this check would be to ensure better session handling.
It is perfectly fine for a user to open multiple instances. There are some elements of our work where this is a necessity, to browse existing records while entering a new one.
I came across this old forum post where a function is given to output a list of the currently open databases in the Workspace. I thought I may be able to adapt this to determine if the same database is open more than once on their machine. However, in testing the function (removing the part that opens a separate database), it always outputs the name of our database twice, regardless of whether I have just one instance open, or two or three.
Here's the adapted code I am using:
Public Sub listDBs()
Dim wrkCurrentWorkSpace As Workspace
Dim i As Integer
Dim strDatabases As String
Set wrkCurrentWorkSpace = DBEngine.Workspaces(0)
For i = 0 To wrkCurrentWorkSpace.Databases.Count - 1
If (i = 0) Then
strDatabases = wrkCurrentWorkSpace.Databases(i).Name
Else
strDatabases = strDatabases & Chr$(13) & Chr$(10) & wrkCurrentWorkSpace.Databases(i).Name
End If
Next
MsgBox strDatabases
Set wrkCurrentWorkSpace = Nothing
End Sub
Am I doing something wrong here?

MS Access Form Bind to ADO Recordset

Driving me bonkers...
The problem is one of speed.
I have a working SQL Server linked to a client accessible website, which I am wanting to add an Access front end to enable us office bound staff to better support both client and field staff.
The current form I have constructed is a main form with five sub forms contained within it, giving us all the relevant client information in one view. This works however is taking 24 seconds to load a single clients complete records.
I have looked at the SQL Server and found the absence of indexes, fixed this and got the time down to 24 seconds with consequent loads closer to 18 seconds depending on the client (some have a lot more records). This might be okay, as whilst this is a relative eternity in computing time in real world time its okay...but not great. I would like to see if I can get a better load by changing the way I connect and how the form is bound to the records etc.
In looking at the various ideas and reading a lot I found:
https://learn.microsoft.com/en-us/office/vba/access/concepts/activex-data-objects/bind-a-form-to-an-ado-recordset?source=docs
Which appealed to me as I am more inclined to use ADO, seldom if ever to I use DAO. ADO I understood originally was intended to use with SQL and so on, and it seems like a sensible idea.
Again as I understand it if I can get this to work it will act as a pass through query returning only one record over the net and should consequently speed my form up considerably. However it wont work.
My code is:
Private Sub cssSetForm(lngID As Long)
Dim cnn As New ADODB.Connection
Dim Rs1 As New ADODB.Recordset
Dim strSQL As String
Dim strR As String
cnn = "Provider=MSOLEDBSQL;Server=Server;Database=DatabaseName;UID=UserName; PWD=Password;"
cnn.Open
strSQL = "SELECT Clients.Clientid, Clients.AccountContact, Clients.AccountEmail, Clients.Address, Clients.Name, Clients.OfficePhone, Clients.PostCode, " & _
"Clients.ShentonAcc, Clients.Suburb FROM Clients WHERE (((Clients.Clientid)=" & lngID & "));"
With Rs1
Set .ActiveConnection = cnn
.Source = strSQL
.LockType = adLockPessimistic
.CursorType = adOpenKeyset
.Open
End With
Debug.Print Rs1.RecordCount
Me.Recordset = Rs1
End sub
Now I am getting no errors until Me.Recordset=rs1 which is generating an error 3251 Operation is not supported for this type of object which is very nice for someone that understands why this is not supported when it is no different than I can see to the example I was copying from.
I don't understand why the form I am working on doesn't support recordsets according to the error message? Is there an error in my code? Is the error in my understanding of the destructions from the linked site? Is the error something else?
Thanks for the help
Well, loading up 5 sub forms is a lot of data pulling. converting to ado reocdsets is NOT going to speed this up.
What you want to do here is NOT load up the sub forms until such time the user say clicks on the appropriate tab to load the one given sub form.
As long as the form in question is opened with a were clause, then the one main form will ONLY pull the one main record from sql server. So doing all kinds of fancy reocrdsets etc. will gain you next to nothing. So, always - but always launch your main form to the one record. If that main form is bound to a table of 5,000 rows, or 1 million rows, it will load instant despite the fact that the form is bound directly to the linked table with 1 million rows.
With this one main form, you edit or do whatever, and then close it. You are then of course right back to the search form/prompt you have to ask the user what reocrd to work on. So, how a accouting package works, or even google? You search, display the search resutlts and pick ONE thing to view. This approach should get your main form load down to about 1 second. Again, just use the "where" clause when you open that form:
eg:
dim strInv as string
strInv = InputBox("Enter invoice number to view")
docmd.OpenForm "frmInvoice",,,"InvoiceNum = " & strInv
Of course the above is air code, and you will likely build some search form say like this:
So in above, the user types in a bit of the name. We then fill the form with a simple where clause, or
me.MySubForm.RecordSource = "select * from tourCust where LastName like '" & sTextbox & "*'"
When a user clicks on the glasses icon to edit + view the ONE row, we use this:
docmd.OpenForm "frmDetails",,,"id = " & me!id
Again, all bound forms, and dispite the tables having 500,000+ rows, the loading of the forms is instant - even when the back end is SQL server.
So, adopt a prompt + search results + edit/view design pattern. EVERY single software system has this loop or design pattern. Not only is it user friendly, it also performs well since access DOES NOT pull the whole table, but ONLY the reocrds you tell the form to load by using the where clause as per above.
Now, for the child forms (sub forms).
As noted, don't load them until the user actually clicks on the given tab.
So, in the on-change event of the tab, you can go:
If Me.TabMainpage.Pages(Me.TabMainpage).Name = Me.pgeDocs.Name Then
'' the tab been changed to show past tours
dim strSQL as string
strSQL = "select * from tblPastTours where tour_ID = " & me!ID
me.
' dynamic load the sub form
If Me.frmSubPastTours .SourceObject = "" Then
Me.frmSubPastTours.SourceObject = "Forms.frmPastTours"
End If
' now load sql into form
me.frmSubPastTours.Form.RecordSource = strSQL
The above is mostly air code, but the idea here is:
don't load the sub form until you need to.
OR YOU can have the sub form load, but leave its RecordSource blank and STUFF in the sql you need for display WHEN you click on the tab.
It is possible that you need all sub forms to display. You could thus leave all sub form RecordSource blank, and then it the main form on-load event, simply stuff in the sql to each sub form. This might improve speed a bit, and with your slower connection this would be the least amount of work.
You "fail" to mention how you launch + load the main form, but as noted, it should be opend to the ONE reocrd. The link master/child pulling of data can be bit slow, and I can't say JUST using the above sql stuff into those forms will help a lot. I would try that first as it is the least amount of work. If load time is still too slow, then placing te sub forms behind a tab control and ONLY loading the sub form, and then setting the datasource will be the fastest.
Attempting to build all those recordsets, and then bind them to a form? It not speed things up, and just shoving a sql string into the form (or sub form) recordSource amounts to really the SAME thing and same performance anyway. So, you save tons of work and code, and quite much the above idea not only applies to sql server back ends, but we all VERY often dynamic load sub-forms and don't load them until such time you view the sub form say behind a tab control.

how to open a vb.application from another vb.application with parameters

i have 2 vb applications. this is the code for the first one which when a button is clicked it will check if the other application is already open. If not, it'll open that application -
Dim sComputer As String
sComputer = Environ("COMPUTERNAME")
Dim LocalByName As Process() = Process.GetProcessesByName("ticket.prices", sComputer)
If LocalByName.Length = 0 Then
System.Diagnostics.Process.Start("http://ticket.prices.application")
End If
this runs fine. but what i need is that the customerid on the application 1 that is calling application 2, should be transfered while opening app 2.
e.g -
Customer 10001 screen is open on app 1. When i click open app 2, the above code runs and opens app 2. how do i make app 2 open to customer 10001 screen. Is there any way to pass parameters while opening app 2 in System.Diagnostics.Process.Start ?
Use the version of 'Process.Start' that takes 2 strings, the second being the commandline parameters. See here for details.
You want the ProcessStartInfo class, or use the Start method taking to strings. ProcessStartInfo gives you a lot of options about how to start your program, which often comes in handy. Its good to get familiar with it.
Dim info as New ProcessStartInfo()
info.Arguments = "10001"
info.FileName = "exename"
Dim LocalByName as New Process()
LocalByName.StartInfo = info
LocalByName.Start()
Getting the arguments in the new program is accomplished via Environment.GetCommandLineArgs()
For Each arg As String In Environment.GetCommandLineArgs()
Console.WriteLine(arg)
Next arg
It looks like what you ultimately want to accomplish is getting the currently selected row from App 1 and passing that to the second program, though. Is this correct? That opens a whole new ball of wax involving interprocess communication.
EDIT: The simplest way to get the selected edit would be to write the id out to a text file. You have to be careful when doing this because if you just write System.IO.File.WriteAllText("selectedrow.txt", "123"), you'll write to the app's startup path directory. You'll want to get the exe's current path as below
Dim u as New Uri(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
dim exepath as String = System.IO.Path.GetDirectoryName(u.LocalPath)
dim fullPath as String = System.IO.Path.Combine(exepath, "selectedrow.txt")
System.IO.File.WriteAllText(fullpath, "123")
This will overwrite the text in the file every time you change rows. You want to wrap this in a try/catch block so as not to crash the program. Make sure you log the errors; don't just swallow them. To read the data, you just do
dim id as string = System.IO.File.ReadAllText(PathToFileYoureWritingToInTheOtherProgram)
in the other program.
This isn't necessarily the best way to go about things, but its the simplest way I know of off the top of my head.
You might could look at MessageQueues if you a better solution, but as long as you're not changing selected rows every 100ms, writing the file should work fine.

Intermittent error when attempting to control another database

I have the following code:
Dim obj As New Access.Application
obj.OpenCurrentDatabase (CurrentProject.Path & "\Working.mdb")
obj.Run "Routine"
obj.CloseCurrentDatabase
Set obj = Nothing
The problem I'm experimenting is a pop-up that tells me Access can't set the focus on the other database. As you can see from the code, I want to run a Subroutine in another mdb. Any other way to achieve this will be appreciated.
I'm working with MS Access 2003.
This is an intermittent error. As this is production code that will be run only once a month, it's extremely difficult to reproduce, and I can't give you the exact text and number at this time. It is the second month this happened.
I suspect this may occur when someone is working with this or the other database.
The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database.
Maybe, it's because of the first line in the 'Routines' code:
If vbNo = MsgBox("Do you want to update?", vbYesNo, "Update") Then
Exit Function
End If
I'll make another subroutine without the MsgBox.
I've been able to reproduce this behaviour. It happens when the focus has to shift to the called database, but the user sets the focus ([ALT]+[TAB]) on the first database. The 'solution' was to educate the user.
This is an intermittent error. As this is production code that will be run only once a month, it's extremely difficult to reproduce, and I can't give you the exact text and number at this time. It is the second month this happened.
I suspect this may occur when someone is working with this or the other database.
The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database.
Maybe, it's because of the first line in the 'Routines' code:
If vbNo = MsgBox("Do you want to update?", vbYesNo, "Update") Then
Exit Function
End If
I'll make another subroutine without the MsgBox.
I've tried this in our development database and it works. This doesn't mean anything as the other code also workes fine in development.
I guess this error message is linked to the state of one of your databases. You are using here Jet connections and Access objects, and you might not be able, for multiple reasons (multi-user environment, unability to delete LDB Lock file, etc), to properly close your active database and open another one. So, according to me, the solution is to forget the Jet engine and to use another connexion to update the data in the "other" database.
When you say "The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database", I assume that the role of your "Routine" is to update some data, either via SQL instructions or equivalent recordset updates.
Why don't you try to make the corresponding updates by opening a connexion to your other database and (1) send the corresponding SQL instructions or (2) opening recordset and making requested updates?
One idea would be for example:
Dim cn as ADODB.connexion,
qr as string,
rs as ADODB.recordset
'qr can be "Update Table_Blablabla Set ... Where ...
'rs can be "SELECT * From Table_Blablabla INNER JOIN Table_Blobloblo
set cn = New ADODB.connexion
cn.open
You can here send any SQL instruction (with command object and execute method)
or open and update any recordset linked to your other database, then
cn.close
This can also be done via an ODBC connexion (and DAO.recordsets), so you can choose your favorite objects.
If you would like another means of running the function, try the following:
Dim obj As New Access.Application
obj.OpenCurrentDatabase (CurrentProject.Path & "\Working.mdb")
obj.DoCmd.RunMacro "MyMacro"
obj.CloseCurrentDatabase
Set obj = Nothing
Where 'MyMacro' has an action of 'RunCode' with the Function name you would prefer to execute in Working.mdb
I've been able to reproduce the error in 'development'.
"This action cannot be completed because the other application is busy. Choose 'Switch To' to activate ...."
I really can't see the rest of the message, as it is blinking very fast. I guess this error is due to 'switching' between the two databases. I hope that, by educating the user, this will stop.
Philippe, your answer is, of course, correct. I'd have chosen that path if I hadn't developed the 'routine' beforehand.
"I've been able to reproduce this behaviour. It happens when the focus has to shift to the called database, but the user sets the focus ([ALT]+[TAB]) on the first database. The 'solution' was to educate the user." As it is impossible to prevent the user to switch application in Windows, I'd like to close the subject.