2019-08-04

22: Execute UNO Dispatch Command and Get All Information in C#

<The previous article in this series | The table of contents of this series | The next article in this series>

Your lazy way may be missing some useful information that is available from the execution.

Topics


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: C#

The table of contents of this article


Starting Context



Target Context


  • The reader will know how to execute any UNO dispatch command and get the result information and the related information in C#.
Stage Direction
Here are Hypothesizer 7, Objector 22A, and Objector 22B in front of a computer.


Orientation


Hypothesizer 7
In this article, we will know how to execute any UNO dispatch command and get the result information and the related information, in C#.

Objector 22B
I can do it also in Visual Basic.NET, right?

Hypothesizer 7
Yes, you can, madam.

Objector 22B
So, where is the Visual Basic.NET implementation?

Hypothesizer 7
Well, which implementation do you mean?

Objector 22B
Your implementation, of course!

Hypothesizer 7
I do not remember that I have created such an implementation . . .

Objector 22B
You haven't created it?

Hypothesizer 7
I believe so.

Objector 22B
I can't believe it!

Hypothesizer 7
Please believe it.

Objector 22B
Then, when will it be created?

Hypothesizer 7
Well, I do not remember that it has been planned to be created . . .

Objector 22B
What? Why?

Hypothesizer 7
I just do not notice much demand for it. . . . If a reader demands it, he or she can let me know.

Objector 22B
How?

Hypothesizer 7
He or she can say so somewhere and link it to this article, which I will notice.

Objector 22B
. . .

Objector 22A
I don't understand the litany of your 'Starting Context'; just succinctly tell me what I want to know so that I can understand!

Hypothesizer 7
Sir . . ., any technological knowledge has to be built up orderly beginning at the basics. You know, you cannot go to calculus without first learning addition, subtraction, multiplication, and division . . .

Objector 22A
Who do you think you are!

Hypothesizer 7
. . . It is not about me; it is about the laws of the universe.

Objector 22A
There may be parallel universes!

Hypothesizer 7
. . . How does that concern this issue, sir?

Objector 22A
You have flat feet!

Hypothesizer 7
Huh?


Main Body


1: An Easy Way to Execute Any UNO Dispatch Command Without Getting the Related Information


Hypothesizer 7
First, let us know a way to execute any UNO dispatch command without getting the related information.

Objector 22A
Huh? Why don't you tell how I can get the whole information?

Hypothesizer 7
One may not always need the whole information, and as some preparatory steps are common, I thought that I should additionally mention the simplified way while I explain the common preparatory steps.

Objector 22A
Then, I will skip this section.

Hypothesizer 7
Sir, as the next section (which is on getting the whole information) will be written based on this section, unfortunately, you are supposed to read this section (at least most of it), anyway.

Objector 22A
"supposed"? Am I "supposed"? Who do you think you are?!

Hypothesizer 7
. . . It is not about my self recognition; it is about the structure of this article.

Objector 22A
You have crooked toenails!

Hypothesizer 7
Huh? . . .

Anyway, as any UNO dispatch command is executed on a frame, first, we have to get the frame. If we want to execute the UNO dispatch command on a document, we will have to get the frame that contains the document; otherwise, we will use the UNO desktop frame.

Objector 22A
Does "the UNO desktop frame" mean 'the frame that is associated with the UNO desktop' or 'the frame that is the UNO desktop'?

Hypothesizer 7
The latter: the UNO desktop itself is a frame.

In order to get the frame that contains the document, first, we have to get a 'unoidl.com.sun.star.frame.XModel' UNO proxy of the document UNO object.

While there are some ways to get access to the UNO object of a specific document, I will not delve into all of them here (I will do so in a future article). Instead, I will introduce a widely-usable way of getting access to the UNO object of the current document.

Objector 22A
"widely-usable"? Yes. But that doesn't cover all the use cases.

Hypothesizer 7
Of course. So, you can refer to the future article.

