Customised Open Document Macro works first time round, but hangs and/or crashes the second time around

I meant the usual course of action.
Macro for a Calc or Writer document:

Global oDBDoc as Object

Sub OpenDBDoc()
  Dim url
  Dim props(0) As New com.sun.star.beans.PropertyValue
  If oDBDoc Is Nothing Then
    url = ConvertToUrl("C:\Temp\Celsius.odb") 
    props(0).Name="Hidden"  
    props(0).Value=True
    oDBDoc =StarDesktop.LoadComponentFromUrl(url,"_default",0,Props)
  End If  
End Sub

Sub CloseDBDoc()
  If Not (oDBDoc Is Nothing) Then
    oDBDoc.close True
    oDBDoc = Nothing
  End If  
End Sub

Are there any pitfalls?

Hi Guys

Thank you so much for your help. Bear with me: compared to you guys I’m a beginner!

Okay:

1. Closing App with LibreOffice in Memory
I loaded a Calc document, then with that document still open I ran my application by clicking on the ODB file icon: the application runs as expected, but when the form is closed (using the code in my original message), the same errors occur when I try to start the app again i.e. when I try to restart, the system hangs or the BASE form appears with BASIC code running and the app’s macros unavailable.

2. Using a Start form in Calc
Then I created a Calc document with two buttons (DBStart and DBStop) addressing the subs in sokol92’s code: the OpenDBDoc code runs as expected when the properties are to set “Hidden” and “False”, as my app’s own open-doc code hides the BASE Window on start up.

With my app form open, I execute the CloseDBDoc sub using the button in the Calc document, and the system then hangs…

3. StandAlone forms
If by StandAlone forms, you mean the creation of a Writer-style form, I tried this earlier in my own app’s development: using this method the form loads before the login dialog executes (The login dialog is necessary because I have to detemine the user’s language before displaying the screen)…

I’m using:
Windows 11
LibreOffice v24.8.03 (X86_64)

I am now working out if I can leave the user with the BASE Window open when the form is closed, with the remote database connected with readonly permissions. But this still leaves my code vulnerable to change by the user.

I am hoping you guys have a better solution?

Best wishes and thanks again,

CB

The problem is synchronous execution of code, which doesn’t allow to do some destructive actions (closing document) in the middle of some other actions.

Note that there exist some ways to do something asynchronously.

  1. Dispatchers may handle a special SynchronMode boolean argument in their dispatch calls.

  2. There is AsyncJob service, which could be interesting.

1 Like

I was first introduced to this technique through this post by @hanya.

This is where the problem starts…
waits for the user to complete his/her work via an open form. When the user closes the form, a flag is switched that closes the application

Anything that prevents the Open Document event to finish running can cause a crash or become unresponsive.
Closing the document in the Open Document event or before the event finishes can do the same.

Sample database:
TestCloseDatabase.odb (13.5 KB)

The code in the sample database for hiding, minimizing/disabling and opening the form is commented out. This will allow testing the form to ensure it works with your OpenOffice/LibreOffice installation and operating system.

The code has only been tested in windows with AOO/LIBO version 4.1 and higher.
Your mileage may vary for other AOO/LIBO installations and operating systems.

If the only office application open is your database, closing the form will close the database without opening the start center thereby completely closing the office.
If there are other applications (writer, calc, base, ect) open, closing the form will only close the database.

The open document event presents 2 options.
Hide the base application…
Or
Minimize and disable the base application.

Pros and cons
If base is hidden users will not see or can interact with the base window. However, if there is 1 or more other office applications open, closing the last other application will also close the hidden base application. Which might not be desirable.

If base is minimized and disabled, closing other applications will not close the database.
Users can restore, maximize move and size the base window. However, they will not be able to interact with the base window nor will they be able to close it.

Macros for closing base

Sub CloseComponent(poComponent As Object)
'poComponent	ThisDatabaseDocument

Dim oComponentsEnum As Object, oComponent As Object
Dim i As Long, sUNO As String
oComponentsEnum = StarDesktop.getComponents().createEnumeration()
If (Not IsNull(oComponentsEnum)) Then
	Do While oComponentsEnum.hasMoreElements()
		On Local Error Goto errLocal
		oComponent = oComponentsEnum.nextElement()
		If Not isDatabaseComponent(oComponent) Then
			i = i + 1
		End If
		errLocal:
		On Local Error Goto 0
	Loop
End If

'Close the connection here if needed

'The application window must be visible
'poComponent.CurrentController.Frame.ContainerWindow.setVisible(True)
' OR
'The application window must be enabled
poComponent.CurrentController.Frame.ContainerWindow.setEnable(True)
Wait 300

