2019年12月8日日曜日

21: UNOディスパッチコマンドを実行し、全情報を取得する(C++編)

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

あなたの怠惰な方法は、実行から入手可能な一部の有用な情報を逃しているかもしれません。

話題


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

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、C++でUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知る。
ト書き
Hypothesizer 7、Objector 21A、Objector 21Bがコンピューターの前にいる。


オリエンテーション


Hypothesizer 7
この記事では、C++でUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知ることになります。

Objector 21A
「C++で」だと?マクロは、Basic、Python、BeanShell、JavaScriptのいずれかでのみ書けると思っていたが。

Hypothesizer 7
サー、マクロを書くことについては仰っしゃるとおりですが、私は、外部コンソールC++UNOクライアントや、外部GUI C++ UNOクライアントや、C++コードを含むLibreOfficeまたはApache OpenOffice拡張機能を作成することについて話しております。

Objector 21A
それじゃあ、マクロを書きたい私には関係ないな。

Hypothesizer 7
あなたは、機能をLibreOfficeまたはApache OpenOffice拡張機能のC++ UNOコンポーネント群に実装して、そうしたUNOコンポーネント群を呼び出す、ごく小さなマクロを作ることができます。

Objector 21A
なんで、私がそんなことをするというのかね?

Hypothesizer 7
なぜなら、ほとんどのマクロ言語、特にBasicには、致命的制約があるからですBasicには、満足のいくドキュメントがないという欠陥もあります。御自身のUNOコンポーネントをJavaまたはC++で作成する方法を知れば、JavaまたはC++でできる事はあなたは何でもできるようになります。

Objector 21A
Basicが最低だということは知っている。だから私はPythonを使う。

Hypothesizer 7
ああ、それはベターそうですね: Pythonは、オブジェクト指向プログラミング言語であり、いくつかの良い機能を備えています、Pythonが動的型付け言語だというのは残念ですが。動的型付け言語であることが良いことだとは私は認めません、動的型付け言語であるにもかかわらず(そうであるがゆえにではなく)プログラミング言語が結構良いということがありうるということは認めますが。

Objector 21A
...最近のバージョンのPythonでは静的な型チェックができるということを知っているか?

Hypothesizer 7
本当ですか?

Objector 21A
ビルトイン機能ではない(タイプアノテーションを受け付けるという部分はビルトイン機能)が、静的な型チェックを行なうツールがいくつかある。

Hypothesizer 7
それはとても有望そうですね。確かに、静的な型チェックができるPythonは素晴らしいでしょう。

Objector 21A
それで?

Hypothesizer 7
本記事のPython版を書きましょう、しかし、だからといって、このC++版が存在すべきでないということにはならないでしょう?

Objector 21A
そうだとは思うが: 私はC++プログラマーでもある、実のところ。確かに、C++には、利点がある、例えば、スピードだ。

Objector 21B
「関連情報」というのが何なのか私には分からないんだけど。それって、「結果情報」とどう違うわけ?

Hypothesizer 7
マダム、それらの間に明快な区別を付けようとすることは有益ではありません。各UNOディスパッチコマンドが、かなり恣意的に、実行情報の一部を結果情報として、残りを関連情報として提供するのです。

'関連情報'は公式には「ステータス変更情報」と呼ばれているようですが、それは実体とかけ離れた名前だと私は結論せざるを得ません: 多くのケースにおいて、「ステータス変更情報」はいかなるもののいかなるステータスのいかなる変更とも関係ありません。

Objector 21B
...

Hypothesizer 7
私が言えるのは、入手可能な情報の全体を取得する方法を知っているのは良いことだということです、なぜなら、有益情報が、UNOディスパッチコマンドによって、結果情報に現われたり、関連情報に現われたりするからです。


本体


1: UNOディスパッチコマンドを関連情報を取得することなく実行する容易な方法


Hypothesizer 7
最初に、UNOディスパッチコマンドを関連情報を取得することなく実行する方法を知っておきましょう。