Objector 22A
I can't refer to any non-existent-yet article . . .

Hypothesizer 7
Let us see this code, where 'l_objectsContext' is the UNO objects context and 'l_unoDocumentInXModel' is the 'unoidl.com.sun.star.frame.XModel' UNO proxy of the document UNO object.

@C# Source Code
			using unoidl.com.sun.star.frame;
			
							XDesktop2 l_unoDesktopInXDesktop2 = (XDesktop2) l_unoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.Desktop", l_unoObjectsContext);
							XModel l_unoDocumentInXModel = (XModel) l_unoDesktopInXDesktop2.getCurrentComponent ();

Objector 22A
Hmm.

Hypothesizer 7
Then, we get a 'unoidl.com.sun.star.frame.XDispatchProvider' UNO proxy of the frame UNO object, like this.

@C# Source Code
							XController l_controllerInXController = (XController) l_unoDocumentInXModel.getCurrentController ();
							XDispatchProvider l_frameInXDispatchProvider = (XDispatchProvider) l_controllerInXController.getFrame ();

Objector 22A
I see.

Hypothesizer 7
On the other hand, a 'unoidl.com.sun.star.frame.XDispatchProvider' UNO proxy of the UNO desktop frame UNO object, can be gotten like this.

@C# Source Code
			using unoidl.com.sun.star.frame;
			
							XDispatchProvider l_frameInXDispatchProvider = (XDispatchProvider) l_unoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.Desktop", l_unoObjectsContext);

Objector 22A
That is as expected.

Hypothesizer 7
Whichever frame we have got access to, then, we get a 'unoidl.com.sun.star.frame.XDispatchHelper' UNO proxy of a UNO dispatch helper UNO object, like this.

@C# Source Code
							XDispatchHelper l_dispatchHelperInXDispatchHelper = (XDispatchHelper) l_unoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.DispatchHelper", l_unoObjectsContext);

Objector 22A
Ah-ha.

Hypothesizer 7
Finally, we execute the UNO dispatch command, specifying the command URL and the command arguments, like this.

@C# Source Code
			using com.sun.star.beans;
			
							PropertyValue [] l_dispatchArgumentPropertiesArray = new PropertyValue [1];
							l_dispatchArgumentPropertiesArray [0] = new PropertyValue ();
							l_dispatchArgumentPropertiesArray [0].Name = "ToPoint";
							l_dispatchArgumentPropertiesArray [0].Value = "$B$2";
							Object l_commandResult = l_dispatchHelperInXDispatchHelper.executeDispatch (l_frameInXDispatchProvider, ".uno:GoToCell", "_self", -1, l_dispatchArgumentPropertiesArray);

Objector 22A
Well . . .

Hypothesizer 7
"_self" and "-1" are the target frame name of the command and the search flag for finding the frame.

As you can guess, '_self' is not any proper name, but a special frame name that means the concerned frame itself, which is 'l_frameInXDispatchProvider' in this case.

In order to understand the other special frame names, we have to know how frames are organized: any frame belongs to a hierarchy of frames, and any hierarchy is under the root frame (which is the desktop).

'_parent' means the direct parent of the concerned frame, '_top' means the top frame of the hierarchy to which the concerned frame belongs (Note that '_top' does not mean the root frame, but a frame under the root frame), and '_blank' means a new top frame.

Objector 22A
An odd terminology. Any top frame isn't top at all . . .

Hypothesizer 7
As for any Calc document, the frame we have gotten is a top frame, which has no child.

Objector 22A
When I use a proper name, not any special name, is the file name the proper name?

Hypothesizer 7
Actually, the proper name is blank unless you have set it explicitly.

Objector 22A
Seems meaningless . . .

Hypothesizer 7
"-1", as the search flag for finding the target frame, actually, means nothing, as the argument is ignored when any special frame name is specified in the previous argument.

Objector 22A
Well . . .

