LibreOffice5(37)bootstrap()で起動したLibreOfficeをエラーなく終了させる

LibreOffice 5.3 SDK - Developer's Guide ExamplesのJavaの例のTestJavaComponentを実行したあとLibreofficeが起動できなくなりました。TestJavaComponentをPythonで書き直しても一緒の結果でした。

前の関連記事:LibreOffice5(36)UNOコンポーネントのJavaの例:TestJavaComponent


TestJavaComponentを実行したあとLibreOfficeが起動できなくなる


sdk/examples/DevelopersGuide/Components/JavaComponentにあるJavaの例をコンパイルしてできたJavaComponent.oxtをLibreOfficeの拡張機能マネージャーに登録してLibreOfficeを終了したあと、make TestJavaComponent.runすると問題なく動きました。
 ************************************************************************
 *
 * SDK environment is prepared for Linux
 *
 * SDK = /opt/libreoffice5.2/sdk
 * Office = /opt/libreoffice5.2/sdk/..
 * Make = /usr/bin
 * Zip = /usr/bin
 * cat = /bin
 * sed = /bin
 * C++ Compiler = /usr/bin
 * Java = /usr
 * SDK Output directory = /home/pq/libreoffice5.2_sdk
 * Auto deployment = NO
 *
 ************************************************************************

pq@pq-VirtualBox:/opt/libreoffice5.2/sdk/examples/DevelopersGuide$ cd Components/JavaComponent
pq@pq-VirtualBox:/opt/libreoffice5.2/sdk/examples/DevelopersGuide/Components/JavaComponent$ make TestJavaComponent.run
"/usr/bin/java"  -Dcom.sun.star.lib.loader.unopath="/opt/libreoffice5.2/sdk/../program" -jar /home/pq/libreoffice5.2_sdk/LINUXexample.out/class/JavaComponent/TestJavaComponent.jar
Connected to a running office ...
Using remote servicemanager
CO> Listening for transport dt_socket at address: 8000
Hello World!
問題はこのあとです。

LibreOfficeのランチャをクリックしても何も反応がありません。

原因はLibreOfficeがバッググラウンドで動いたままだからです。

豆ボタン→システムツール→タスクマネージャ。

コマンドの欄を探すとsoffice.binがあります。

これを選択して右クリック→終了、で終わらすとLibreOfficeのランチャで起動できるようになります。

だけど毎回こんなことはやってられませんので、この原因と対策を考えました。

TestJavaComponent.javaを読むとSystem.exit(0);で終わっていますがこれはTestJavaComponent.javaを終わらせているだけです。

TestJavaComponent.javaはLibreOfficeはBootstrap (Java UNO Runtime Reference)で起動していますが、終了はしていません。

Office Development examplesにあるTerminationTestでも同じ結果です。

ということでTestJavaComponent.javaを終了する前にLibreOfficeのプロセスを終了させることを考えます。

XDesktopインターフェイスのterminate()で終了するとエラーがでる


TestJavaComponent.javaをPythonで書き直して、officehelper.bootstrap()でLibreOfficeを起動させてみても同じ結果です。
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
desktop.terminate()
最後にterminate()を実行してみました。
Connected to a running office ...
Using remote servicemanager
Listening for transport dt_socket at address: 8000
Hello World!
Traceback (most recent call last):
  File "/home/pq/.config/libreoffice/4/user/Scripts/python/TestPythonComponent/TestPythonComponent/TestPythonComponent.py", line 25, in <module>
    desktop.terminate()
__main__.DisposedException: Binary URP bridge disposed during call
DisposedException例外が発生していますが、LibreOfficeは終了できています。

LibreOffice5(1)officehelper.bootstrap()を使うでみたようにofficehelper.pyはLibreOffice(5)PythonでLibreOfficeが動く仕組み:UNOのオートメーションで動いており、PythonプロセスがまだLibreOfficeプロセスに接続しているのに先にLibreOfficeを終了してしまっているために発生している例外です。
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
try:
    terminated = desktop.terminate()  # LibreOfficeをデスクトップに展開していない時はエラーになる。
    if terminated:
        print("The Office has been terminated.")  # 未保存のドキュメントがないとき。
    else:
        print("The Office is still running. Someone else prevents termination.")  # 未保存のドキュメントがある時
except:
    pass
try文を導入するとエラーが出力されなくなりますが、エラーが起こっていることには変わりありません。

LibreOfficeのウィンドウを表示したままこのスクリプトを実行したときは5行目または7行目のテキストがちゃんと出力されます。

つまり問題はbootstrap()でウィンドウを表示させずに起動したときに起こるわけです。

LibreOffice: DisposedException Exception Referenceを読むとXComponentインターフェイスを実装するように書いてありますが、この記事の下でやっているように実装方法がわかりませんでしたので他の方法を考えました。

デスクトップをterminate()する前にドキュメントをロードして解決


XComponentインターフェイスを実装する方法がよくわからなかったので、XComponentインターフェイスを実装しているUNOオブジェクトを予め開いてからデスクトップをterminate()することにしました。
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
from com.sun.star.beans import PropertyValue
prop = PropertyValue(Name="Hidden",Value=True)
desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, (prop,))  # バックグラウンドでWriterのドキュメントを開く。
terminated = desktop.terminate()  # LibreOfficeをデスクトップに展開していない時はエラーになる。
if terminated:
    print("The Office has been terminated.")  # 未保存のドキュメントがないとき。
else:
    print("The Office is still running. Someone else prevents termination.")  # 未保存のドキュメントがあってキャンセルボタンが押された時。
4行目でWriterのドキュメントをバックグラウンドでロードしています。

これでエラーなくLibreOfficeのプロセスを終了させることができました。

起動しているLibreOfficeに未保存なドキュメントがないときは即座にLibreOfficeが終了します。

未保存のドキュメントがあるときは、保存するか、保存しないか、キャンセルするかというダイアログがでてきます。

キャンセルと選択するとThe Office is still running. Someone else prevents termination.というメッセージを出力します。

XComponentインターフェイスを継承してみる


とりあえずPython UNO ComponentにしているPythonのクラスでcom.sun.star.lang.XComponentを継承するとunoinspではXComponentを備えているようにみえます。

pyuno object
├─.test.SomethingB
│   └─.test.XSomethingB
│         │   string  methodTwo( [in] string value)
│         └─.uno.XInterface
│                     void  acquire()
│                      any  queryInterface( [in] type aType)
│                     void  release()
├─.lang.XComponent
│         void  addEventListener( [in] .lang.XEventListener xListener)
│         void  dispose()
│         void  removeEventListener( [in] .lang.XEventListener aListener)
├─.lang.XServiceInfo
│           string  getImplementationName()
│         [string]  getSupportedServiceNames()
│          boolean  supportsService( [in] string ServiceName)
└─.lang.XTypeProvider
            [byte]  getImplementationId()
            [type]  getTypes()


しかし、そのメソッドを使おうとすると、メソッドがないと言われて実行できません。
com.sun.star.uno.RuntimeException         Traceback (most recent call last)
<ipython-input-5-eb746f80dd8c> in <module>()
----> 1 obj.dispose()

com.sun.star.uno.RuntimeException: <class 'AttributeError'>: 
'TestComponentB' object has no attribute 'dispose', traceback follows
no traceback available
XComponentの実装の仕方の例を見つけられなかったので、idlで継承してみました。

XSomethingB.idlファイルを次のように書き換えました。
 #ifndef INCLUDED_COM_SUN_STAR_TEST_XSOMETHINGB_IDL
#define INCLUDED_COM_SUN_STAR_TEST_XSOMETHINGB_IDL

#include <com/sun/star/lang/XComponent.idl>

module com {  module sun {  module star {  module test {
    interface XSomethingB {
        string methodTwo([in]string value);
        interface com::sun::star::lang::XComponent;
    };
   
}; }; }; };

#endif 
XSomethingBインターフェイスにXComponentインターフェイスを継承させました。

XInterfaceインターフェイスはどのインターフェイスも継承しているのでXComponentインターフェイスを継承すればXInterfaceインターフェイスを継承する必要はありません。

でもXInterfaceインターフェイスはincludeするだけで継承されましたが、XComponentインターフェイスは上の9行目のようにちゃんと書かないとだめでした。

pyuno object
├─.test.SomethingB
│   └─.test.XSomethingB
│         │   string  methodTwo( [in] string value)
│         └─.lang.XComponent
│               │   void  addEventListener( [in] .lang.XEventListener xListener)
│               │   void  dispose()
│               │   void  removeEventListener( [in] .lang.XEventListener aListener)
│               └─.uno.XInterface
│                           void  acquire()
│                            any  queryInterface( [in] type aType)
│                           void  release()
├─.lang.XServiceInfo
│           string  getImplementationName()
│         [string]  getSupportedServiceNames()
│          boolean  supportsService( [in] string ServiceName)
└─.lang.XTypeProvider
            [byte]  getImplementationId()
            [type]  getTypes()


unoinspで見るとidlで書いた通りXSomethingBインターフェイスがXComponentインターフェイスを継承しています。

しかしやっぱり実装なしではdispose()メソッドが実行できませんでした。

LibreOffice(65)Writing UNO componentsのThumbs Exampleその1でLibreOffice APIにあるインターフェイスを継承しているときはそれは実装も継承していると考えていたのですが、そうではなくて単にUNOIDLを利用していただけとようやく理解出来ました。

 Thumbs ExampleのImageShrink.javaを読んでみるとUNOIDLのインターフェイスを継承したメソッドもちゃんと実装していました。

LibreOffice5(4)unoinsp.pyの修正で出力されていなかったインターフェイスはidlファイルでは継承せず、UNOオブジェクトをインスタンス化するクラスで継承しているインターフェイスということもわかりました。

ディスパッチコマンドでも解決せず


LibreOffice(35)マクロの記録をPythonに翻訳1:リストとタプル
で使ったディスパッチフレームワークの .uno:Quitコマンドを送信してみました。
dispatcher = smgr.createInstanceWithContext("com.sun.star.frame.DispatchHelper",ctx)
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
dispatcher.executeDispatch(desktop, ".uno:Quit", "", 0, tuple()) 
これはDisposedException例外が発生する上にLibreOfficeがクラッシュして再起動してしまいました。

結局デスクトップをterminate()する前にドキュメントをこっそりロードするのが無難な方法だと思います。

参考にしたサイト


LibreOffice 5.3 SDK - Developer's Guide Examples
BootstrapでLibreOfficeを起動する例の実行後にLibreOfficeのタスクが残ってしまいます。

Bootstrap (Java UNO Runtime Reference)
BootstrapのJava版の解説。Pythonではofficehelper.pyを使います。

LibreOffice: DisposedException Exception Reference
リモートプロセスからLibreOfficeをterminate()するとこの例外が発生しました。

次の関連記事:LibreOffice5(38)Java Component ExampleをPythonに翻訳する

PR

0 件のコメント:

コメントを投稿