If i = 1 Then
	sUNO = ".uno:Quit"
Else
	sUNO = ".uno:CloseDoc"
End If

unoQuitCloseDoc(poComponent, sUNO)
End Sub

Function getModuleIdentifier(oComp As Object) As String
Dim oModuleMgr As Object
oModuleMgr = createUnoService("com.sun.star.frame.ModuleManager")
getModuleIdentifier = oModuleMgr.identify(oComp)
End Function

Function isDatabaseComponent(oComp As Object) As Boolean
Dim sIdentifier As String
sIdentifier = getModuleIdentifier(oComp)
If sIdentifier = "com.sun.star.report.ReportDefinition" Then isDatabaseComponent = True : Exit Function
If Instr(sIdentifier, ".sdb") > 0 Then
	If sIdentifier <> "com.sun.star.sdb.OfficeDatabaseDocument" Then
		isDatabaseComponent = True
	End If
End If
End Function

Sub unoQuitCloseDoc(oDoc As Object, sUNO As String)
Dim oParser As Object, oFrame As Object, oDispatcher As Object
Dim aURL As New com.sun.star.util.URL
Dim arArgs() As New com.sun.star.beans.PropertyValue
If IsNull(oDoc) Then Exit Sub
If sUNO <> ".uno:Quit" Then
	If sUNO <> ".uno:CloseDoc" Then Exit Sub
End If
oDoc.setModified(False)
oParser = createUnoService("com.sun.star.util.URLTransformer")
aURL.Complete = sUNO
oParser.parseStrict(aURL)
oFrame = oDoc.CurrentController.Frame
oDispatcher = oFrame.queryDispatch(aURL, "_self", com.sun.star.util.SearchFlags.NORM_WORD_ONLY)
oDispatcher.dispatch(aURL, arArgs())
End Sub

The following macros can be placed in any module of the Standard library from “My Macros…”.
In a macro from a Base (.odb) document to close this document, you need to place the line before the end of the sub (function):

AsyncClose ThisComponent

In my experiments, the Base document closes normally.

To Standard library:

Sub AsyncClose(Byval oDoc As Object)
  CreateUnoService("com.sun.star.awt.AsyncCallback").addCallback(_
  CreateUnoListener("AsyncClose_",  "com.sun.star.awt.XCallback"), _
   oDoc)
End Sub

Sub AsyncClose_notify(arg)
  arg.Close True
End Sub
1 Like

I tested the AsyncCallback service a long time ago. It always left soffice.exe, soffice.bin and sbase.exe running in the background unless another office application was running but sbase was still running. And sometimes caused a crash.
However, I tested again using your code (incase something had changed) in OpenOffice 4.1.15 and LibreOffice 25.2.2 with the same results. While the service may work with other office applications (I never tested this) it does not play nicely with base.

Sub AsyncClose_notify(arg)
  arg.Close True  'this is the same as ThisDatabaseDocument.close which causes the problems
End Sub

That is true; but you can check your options 2 and 3 in the AsyncClose_notify; and I can tell, that your option 3 does work (you would need to pass the main frame there, like AsyncClose ThisComponent.CurrentController.Frame). Maybe option 2 would work, too (and then will not need any arguments passed - you will be able to create an instance of desktop right there).

Right, this works:


Sub Main
  AsyncClose Nothing
End Sub

...

Sub AsyncClose_notify(arg)
 oDesktop = createUnoService("com.sun.star.frame.Desktop")
 oDesktop.terminate()
End Sub

EDIT: I now see that @gzwgaw is not @CandidoBandido, and therefore “your options” wording is incorrect.

I never tried closing the desktop in the callback.
So… this is what i tried.

Sub AsyncClose()
CreateUnoService("com.sun.star.awt.AsyncCallback").addCallback(_
CreateUnoListener("AsyncClose_",  "com.sun.star.awt.XCallback"), Nothing)
End Sub

Sub AsyncClose_notify(arg)
Dim oDesktop As Object
oDesktop = createUnoService("com.sun.star.frame.Desktop")
oDesktop.terminate()
End Sub

I tested in OpenOffice and LibreOffice 10 times each. Five times using a button on a form and 5 times by the form’s title bar close button.
It worked like a charm in OpenOffice. LibreOffice crashed every time and on next startup got a crash report dialog.
Perhaps a bug in LibreOffice 25.2.2? I also rolled back to 24.2.8 with same results.
I also tested in OpenOffice portable 4.1.15 and LibreOffice portable 24.8.7 and got the same results. OpenOffice ok, LibreOffice not ok. Both are installed on a Samsung t2b portable ssd incase that means anything.

I also tired