Hypothesizer 7
What the result information exactly is depends on the UNO dispatch command, but in many cases, the result information is not very useful.


2: The Way to Execute Any UNO Dispatch Command and Get the Result Information and the Related Information


Hypothesizer 7
In order to get the related information, we have to take the way discussed in this section.

As the result information and the related information are informed to a 'unoidl.com.sun.star.frame.XDispatchResultListener' and a 'unoidl.com.sun.star.frame.XStatusListener', respectively, we create a class that that implements 'unoidl.com.sun.star.frame.XDispatchResultListener' and create one or more classes that implement 'unoidl.com.sun.star.frame.XStatusListener', where a single class can implement both of the UNO interfaces.

For example, these are a 'unoidl.com.sun.star.frame.XDispatchResultListener' implementation and a 'unoidl.com.sun.star.frame.XStatusListener' implementation.

@C# Source Code
			using unoidl.com.sun.star.lang;
			
				class ResultInformationListener : XDispatchResultListener {
					public ResultInformationListener () {
					}
					
					public void dispatchFinished (DispatchResultEvent a_dispatchResultEvent) {
						Console.Out.WriteLine ("### The dispatch execution is finished.");
					}
					
					public void disposing (unoidl.com.sun.star.lang.EventObject a_source) {
					}
				}
				
				class RelatedInformationListener : XStatusListener {
					public RelatedInformationListener () {
					}
					
					public void statusChanged (FeatureStateEvent a_featureStateEvent) {
						Console.Out.WriteLine ("### A dispatch execution related information piece is offered.");
					}
					
					public void disposing (unoidl.com.sun.star.lang.EventObject a_source) {
					}
				}
				
							ResultInformationListener l_resultInformationListener = new ResultInformationListener ();
							RelatedInformationListener l_relatedInformationListener = new RelatedInformationListener ();

Objector 22A
I don't understand how can I use those event objects . . .

Hypothesizer 7
Please look those classes up in the UNO API document (here for 'unoidl.com.sun.star.frame.DispatchResultEvent', here for 'unoidl.com.sun.star.lang.EventObject', and here for 'unoidl.com.sun.star.frame.FeatureStateEvent').

Objector 22B
I have noticed now that the prefix, "unoidl.", is not any part of the UNO API names. Where did it come from?

Hypothesizer 7
Well, . . . I do not know the exact mechanism, but most .NET Framework UNO type names have it, probably, in order to avoid name conflicts.

Objector 22B
. . .

Hypothesizer 7
We get a 'unoidl.com.sun.star.frame.XDispatchProvider' UNO proxy of the frame UNO object, in the same way explained in the previous section.

Then, we set up a 'unoidl.com.sun.star.util.URL' object (a non-UNO object) like this.

@C# Source Code
			using unoidl.com.sun.star.util;
				
							unoidl.com.sun.star.util.URL l_urlInURL = new unoidl.com.sun.star.util.URL ();
							l_urlInURL.Complete = ".uno:GoToCell";
							XURLTransformer l_urlTransformerInXURLTransformer = (XURLTransformer) l_unoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.util.URLTransformer", l_unoObjectsContext);
							l_urlTransformerInXURLTransformer.parseStrict (ref l_urlInURL);

Objector 22A
. . . I didn't have to do such a tedious thing in the last section . . .

Hypothesizer 7
That is because the helper has taken up the tedious work. However, our doing the tedious work can be more efficient by maximally reusing the URL transformer UNO object and the 'unoidl.com.sun.star.util.URL' object.

Objector 22A
That's possible.

Hypothesizer 7
Anyway, then, we get a 'unoidl.com.sun.star.frame.XNotifyingDispatch' UNO proxy of the dispatcher UNO object like this.

@C# Source Code
							XNotifyingDispatch l_dispatcherInXNotifyingDispatch = (XNotifyingDispatch) l_frameInXDispatchProvider.queryDispatch (l_urlInURL, "_self", (short) -1);