Objector 21A
...そんな方法を知る必要はないんじゃないか?UNOディスパッチコマンドを入手可能な全情報を取得するように実行する方法を知ってれば、それで十分だろう?

Hypothesizer 7
そのとおりです。関連情報は全然要らないという一部の人々がより容易な方法を知りたいかもしれないと思っただけでして。

えーと、UNOディスパッチコマンドを入手可能な全情報を取得するように実行する方法は次セクションで紹介されますが、次セクションは本セクションに基づいて記述されるので、あなたはどのみち本セクション(少なくともその大半)を読むように想定されています。

Objector 21A
それは残念だ...

Hypothesizer 7
読者は、LibreOfficeまたはApache OpenOfficeのインスタンスへのUNOオブジェクト群コンテキストを取得する方法の知識を持っている(外部コンソールC++プログラムにおいてそうする方法についての記事があります。外部GUI C++プログラムにおいてそうする方法についての記事、C++コードを含むLibreOfficeまたはApache OpenOffice拡張機能('UNO拡張機能'とも呼ぶ)においてそうする方法についての記事は、以後、公開されます)ものと事前想定されています。

Objector 21A
私はいろいろと想定されているようだ...

Hypothesizer 7
第1に、そのドキュメントUNOオブジェクトの'::com::sun::star::frame::XModel' UNOプロキシを取得しなければなりません。

Objector 21A
...どの「ドキュメント」のことを言っているのだ?

Hypothesizer 7
その上でUNOディスパッチコマンドを実行したいというドキュメントです。

Objector 21A
当該UNOディスパッチコマンドは特定のドキュメントについてのものじゃないかもしれんだろう?

Hypothesizer 7
ああ、確かに。その場合は、UNOデスクトップフレームを直接使えます。

Objector 21A
はあ?

Hypothesizer 7
UNOディスパッチコマンドはどれも、あるフレームに対して実行されるのであり、そのフレームをどう取得できるかについて私たちは話しています。もしも当該UNOディスパッチコマンドが特定のドキュメントについてのものでないのであれば、取得されるべきフレームはUNOデスクトップであるはずです。

Objector 21A
ああ、じゃあ「UNOデスクトップ」はフレームであるわけだ。

Hypothesizer 7
はい。以下のコードの第1実行行を少し変えて、'::com::sun::star::frame::XDesktop2' UNOプロキシの代わりに'::com::sun::star::frame::XDispatchProvider' UNOプロキシを取得するようにできます。それで、あなたは既に、あなたが必要としているUNOディスパッチャー群プロバイダーを持っています。

その一方で、当該UNOディスパッチコマンドがあるドキュメントに対して実行される場合は、そのドキュメントを格納しているフレームを取得します。

Objector 21A
じゃあ、そのドキュメントUNOオブジェクトを取得しよう!

Hypothesizer 7
もしも、そのドキュメントをあなたのプログラム内でオープンしたのであれば、そのドキュメントがオープンされた際に、そのドキュメントUNOオブジェクトをあなたは取得できたはずです。

Objector 21A
残念ながらそうしてないな。

Hypothesizer 7
それでは、特定のドキュメントのUNOオブジェクトを取得する方法がいくつかあります、しかし、それらの全てに立ち入ることは、ここではしません(ある以降の記事でそうするでしょう)。

その代わりに、1つの方法を紹介しましょう: カレントドキュメントのUNOオブジェクトを取得します。以下のコードを見てみましょう('l_objectsContext'は、UNOオブジェクト群コンテキストであり、'l_unoDocumentInXModel'は、当該ドキュメントUNOオブジェクトの'::com::sun::star::frame::XModel' UNOプロキシです)。

@C++ ソースコード
#include <com/sun/star/frame/XDesktop2.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include "theBiasPlanet/unoUtilities/stringsHandling/UnoExtendedStringHandler.hpp"