Sub AsyncClose()
CreateUnoService("com.sun.star.awt.AsyncCallback").addCallback(_
CreateUnoListener("AsyncClose_",  "com.sun.star.awt.XCallback"), _
ThisDatabaseDocument.CurrentController.Frame
End Sub

Sub AsyncClose_notify(arg)
  arg.Close True
End Sub

Got the same results as above. OpenOffice ok, LibreOffice not ok.
Also I am using this in a database where closing a form is closing the database. Which I thought might be a problem with the callback. So I ran the AsyncClose macro from the base window (Tools Macros Run Macro) and got the same results.

Reading about ThisDatabaseDocument, I feel you are talking about macros stored in the document? But note:

(emphasis mine). There, you can use ThisComponent, but not ThisDatabaseDocument. And there, the functions using Desktop service work.

As for document-local ones, I don’t see crashes using 25.8.0.2 (only the process keeping running, as if quickstarter was active, which may be not a problem?); but version aside, the discussion here badly needs more concrete examples - with a document with macros, and specific steps.

Let’s test together.

  1. Create a module AsyncModule in the Standard application library (from My Macros) and write the macros (AsyncClose, AsyncClose_notify) from my message above into this module.
  2. Open the attached PassFormData.odb file and execute the CloseMe macro from document Standard library, module Module1.
    Expected behavior: the PassFormData.odb file closes without any error situations.
    Repeat this step many times.
    The PassFormData.odb file is taken from one of @Ratslinger 's messages.
  3. Open the attached PassFormData.odb file, open (Open…) CUSTOMERS form and click Close Me button
    Expected behavior: the PassFormData.odb file closes without any error situations.
    Repeat this step many times.
    PassFormData.odb (25.5 KB)

Hi soko192

I placed the two subs AsyncClose and AsyncClose_notify in the My Macros Standard Library, and my App (.ODB) now opens and closes normally!

THANKS!!!

So I have marked this as the solution. Now all I need to do is work out how to copy the subs into a user’s library the first time round…

Best wishes,

CB

For reference, I deleted from my code above:

ThisComponent.close(true)
WAIT 400
oDesktop.terminate()

… and changed it to :

AsyncClose ThisComponent

1 Like

The wonderful book by Andrew Pitonyak OpenOffice.org Macros Explained (OOME_4_1.odt) has a chapter “17. Library Management”/

I understood that as moving from My Macros to the standard library in the document. My mistake.
I had already moved the macros to My Macros Standard library and ran from there. Tested both office suites using the desktop service and it worked. No processes were left running and no crashes.
I also tested your macros in both suites. LibreOffice ok. OpenOffice processes were left running.

1 Like

Hi Everyone

So I created a library module in a different file (a Writer file called READMEFIRST) that has a push button that when clicked copies the files over to the Standard Library in My Macros on the destination computer. So my .odb should never hang any more.

I used Listing 527 in Pitonyak’s chapter cited in soko192’s message to help me write the macro.

The only fun I had was distinguishing between the destination library container (on the destination computer) and the container on the source computer. In case it helps anybody else:

oDestLibs = GlobalScope.BasicLibraries  ' this is the destination library container object on the home computer
oSrcLibs =  oComponent.BasicLibraries   ' this is the source library container object of the current document

Thanks to all who have me this far.

Best Wishes,

CB

2 Likes

@CandidoBandido : Only one hint: The Standard library will often be cleared from content. Just opened LO 25.8.0.2 and the library is empty again. So I won’t save anything in this library except test procedures.

To be honest, I never ever has such an accident. And I work with very many releases. Such a thing is an own bug.

Problem with this buggy behavior: I couldn’t write a step by step description to reproduce it. But it just had happened when I opened LO 25.8.0.2 and want to test the code reported above. Whole library had been cleared. No problem for me, because I have seen it before and only use it for testing.

I have also tested the procedure with database @sokol92 had attached. If Base is the only open component the desktop disappears. But when having a look at the open processes LibreOffice is still there in the background with 946,4 MB. So it isn’t really closed under OpenSUSE 15.6 Linux together with LO 25.8.0.2

Okay.

For me, the library is still there today!

But - Belt and Braces - I will write a procedure to test whether or not the library module exists when I start the .odb app and exit the app with a message to reload the library (and reboot the computer!) if it’s not there…

And thanks again for all your help!

Best wishes,

CB

In Win10 LO 25.2.5.2 Task Manager also shows that the LibreOffice process exists. Perhaps (?) this is the process responsible for Start Сenter. It doesn’t seem to have any negative consequences for me.


My macro only asynchronously closes the .odb file (any document). We can also write a macro that (asynchronously) closes the application:

  • closes BasicIDE (if open)
  • sets the status of the modified-flag of open documents to False
  • closes StarDesktop