Objector 22A
Can I reuse the dispatcher?

Hypothesizer 7
For the same URL, yes; for a different URL, no in general, although yes if the dispatcher happens to also be a dispatcher of the different URL.

Objector 22A
Ah, so, different dispatchers for different URLs, generally speaking . . .

Hypothesizer 7
Yes, that is the reason we have to specify the URL in order to get the dispatcher.

Objector 22A
That's quite reasonable.

Hypothesizer 7
Then, we register the related information listener(s) into the dispatcher, and finally, we ask the dispatcher to dispatch our command, like this.

@C# Source Code
			using unoidl.com.sun.star.beans;
			
							l_dispatcherInXNotifyingDispatch.addStatusListener (l_relatedInformationListener, l_urlInURL);
							PropertyValue [] l_dispatchArgumentPropertiesArray = new PropertyValue [2];
							l_dispatchArgumentPropertiesArray [0] = new PropertyValue ();
							l_dispatchArgumentPropertiesArray [0].Name = "SynchronMode";
							l_dispatchArgumentPropertiesArray [0].Value = new Any (true);
							l_dispatchArgumentPropertiesArray [1] = new PropertyValue ();
							l_dispatchArgumentPropertiesArray [1].Name = "ToPoint";
							l_dispatchArgumentPropertiesArray [1].Value = new Any ("$B$2");
							l_dispatcherInXNotifyingDispatch.dispatchWithNotification (l_urlInURL, l_dispatchArgumentPropertiesArray, l_resultInformationListener);
							l_dispatcherInXNotifyingDispatch.removeStatusListener (l_relatedInformationListener, l_urlInURL);

Objector 22A
It seems that I have to explicitly make it synchronous by specifying the 'SynchronMode' argument.

Hypothesizer 7
If you want it asynchronous, you will not have to, although I will want it synchronous, almost always.

The 'statusChanged' method can be called multiple times (or may not be called at all), and the 'dispatchFinished' method is called finally once.

In my UNO utility project, 'unoUtilitiesToBeDisclosed', which is used by my samples (a console C# UNO client, a files converter, etc.), the UNO document wrapper class, 'theBiasPlanet.unoUtilities.documentsHandling.UnoDocument', implements both of the listener interfaces and also has a method, 'dispatch', which executes any UNO dispatch command and returns the result information and the related information packed in the return value.

Objector 22B
. . .

Hypothesizer 7
'UnoDispatchSlotsConstantsGroup.BaseDispatchSlot a_dispatchSlot' as the first argument of the 'dispatch' method is an object that contains the URL and the argument names of the UNO dispatch command to be executed. In fact, the 'theBiasPlanet.unoUtilities.constantsGroups.UnoDispatchSlotsConstantsGroup' class holds such objects. I thought that putting such information in one place would be more favorable than scattering such information as string literals in programs.


3: The Conclusion and Beyond


Hypothesizer 7
Now, we know how to execute any UNO dispatch command and get the result information and the related information in C#.

Of course, we can execute any UNO dispatch command also in some other programming languages (Java, C++, Python, LibreOffice Basic or Apache OpenOffice Basic, BeanShell, and JavaScript); we have learned how to do so in Java and in C++ in some previous articles, and we will learn how to do so in some other languages in future articles (in Python and in LibreOfiice or Apache OpenOffice Basic).

There are an unfinished (so far) list of UNO dispatch commands for the LibreOffice foundations and the specifications of the listed commands and an unfinished (so far) list of UNO dispatch commands for LibreOffice Calc and the specifications of the listed commands.

Although executing some UNO dispatch commands is a way to handle any LibreOffice or Apache OpenOffice instance, it does not cover all of what we can do to handle the LibreOffice or Apache OpenOffice instance: we can do more by directly handling some UNO objects (please refer to the table of contents of this series).


References


<The previous article in this series | The table of contents of this series | The next article in this series>