using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::uno;
using namespace ::theBiasPlanet::unoUtilities::stringsHandling;

						Reference <XDesktop2> l_unoDesktopInXDesktop2 (l_unoObjectsContext->getServiceManager ()->createInstanceWithContext (UnoExtendedStringHandler::getOustring ("com.sun.star.frame.Desktop"), l_unoObjectsContext), UNO_QUERY);
						Reference <XModel> l_unoDocumentInXModel (l_unoDesktopInXDesktop2->getCurrentComponent (), UNO_QUERY);

Objector 21A
...その'UnoExtendedStringHandler::getOustring'というのは何だ?

Hypothesizer 7
それは、私のユーティリティクラスのメソッドであり、指定したUTF-8 '::std::string'インスタンスからUNO C++文字列インスタンスを作成するものです(そのクラスは、私のどのUNOサンプル(例えば、ファイルコンバーター)のZIPファイルにも含まれている'unoUtilitiesToBeDisclosed'プロジェクト内にあります)。

Objector 21A
なぜそんなユーティリティクラスが必要なんだ?

Hypothesizer 7
なぜなら、'UNO C++文字列'というのは、文字配列でもスタンダード文字列でもなく、UNO独自のクラスであり、UNO C++文字列インスタンスを作成するのは、それほど単純ではないからです: UTF-16文字配列をそのコンストラクタ群の1つに渡さなければなりません。

Objector 21A
うーん...

Hypothesizer 7
次に、当該フレームUNOオブジェクトの'::com::sun::star::frame::XDispatchProvider' UNOプロキシを取得します、以下のように。

@C++ ソースコード
<![CDATA[#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>

						Reference <XController> l_controllerInXController (l_unoDocumentInXModel->getCurrentController (), UNO_QUERY);
						Reference <XDispatchProvider> l_frameInXDispatchProvider (l_controllerInXController->getFrame (), UNO_QUERY);

Objector 21A
なるほど。

Hypothesizer 7
次に、UNOディスパッチヘルパーUNOオブジェクトの'::com::sun::star::frame::XDispatchHelper' UNOプロキシを取得します、以下のように。

@C++ ソースコード
#include <com/sun/star/frame/XDispatchHelper.hpp>

						Reference <XDispatchHelper> l_dispatchHelperInXDispatchHelper (l_unoObjectsContext->getServiceManager ()->createInstanceWithContext (UnoExtendedStringHandler::getOustring ("com.sun.star.frame.DispatchHelper"), l_unoObjectsContext), UNO_QUERY);

Objector 21A
はああ。

Hypothesizer 7
最後に、UNOディスパッチコマンドを実行します、コマンドURLおよびコマンド引数群を指定して、以下のように。

@C++ ソースコード
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>

using namespace ::com::sun::star::beans;

						Sequence <PropertyValue> l_dispatchArgumentPropertiesSequence (1);
						l_dispatchArgumentPropertiesSequence [0].Name = UnoExtendedStringHandler::getOustring ("ToPoint");
						l_dispatchArgumentPropertiesSequence [0].Value = Any (UnoExtendedStringHandler::getOustring ("$B$2"));
						Any l_commandResult = l_dispatchHelperInXDispatchHelper->executeDispatch (l_frameInXDispatchProvider, UnoExtendedStringHandler::getOustring (".uno:GoToCell"), UnoExtendedStringHandler::getOustring ("_self"), -1, l_dispatchArgumentPropertiesSequence);

Objector 21A
「_self」?「-1」?

Hypothesizer 7
それらは、コマンドのターゲットフレーム名とそのフレームを発見するための検索フラグです。

Objector 21A
「_self」というのは意義のある名前に思えないな: どのフレームも自らを「_self」と呼んだんじゃあ、どのフレームも区別されない...

Hypothesizer 7
それは、問題にしているフレーム自体(本ケースでは'l_frameInXDispatchProvider')を意味する特殊フレーム名です。それに意味があるのは、'self'であることの起点が指定されているからです。

Objector 21A
「-1」はどういう意味なんだ?

Hypothesizer 7
実は、それは何も意味しません、その引数は、その前の引数に特殊フレーム名が指定された場合は無視されるので。

Objector 21A
'_self'の代わりに何を指定できるんだ?

Hypothesizer 7
どのフレームも1つのフレーム群階層に属しますが、'_parent'は、問題にしているフレームの直接の親を意味し、'_top'は、問題にしているフレームが属する階層のトップフレームを意味し、'_blank'は、新たなトップフレームを意味します。

ご注意いただきたいのですが、階層はどれもルートフレーム(デスクトップ)の下にありますが、階層のトップフレームは、ルートフレームではなく、ルートフレームの下のフレームです。

Objector 21A
妙な用語体系だと言わざるを得ないな。

それはとにかく、Calcドキュメントの階層はどのようになっているんだ?

Hypothesizer 7
実は、私たちが取得したフレームがトップフレームであり、子はありません。

Objector 21A
それでは、'_self'の代わりに'_top'を指定できると、結果に何の影響もなく?

Hypothesizer 7
はい、できます。

Objector 21A
フレームの固有名も使えるんだろ?

Hypothesizer 7
はい、可能です、ただ、どのフレームも固有名はブランクです、あなたが名前を与えない限り。

Objector 21A
そうか...

Hypothesizer 7
結果情報が具体的に何であるかは、UNOディスパッチコマンド次第です、しかし多くの場合、結果情報はあまり有益ではありません。


2: UNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法


Hypothesizer 7
関連情報を取得するには、本セクションで論じられる方法を取らなければなりません。

結果情報および関連情報はそれぞれ'::com::sun::star::frame::XDispatchResultListener'および'::com::sun::star::frame::XStatusListener'に通知されるので、'::com::sun::star::frame::XDispatchResultListener'を実装するクラス1つおよび'::com::sun::star::frame::XStatusListener'を実装するクラス1つ以上を作成します。単一のクラスに両UNOインターフェースを実装させることもできます。

例えば、以下は、'::com::sun::star::frame::XDispatchResultListener'の1実装と'::com::sun::star::frame::XStatusListener'の1実装です。

@C++ ソースコード
#include <com/sun/star/frame/XDispatchResultListener.hpp>
#include <com/sun/star/frame/XStatusListener.hpp>
#include <com/sun/star/lang/EventObject.hpp>
#include <com/sun/star/frame/DispatchResultEvent.hpp>
#include <com/sun/star/frame/FeatureStateEvent.hpp>
#include <cppuhelper/compbase1.hxx>
#include <osl/mutex.hxx>

using namespace ::cppu;
using namespace ::osl;

			class ResultInformationListener : public WeakComponentImplHelper1 <XDispatchResultListener> {
				protected:
					Mutex i_mutex;
				public:
					ResultInformationListener ();
					virtual ~ResultInformationListener ();
					virtual void SAL_CALL dispatchFinished (DispatchResultEvent const & a_dispatchResultEvent) override;
					virtual void SAL_CALL disposing (::com::sun::star::lang::EventObject const & a_source) override;
			};
			
			ResultInformationListener::ResultInformationListener () : WeakComponentImplHelper1 (i_mutex) {
			}
			
			ResultInformationListener::~ResultInformationListener () {
			}
			
			void ResultInformationListener::dispatchFinished (DispatchResultEvent const & a_dispatchResultEvent) {
				cout << "### The dispatch execution is finished." << endl << flush;
			}
			
			void ResultInformationListener::disposing (::com::sun::star::lang::EventObject const & a_source) {
			}
			
			class RelatedInformationListener : public WeakComponentImplHelper1 <XStatusListener> {
				protected:
					Mutex i_mutex;
				public:
					RelatedInformationListener ();
					virtual ~RelatedInformationListener ();
					virtual void SAL_CALL statusChanged (FeatureStateEvent const & a_featureStateEvent) override;
					virtual void SAL_CALL disposing (::com::sun::star::lang::EventObject const & a_source) override;
			};
			
			RelatedInformationListener::RelatedInformationListener () : WeakComponentImplHelper1 (i_mutex) {
			}
			
			RelatedInformationListener::~RelatedInformationListener () {
			}
			
			void RelatedInformationListener ::statusChanged (FeatureStateEvent const & a_featureStateEvent) {
				cout << "### A dispatch execution related information piece is offered." << endl << flush;
			}
			
			void RelatedInformationListener::disposing (::com::sun::star::lang::EventObject const & a_source) {
			}
			
						Reference <ResultInformationListener> l_resultInformationListener (new ResultInformationListener ());
						Reference <RelatedInformationListener> l_relatedInformationListener (new RelatedInformationListener ());

Objector 21B
「WeakComponentImplHelper1」?「Mutex」?

Hypothesizer 7
ああ、それらの使用法にはここでは立ち入りませんが、クラスをUNOコンポーネントにするには、そうしたコーディングが必要なのです。

Objector 21B
「DispatchResultEvent」?「EventObject」?「FeatureStateEvent」?

Hypothesizer 7
それらは、UNO APIドキュメント('::com::sun::star::frame::DispatchResultEvent'は、ここ、'::com::sun::star::lang::EventObject'は、ここ、'::com::sun::star::frame::FeatureStateEvent'は、ここ)で調べてください。

Objector 21B
ふーん...

Hypothesizer 7
フレームUNOオブジェクトの'::com::sun::star::frame::XDispatchProvider' UNOプロキシを取得しますが、方法は、前セクションで説明したのと同じです。

次に、'::com::sun::star::util::URL'オブジェクト(UNOオブジェクトではない)を以下のようにセットアップします。

@C++ ソースコード
#include <com/sun/star/util/URL.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>

using namespace ::com::sun::star::util;

						::com::sun::star::util::URL l_urlInURL;
						l_urlInURL.Complete = UnoExtendedStringHandler::getOustring (".uno:GoToCell");
						Reference <XURLTransformer> l_urlTransformerInXURLTransformer (l_unoObjectsContext->getServiceManager ()->createInstanceWithContext (UnoExtendedStringHandler::getOustring ("com.sun.star.util.URLTransformer"), l_unoObjectsContext), UNO_QUERY);
						l_urlTransformerInXURLTransformer->parseStrict (l_urlInURL);

Objector 21B
...前セクションの方法では、URL文字列をただ指定した。今度は、なぜ、'::com::sun::star::util::URL'オブジェクトをセットアップしなければいけないの?

Hypothesizer 7
ヘルパーは、'::com::sun::star::util::URL'オブジェクトをセットアップする作業を、ヘルパーとしての責務として引き受けたのでしょう。しかし、私たちが'::com::sun::star::util::URL'オブジェクトをセットアップするほうが、より効率的であり得ます、URLトランスフォーマーUNOオブジェクトおよび'::com::sun::star::util::URL'オブジェクトを最大限に再利用することによって。

Objector 21B
ああ、それはあり得る。

Hypothesizer 7
とにかく、次に、ディスパッチャーUNOオブジェクトの'com.sun.star.frame.XNotifyingDispatch' UNOプロキシを以下のように取得します。

@C++ ソースコード
#include <com/sun/star/frame/XNotifyingDispatch.hpp>

						Reference <XNotifyingDispatch> l_dispatcherInXNotifyingDispatch (l_frameInXDispatchProvider->queryDispatch (l_urlInURL, UnoExtendedStringHandler::getOustring ("_self"), (short) -1), UNO_QUERY);

Objector 21B
1つのディスパッチャーをいくつかのURLに再利用することはできないの?

Hypothesizer 7
できる可能性はありますが、ある特定のディスパッチャーが他のどのURL群を処理してくれるかを私たちは通常知らないので、URL毎にディスパッチャーをクエリーしなければならないことに通常なります。

Objector 21B
「_self」と「-1」は、前セクションにおけるものと同じ意味なんでしょう?

Hypothesizer 7
はい、そうです。

次に、関連情報リスナー(複数可能)をディスパッチャーに登録し、最後に、私たちのコマンドをディスパッチャーに送ります、以下のように。

@C++ ソースコード
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>

using namespace ::com::sun::star::beans;

						l_dispatcherInXNotifyingDispatch->addStatusListener (l_relatedInformationListener, l_urlInURL);
						Sequence <PropertyValue> l_dispatchArgumentPropertiesSequence (2);
						l_dispatchArgumentPropertiesSequence [0].Name = UnoExtendedStringHandler::getOustring ("SynchronMode");
						l_dispatchArgumentPropertiesSequence [0].Value = Any (true);
						l_dispatchArgumentPropertiesSequence [1].Name = UnoExtendedStringHandler::getOustring ("ToPoint");
						l_dispatchArgumentPropertiesSequence [1].Value = Any (UnoExtendedStringHandler::getOustring ("$B$2"));
						l_dispatcherInXNotifyingDispatch->dispatchWithNotification (l_urlInURL, l_dispatchArgumentPropertiesSequence, l_resultInformationListener);
						l_dispatcherInXNotifyingDispatch->removeStatusListener (l_relatedInformationListener, l_urlInURL);

Objector 21B
'dispatchWithNotification'は非同期みたいね。

Hypothesizer 7
基本的にはそうです。'SynchronMode'引数を指定することで私はそれを同期にしましたが。

Objector 12B
ふーん...、確かに、それを非同期にしたい場合なんて想像できないわ。

Hypothesizer 7
'statusChanged'メソッドは複数回呼ばれる可能性があり(または全然呼ばれないかもしれない)、'dispatchFinished'メソッドは最後に一度だけ呼ばれます。

私の各種サンプル(コンソールC++ UNOクライアントファイルコンバーター, など)で使用されている私のUNOユーティリティプロジェクト、'unoUtilitiesToBeDisclosed'では、UNOドキュメントラッパークラスである'::theBiasPlanet::unoUtilities::documentsHandling::UnoDocument'が両リスナーインターフェースを実装しており、'dispatch'というメソッドも持っています。このメソッドは、UNOディスパッチコマンドを実行し、結果情報および関連情報をリターン値にパックして戻します。

Objector 21B
...'dispatch'メソッドの第1引数のその'UnoDispatchSlotsConstantsGroup::BaseDispatchSlot a_dispatchSlot'というのは何なの?

Hypothesizer 7
それは、実行しようとするUNOディスパッチコマンドのURLと引数名群を保持しているオブジェクトです。実は、'::theBiasPlanet::unoUtilities::constantsGroups::UnoDispatchSlotsConstantsGroup'クラスがそうしたオブジェクトたちを保持しています。そうした情報は、各プログラムに文字列リテラルとして散らばらせるよりも1箇所においたほうが望ましいだろうと思いました。


3: 結びとその先


Hypothesizer 7
これで、C++でUNOディスパッチコマンドを実行してその結果情報および関連情報を取得する方法を知りました。

もちろん、UNOディスパッチコマンドは、他のプログラミング言語(Java、Microsoft .NET Framework(C#およびVisual Basic.NET)、Python、LibreOffice BasicまたはApache OpenOffice Basic、BeanShell、JavaScript)で実行することもできます。Javaでそうする方法をある以前の記事で学びましたし、いくつかの他のプログラミング言語でそうする方法を以降の記事群(C#用Python用LibreOfficeまたはApache OpenOffice Basic用)で学びます。

LibreOffice基盤のためのUNOディスパッチコマンドの未完の(今のところ)リストとリストされたコマンドの仕様説明および LibreOffice CalcのためのUNOディスパッチコマンドの未完の(今のところ)リストとリストされたコマンドの仕様説明があります。

UNOディスパッチコマンドを実行するのは、LibreOfficeまたはApache OpenOfficeのインスタンスを操作する1つの方法ですが、LibreOfficeまたはApache OpenOfficeのインスタンスの操作で私たちができることの全てをカバーしているわけではありません: UNOオブジェクトを直接操作することでもっと多くのことを行なえます(本シリーズの目次を御参照ください)。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>