MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - D-Busのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - D-Bus
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == D-Busは、オープンソースのプロセス間通信(IPC:Inter Process Communication)機構であり、freedesktop.orgプロジェクトの一部である。<br> IPCとは、1台のコンピュータ上で動作する複数のプログラムの間で情報を交換するシステムのことである。<br> <br> IPCには、パイプ、名前付きパイプ、シグナル、共有メモリ、Unixソケット、ループバックソケット等がある。<br> D-Busもリンク層はUnixソケットで動作しているが、手順とフォーマット(プレゼンテーション層)が既定されていることが、「生の」Unixソケットとは異なる。<br> <br> 開発当初はGNOME等のGUIの制御を目的としていが、今日では、GUIに限らず幅広いソフトウェアで使用されており、<br> デスクトップの通知、メディアプレーヤー制御、XDGポータル等、多くのfreedesktop.org標準がD-Busをベースに構築されている。<br> <br> IPCとは、あるプロセスから別のプロセスへ情報を取得する方法を説明するために使用することができる。<br> これは、データの交換、メソッドの呼び出し、イベントのリスニング等がある。<br> <br> * デスクトップにおけるIPCの使用例 ** スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)<br> ** 集中型サービスへのアクセスの提供 ** 協調型ソフトウェアの複数のインスタンス間の調整 <br> * D-Busの使用例 ** freedesktop.orgのnotification仕様 **: これは、ソフトウェアは通知を中央サーバ(Plasma等)に送信して、中央サーバは通知を表示して、通知が閉じられたりアクションが実行されたりといったイベントを送り返すものである。 <br> * IPCの他の使用例 ** ユニークなソフトウェアのサポート **: これは、ソフトウェアの起動時に、まず、同じソフトウェアの他の実行中のインスタンスを確認して、 **: もし存在すれば、IPCを介して実行中のインスタンスにメッセージを送信して、自分自身を表示して終了させる。 <br> D-Busは、言語やツールキットに囚われないため、あらゆるプロバイダのソフトウェアやサービスが相互作用することができる。<br> デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。<br> <br> また、Qtは、D-Busと対話するためのクラスとツールのセットを提供している。<br> <br> D-Busの詳細を知りたい場合は、[[設定 - D-Bus]]のページを参照すること。<br> <br><br> == QtとD-Bus == ==== Qt D-Busライブラリ ==== Qt D-Busライブラリは、D-Busプロトコルを使用してプロセス間通信を行うUnix向けライブラリであり、D-Busの基本APIをカプセル化して実装したものである。<br> これは、Qtのシグナル・スロット機構で拡張されたインターフェイスを提供する。<br> <br> Qt D-Busライブラリを使用するには、<code>QtDBus</code>をインクルードする必要がある。<br> <syntaxhighlight lang="c++"> #include <QtDBus> </syntaxhighlight> <br> Qtプロジェクトファイルを使用する場合は、変数<code>QT</code>に<code>dbus</code>オプションを追加する必要がある。<br> QT += dbus <br> ==== QtとD-Busのデータ型 ==== QtとD-Busは、<code>QDBusArgument</code>クラスを通して、ネイティブな型をサポートしている。<br> また、<code>QDBusArgument</code>クラスは、ネイティブな型の他に非ネイティブ型である<code>QStringList</code>クラスと<code>QByteArray</code>クラスもサポートする。<br> <center> {| class="wikitable" | style="background-color:#fefefe;" |- ! style="background-color:#66CCFF;" | Qtのデータ型 ! style="background-color:#66CCFF;" | D-Busのデータ型 |- | uchar || BYTE |- | bool || BOOLEAN |- | short || INT16 |- | ushort || UINT16 |- | int || INT32 |- | uint || UINT32 |- | qlonglong || INT64 |- | qulonglong || UNIT64 |- | double || DOUBLE |- | QString || STRING |- | QDBusVariant || VARIANT |- | QDBusObjectPath || OBJECT_PATH |- | QDBusSignature || SIGNATURE |} </center> <br> * コンポジットタイプ *: D-Busでは、ネイティブの型を集約した3つの複合型を規定しており、<code>ARRAY</code>、<code>STRUCT</code>、<code>マップ / ディクショナリ</code>が存在する。 *: <code>ARRAY</code>は、0個以上の同一の要素からなる集合体である。 *: <code>STRUCT</code>は、異なる型の固定数の要素で構成されるコレクションである。 *: <code>マップ / ディクショナリ</code>は、要素のペアの<code>ARRAY</code>である。マップは0個以上の要素を持つことができる。 <br> * 拡張された型システム *: Qt D-Busライブラリにおいて、カスタムデータ型を使用する場合、任意のクラスに<code>Q_DECLARE_METATYPE()</code>をQtメタタイプとして宣言して、 *: <code>qDBusRegisterMetaType</code>関数で登録する必要がある。 *: ストリーム演算子は、登録システムによって自動的に検出されます。 *: <br> *: Qt D-Busライブラリは、Qtコンテナクラスがストリーム演算子関数を実装せずに、<code>QMap</code>や<code>QList</code>のような配列やマップを使用するためのテンプレート特殊化を提供している。 *: それ以外の型では、フローオペレータが実装を表示する必要がある。 <br> ==== QDBusMessageクラス ==== <code>QDBusMessage</code>クラスは、D-Busが送信または受信するメッセージを表す。<br> <br> <code>QDBusMessage</code>クラスは、バス上の4種類のメッセージタイプから1種類を選択する。<br> 4種類のメッセージタイプを、以下に示す。<br> * メソッド呼び出し * メソッドの戻り値 * シグナルの送信 * エラーコード <br> メッセージは、<code>createError</code>メソッド、<code>createMethodCall</code>メソッド、<code>createSignal</code>メソッドを使用して作成することができる。(全て<code>static</code>メソッド)<br> <code>QDBusConnection</code>クラスの<code>send</code>メソッド(<code>static</code>メソッド)を使用して、メッセージを送信する。<br> <br> ==== QDBusConnectionクラス ==== <code>QDBusConnection</code>クラスは、D-Busへの接続を表しており、D-Busセッションの出発点となる。<br> <br> <code>QDBusConnection</code>クラスを通じてD-Busへ接続することにより、<br> リモートオブジェクトやインターフェースへのアクセス、リモートシグナルをローカルスロット関数へ接続、オブジェクトの登録等を行うことができる。<br> <br> D-Bus接続は、<code>connectToBus</code>メソッドを通して行われて、バスサーバへの接続を作成および初期化した後、D-Bus接続名を接続に関連付ける。<br> <br> 切断する場合は、<code>disconnectFromBus</code>メソッドを使用する。<br> D-Bus接続を切断した後、<code>connectToBus</code>メソッドは接続を再構築しないため、新しい<code>QDBusConnection</code>クラスのインスタンスを生成する必要がある。<br> <br> 最もよく使用される2種類のバスタイプとして、<code>sessionBus</code>メソッドと<code>systemBus</code>メソッドがあり、それぞれセッションバスとシステムバスへの接続を作成する。<br> これは、最初に使われるときに開かれ、<code>QCoreApplication</code>クラスのデストラクタが呼ばれる時に切断される。<br> <br> また、D-Busは、バスサービスを使用しないポイントツーポイント通信をサポートしており、2つのソフトウェアが直接通信することにより、メッセージを送受信することができる。<br> これは、<code>connectToBus</code>メソッドにアドレスを渡すことで可能となる。<br> <br> * QDBusConnection connectToBus(BusType type, const QString &name) *: 第1引数に指定した接続を開いて、第2引数で指定した接続名と関連付ける。 *: この接続に関連付けられた<code>QDBusConnection</code>クラスのインスタンスを返す。 <br> * QDBusConnection connectToBus(const QString &address, const QString &name) *: 第1引数で指定したプライベートバスを開いて、第2引数で指定した接続名と関連付ける。 *: この接続に関連付けられた<code>QDBusConnection</code>クラスのインスタンスを返す。 <br> * QDBusConnection connectToPeer(const QString &address, const QString &name) *: 第1引数で指定したポイントツーポイント接続を開いて、接続名を関連付ける。 *: この接続に関連付けられた<code>QDBusConnection</code>クラスのインスタンスを返す。 <br> * void disconnectFromBus(const QString &name) *: 引数で指定した名前のバス接続を閉じる。 <br> * void disconnectFromPeer(const QString &name) *: 引数で指定した名前のピア接続を閉じる。 <br> * QByteArray localMachineId() *: D-Busシステムが知っているローカルIDを返します。 <br> * QDBusConnection sender() *: シグナルを送信した接続を返す。 <br> * QDBusConnection sessionBus() *: セッションバスに対して開いた<code>QDBusConnection</code>クラスのインスタンスを返す。 <br> * QDBusConnection systemBus() *: システムバスに対して開いた<code>QDBusConnection</code>クラスのインスタンスを返す。 <br> * QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout = -1) const *: メッセージは、この接続を通じてメッセージを送信して、直ちにリターンする。 *: このメソッドは、メソッド呼び出しのみをサポートしており、レスポンスを追跡するために使用される<code>QDBusPendingCall</code>クラスのインスタンスを返す。 <br> * QDBusMessage call(const QDBusMessage & message, QDBus::CallMode mode = QDBus::Block, int timeout = -1 ) const *: メッセージは、この接続を通じて送信・ブロックして、応答を待つ。 <br> * bool registerObject(const QString &path, QObject *object, RegisterOptions options = ExportAdaptors) *: 第2引数で指定した現在のクラスのインスタンスを、第1引数に指定したQ-DBusオブジェクト名に登録する。 *: 第3引数には、D-Busに公開されるオブジェクトの数を指定する。 *: 登録に成功した場合は、<code>true</code>を返す。 <br> * bool registerService(const QString &serviceName) *: 第1引数に指定したバス名(D-Busサービス名)に登録する。 *: 登録に成功した場合は<code>true</code>、指定したバス名(D-Busサービス名)が他のソフトウェアで既に登録されている場合は登録に失敗して<code>false</code>を返す。 <br> ==== qdbuscpp2xmlコマンド ==== <code>qdbuscpp2xml</code>コマンドは、<code>QObject</code>クラスを継承したクラスのヘッダファイル等をパースして、D-Busインターフェースファイルを生成する。<br> スロットの引数が<code>const</code>で宣言されている場合は入力、非<code>const</code>の場合は出力とみなされることがある。<br> qdbuscpp2xml <入力オプション> <QObjectクラスを継承したクラスを定義したヘッダファイル> <出力オプション> <生成するD-Busインターフェースファイル名> 例. qdbuscpp2xml -A SampleHelper.h -o org.qt.sample.xml <br> <code>qdbuscpp2xml</code>コマンドのオプションを、以下に示す。<br> * -p または -s または -m *: スクリプト化されたアトリビュート(<code>-p</code>オプション)、シグナル(<code>-s</code>オプション)、メソッド(スロット関数)(<code>-m</code>オプション)のみがパースされる。 * -P または -S または -M *: 全てのアトリビュート(<code>-P</code>オプション)、シグナル(<code>-S</code>オプション)、メソッド(スロット関数)(<code>-M</code>オプション)を解析する。 * -a *: スクリプト化された内容を全て出力する。(<code>-psm</code>オプションと等価) * -A *: 全てのコンテンツを出力する。(<code>-PSM</code>オプションと等価) * -o <生成するD-Busインターフェースファイル名> *: D-Busインターフェースファイルを生成する。 <br> 以下の例では、C++クラスからD-Busインターフェイスファイル (XMLファイル) を生成している。<br> <br> まず、インターフェイスを定義したC++クラスを作成する。<br> <syntaxhighlight lang="c++"> // ExampleObject.h #include <QObject> #include <QString> class ExampleObject : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.example.ExampleInterface") public slots: QString exampleMethod(const QString &input) { return "Hello, " + input; } }; </syntaxhighlight> <br> 次に、<code>qdbuscpp2xml</code>コマンドを実行して、D-Busインターフェースファイル (XMLファイル) を生成する。<br> 以下の例では、ExampleObject.hファイルからインターフェイスを読み取り、ExampleInterface.xmlファイルを生成している。<br> qdbuscpp2xml -m -s ExampleObject.h -o ExampleInterface.xml <br> 生成されたD-Busインターフェースファイル (XMLファイル) を以下に示す。<br> <syntaxhighlight lang="xml"> <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="com.example.ExampleInterface"> <method name="exampleMethod"> <arg direction="in" type="s" name="input"/> <arg direction="out" type="s" name="output"/> </method> </interface> </node> </syntaxhighlight> <br> ==== qdbusxml2cppコマンド ==== <code>qdbusxml2cpp</code>コマンドは、D-Busインターフェースファイルの定義に従い、C++のアダプターコードを生成する。<br> これは、<code>qdbusabstractadapter</code>と<code>QDBusAbstractInterface</code>を継承したプロセス通信サーバとクライアントの実装コードを自動生成する。<br> # mocファイルはインクルードしない場合 qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス> 例. qdbusxml2cpp -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml # mocファイルもインクルードする場合 qdbusxml2cpp -m -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス> 例. qdbusxml2cpp -m -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml # 各クラスに個別のインターフェースを宣言している場合は、各インターフェースごとにアダプターを生成する qdbusxml2cpp -m -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス> <D-Busインターフェース名> 例. qdbusxml2cpp -m -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml org.qt.policykit.examples2 <br> <code>qdbusxml2cpp</code>コマンドのオプションを、以下に示す。<br> * <code>-a <アダプターソースコードおよびヘッダのファイル名></code> または <code>-A <アダプターソースコードおよびヘッダのファイル名></code> *: 指定された名前でアダプターソースコードおよびヘッダを生成する。 * <code>-c <アダプターソースコードで使用するクラス名></code> または <code>-C <アダプターソースコードで使用するクラス名></code> *: アダプターソースコードで使用するクラス名を指定する。 * <code>-i <対象となるクラスを記述しているヘッダファイル名></code> または <code>-I <対象となるクラスを記述しているヘッダファイル名></code> *: アダプターソースコードに、<code>#include <対象となるクラスを記述しているヘッダファイル></code>を追加する。 * <code>-l <対象となるクラス名></code> または <code>-L <対象となるクラス名></code> *: アダプタソースコードを生成する時に使用するクラス名(親クラス)を指定する。 * <code>-m</code> *: アダプターソースコードに、<code>#include "<アダプターソースコード名.moc>"</code>ステートメントを追加する。 * <code>-N</code> *: 名前空間を使用しない。 * <code>-p <アダプターソースコードのファイル名></code> または <code>-P <アダプターソースコードのファイル名></code> *: アダプターソースコードのファイルへのプロキシコードを生成する。 <br> ==== サンプルコード 1 ==== 以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。<br> # セッションバスへの接続を取得する。 # セッションバスへのD-Busインターフェースを作成する。 # D-Busインターフェイス名でのサービス登録する。 # バス上に誰が存在しているかを確認する。 # pingリクエストを受信するために登録する。 # Pingメッセージの送信する。 # 受信したPingメッセージの表示する。 <br> 受信したPingメッセージの表示以外は、doItメソッドで行う。<br> Pingメッセージは、pingReceivedメソッドで処理される。<br> <br> プログラムを実行すると、セッションバス上に既に多くのサービスが表示されるが、"-- end --"の付近にPingリクエストに応答していることがわかる。<br> <br> <syntaxhighlight lang="c++"> #include <QDBusConnection> #include <QDBusConnectionInterface> #include <QTimer> #include "MyObject.h" MyObject::MyObject(QObject *parent) : QObject(parent) { QTimer::singleShot(10, this, &MyObject::doIt); } void MyObject::doIt() { /// [1] セッションバスへの接続を取得 QDBusConnection bus = QDBusConnection::sessionBus(); /// [2] セッションバスへD-Busインターフェースを作成 QDBusConnectionInterface *busIF = bus.interface(); /// [3] D-Busインターフェイス名でのサービス登録 QString ifName = "com.packt.bigproject"; busIF->registerService(ifName, QDBusConnectionInterface::ReplaceExistingService, QDBusConnectionInterface::AllowReplacement); /// [4] バス上に誰が存在しているかを確認 QDBusReply<QStringList> serviceNames = busIF->registeredServiceNames(); qDebug() << bus.name() << "knows the following Services:" << serviceNames.value(); /// [5] pingリクエストを受信するために登録 (QObject::connectメソッドと似ている) QString service = ""; QString path = ""; QString name = "ping"; bus.connect(service, path, ifName, name, this, SLOT(pingReceived(QString))); /// [6] Pingメッセージを送信 QDBusMessage msg = QDBusMessage::createSignal("/", ifName, name); msg << "Hello World!"; bus.send(msg); /// 5秒以内にもう1度行う QTimer::singleShot(5000, this, &MyObject::doIt); } void MyObject::pingReceived(QString msg) { /// [8] 受信したPingメッセージの表示 qDebug() << __FUNCTION__ << "Ping:" << msg; } </syntaxhighlight> <br> サンプルコードの詳細を知りたい場合は、以下に示すGithubを参照すること。<br> https://github.com/PacktPublishing/Hands-On-Embedded-Programming-with-Qt/blob/master/Chapter10/DBusBruteForce/MyObject.cpp<br> <br> ==== サンプルコード 2 ==== 以下の例では、Firewalldにおいて、以下に示すようなコマンドと同等のものをD-Busを使用して実行している。<br> sudo firewall-cmd --permanent --zone=<ゾーン名> --add-port=<ポート番号>/tcp sudo firewall-cmd --reload <br> 以下に示すサンプルコードを実行するには、適切な権限 (root権限) が必要となることに注意する。<br> <syntaxhighlight lang="c++"> #include <QDBusConnection> #include <QDBusInterface> #include <QDBusReply> // 開放するポートを指定する。 bool addPort(const QString& zone, int port, const QString &protocol) { QDBusReply<void> reply = interface.call("addPort", zone, port, protocol); return reply.isValid(); } // 変更を永続化する bool makePermament() { QDBusReply<void> reply = interface.call("runtimeToPermanent"); return reply.isValid(); } // Firewalldを再読み込みする bool reloadFirewall() { QDBusReply<void> reply = interface.call("reload"); return reply.isValid(); } int main() { // ...略 // FirewalldのD-Busインターフェースに接続する QDBusConnection bus = QDBusConnection::systemBus(); QDBusInterface interface("org.fedoraproject.FirewallD1", "/org/fedoraproject/FirewallD1", "org.fedoraproject.FirewallD1", bus); // 複数のゾーンに対して操作を行うことも可能 QStringList zones = {"public", "internal", "work"}; for (const auto& zone : zones) { if (addPort(zone, 80, "tcp")) { qDebug() << "Port 80 added to" << zone << "zone successfully"; } else { qDebug() << "Failed to add port 80 to" << zone << "zone"; } } if (makePermament()) { qDebug() << "Changes made permanent"; } if (reloadFirewall()) { qDebug() << "Firewall reloaded"; } // ...略 return 0; } </syntaxhighlight> <br><br> == Qtプロジェクト == Qtプロジェクトにおいて、Qt Creator付属の<code>qdbusxml2cpp</code>コマンドを実行することにより、D-Busインターフェースのアダプタクラスを生成する。<br> 生成されるファイルは、D-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルである。<br> # mocファイルはインクルードしない場合 qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス> 例. qdbusxml2cpp -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.dbus.interface.examples.xml # mocファイルもインクルードする場合 qdbusxml2cpp -m -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス> 例. qdbusxml2cpp -m -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.dbus.interface.examples.xml <br> 生成されたファイルをQtプロジェクトに指定およびインクルードする。<br> <br> または、Qtプロジェクトファイルにおいて、<code>qdbusxml2cpp</code>コマンドを自動実行する設定を記述してもよい。<br> system(qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>) <br><br> == CMakeプロジェクト == CMakeプロジェクトにおいて、<code>qt_add_dbus_adaptor</code>コマンドを指定することにより、D-Busインターフェースのアダプタクラスを生成することができる。<br> <code>qt_add_dbus_adaptor</code>コマンドは、Qt D-Bus XMLコンパイラ (qdbusxml2cpp) のアダプタモードでの呼び出しを設定する。<br> <br> 第2引数で指定したD-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルを生成する。<br> 生成されたファイルのパスが第1引数に追加される。<br> <br> 第3引数には、D-Busインターフェースに基づく親クラスのヘッダファイル名を指定する。<br> 生成されるアダプタを実装したソースコードには、<code>#include "<第3引数で指定したヘッダファイル名>"</code>としてインクルードされる。<br> <br> 第4引数には、第3引数のクラス名(D-Busインターフェースに基づく親クラス名)を指定する。(省略可能)<br> 第5引数には、生成するヘッダファイル名(拡張子.hは不要)を指定する。(省略可能)<br> 第6引数には、生成するアダプタのクラス名を指定する。(省略可能)<br> <br> 第4引数から第6引数までを省略する場合、親クラス名、ヘッダファイル名、クラス名は、第3引数の指定値から自動的に生成される。<br> <syntaxhighlight lang="cmake"> qt_add_dbus_adaptor( <任意の変数名> # 生成されるソースファイル名を指定 例: SampleAdaptor.cpp SampleAdaptor.h <D-Busインタフェースファイル> # XML形式 <親クラスのヘッダファイル名> <第3引数のクラス名> # 省略可能 <生成するヘッダファイル名> # 省略可能 # 拡張子.hは不要 <生成するアダプタのクラス名> # 省略可能 ) </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - D-Bus
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse