ホーム

OFF-SOFT.net

OFF-SOFT.net

ウェブやソフトウェアに関するサポート&情報サイトです。サイト構築からソフトウェアの作成、利用まであなたの助けになるかも・・・・しれません。たぶん・・。

Qt (7) QtでWindows Serviceを作る

公開日| 2009年05月30日 | 3 のコメントがあります。
概要 :
 Qtで、WindowsのServiceを作成することはできるでしょうか。
 Unix系のdeamonプロセスとWindowsのServiceをラップしたクラスが提供されています。そのクラスを用いることで簡単に作成することができます。

 ただ、標準のQt SDKの中には、含まれていません。 以降のアドレスから、ダウンロードし、コンパイル(ビルド)作業が必要になります。

 この記事では、VC++ 2008 Expressでの環境作成とサンプルの動作概要について簡単に説明します。


ダウンロード: http://www.qtsoftware.com/products/appdev/add-on-products/catalog/4/Utilities/qtservice
※商用とGPL/LPGL用がありますので、間違いのないようダウンロードします。

GPL/LPGL用は、以下のコメントがあるところからダウンロードします。
Download Open Source Edition (LGPL):

コンパイルしましょう
ダウンロードしたZIPファイルを解凍します
オープンソース版であれば、以下のような名前のZIPファイルをダウンロードします。
(バージョンによって多少は異なります。)

1
qtservice-2.6-opensource.zip

ここでは、例として、先のZIPファイルを以下のディレクトリへ解凍したものとして、話を進めます。
C:\Qt\Qt\service

解凍したら、以下のようなディレクトリが作成されます。

1
2
3
4
5
6
C:\Qt\Qt\service\buildlib
C:\Qt\Qt\service\doc
C:\Qt\Qt\service\examples
C:\Qt\Qt\service\lib
C:\Qt\Qt\service\src
C:\Qt\Qt\service\tmp


コンパイル(ビルド)します
  1. VC++ 2008 Expressのスタートアップメニューの中の"Visula Studio 2008 コマンドプロンプト"を開きます

  2. コマンドプロンプトから、解凍先ディレクトリ へカレントディレクトリを移動します
    1
    2
    
    C:\Program Files\Microsoft Visual Studio 9.0\VC> cd C:\Qt\Qt\service
    C:\Qt\Qt\service> 
  3. ライブラリのビルド方法を指定します
    -- ここでは、DLL版を作成します。
    1
    2
    
    C:\Qt\Qt\service> configure -library
    C:\Qt\Qt\service> 
    -- static ライブラリを作成する場合は、パラメータなしでconfig.batを起動します。
    -- ライセンスの確認メッセージが表示されますので、それに答えて作業を進めます。

  4. ライブラリをコンパイル(ビルド)します
    1
    2
    3
    4
    5
    6
    
    C:\Qt\Qt\service> qmake
    C:\Qt\Qt\service> nmake
       :
       :
       :
    C:\Qt\Qt\service> 
    -- 必ず、ここで、qmakeを実施し、makeを起動します。(数分の作業です)


ここまで完了すると、デバッグ版、リリース版、サンプルが全てコンパイル(ビルド)されています。

開発環境を設定しましょう
[ マイコンピュータ ] - [ プロパティ ]で環境設定を行います。


環境変数名
PATH 環境変数のpathにLibのディレクトリを追加します。

例)
%path%;C:\Qt\Qt\service\.
-- C:\Qt\Qt\service\lib\ : Qt - Service Lib(DLL)のディレクトリ

ここで、設定する環境変数のpathは、システム環境変数のPATHへ設定します。 ユーザ環境変数のPATHでないことに注意します。

Qtの各ライブラリのパスについても同様です。Serviceを扱う場合、全てのQt関連のパスは、システム環境変数のPATHへ設定します。

設定を終えたら、OKボタンをクリックします。
システムの環境変数を変更していますので、システムをリブートします。


リブートを終えたら、サンプルを実行してみましょう。

サンプルを実行してみましょう
このService関連でのサンプルは、3つあります。

  • An Interactive Service
    -- デスクトップ上に画面を表示する簡単なサービスのサンプルです。
  • A simple HTTP Server
    -- HTTPのサーバの簡単なサービスのサンプルです。
  • A simple Service Controller
    -- サービスの登録/削除や実行/停止を行うサンプルです。

ここでは、An Interactive Service, A simple Service Controller を使ってみましょう。


説明の便宜上、同じディレクトリへ実行ファイルをコピーします。
1
2
3
C:\temp> copy C:\Qt\Qt\service\examples\controller\release\controller.exe .
C:\temp> copy C:\Qt\Qt\service\examples\interactive\release\interactive.exe .
C:\temp> 

controller.exeを使ってinteractive.exeをサービス登録(install)します。
1
2
3
4
5
6
C:\temp> controller.exe -i C:\temp\interactive.exe
 
The service Qt Interactive Service has been installed under: C:\temp\interactive.exe
The service was installed.
 
C:\temp> 

The service was installed.が出力されれば、OKです。
The service was not installed.では、エラーですので、入力などを確認してみてください。

また、コントロールパネルのサービスでも確認してみましょう。

上記のように、"Qt Interactive Service"という名前のサービスが確認できればOKです。

controller.exeを使ってinteractive.exeを実行します。
1
2
3
4
C:\temp> controller.exe "Qt Interactive Service" -s
The service "Qt Interactive Service" was started.
 
C:\temp> 

The service "Qt Interactive Service" was started.が出力されれば、OKです。
The service "Qt Interactive Service" was not started.では、エラーですので、入力などを確認してみてください。

また、デスクトップ左上に"Service"とういうラベルのみの画面が表示されたと思います。

この画面が表示されない場合は、何か環境に問題がありますので、再度、見直されることをお勧めします。

このサービスは、Vista, Windows 7では、開始できません。XP,2000などで確認してください。

controller.exeを使ってinteractive.exeを停止します。
1
2
3
4
C:\temp> controller.exe "Qt Interactive Service" -t
The service "Qt Interactive Service" was stopped.
 
C:\temp> 

The service "Qt Interactive Service" was stopped.が出力されれば、OKです。
The service "Qt Interactive Service" was not stopped.では、エラーですので、入力などを確認してみてください。

また、デスクトップ左上に"Service"とういうラベルのみの画面が消えたと思います。

controller.exeを使ってinteractive.exeをサービス削除(uninstall)します。
1
2
3
4
C:\temp> controller.exe "Qt Interactive Service" -u
The service "Qt Interactive Service" was uninstalled.
 
C:\temp> 

The service "Qt Interactive Service" was uninstalled.が出力されれば、OKです。
The service "Qt Interactive Service" was not uninstalled.では、エラーですので、入力などを確認してみてください。


サンプルのソースコードを見ても、サービスの登録に2、3行程度です。
サービス自体は、QtService<QApplication>を継承し、各メソッドを準備すれば、それほど、難しいものではありません。

Qtでサービスを作成する需要がどれほどあるのかわかりませんが、簡単であることは、間違いありません。
一度、試されるのも良いと思います。
簡単なサンプルの解説
An Interactive Service, A simple Service Controller のいずれのサンプルソースもmain.cppのみです。
[An Interactive Service]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class InteractiveService : public QtService<QApplication>
{
public:
    InteractiveService(int argc, char **argv);
    ~InteractiveService();
 
protected:
 
    void start();
    void stop();
    void pause();
    void resume();
    void processCommand(int code);
 
private:
    QLabel *gui;
};
 
InteractiveService::InteractiveService(int argc, char **argv)
    : QtService<QApplication>(argc, argv, "Qt Interactive Service"), gui(0)
{
    setServiceDescription("A Qt service with user interface.");
    setServiceFlags(QtServiceBase::CanBeSuspended);
}
 
InteractiveService::~InteractiveService()
{
}
 
void InteractiveService::start()
{
#if defined(Q_OS_WIN)
    if ((QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) &&
        (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA)) {
        logMessage( "Service GUI not allowed on Windows Vista. See the documentation for this example for more information.", QtServiceBase::Error );
        return;
    }
#endif
 
    qApp->setQuitOnLastWindowClosed(false);
 
    gui = new QLabel("Service", 0, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
    gui->move(QApplication::desktop()->availableGeometry().topLeft());
    gui->show();
}
 
void InteractiveService::stop()
{
    delete gui;
}
 
void InteractiveService::pause()
{
    if (gui)
	gui->hide();
}
 
void InteractiveService::resume()
{
    if (gui)
	gui->show();
}
 
void InteractiveService::processCommand(int code)
{
    gui->setText("Command code " + QString::number(code));
    gui->adjustSize();
}

ここでは、InteractiveServiceのクラスのみを抜粋しています。
mainルーチンでは、このクラスをnewして、exec()を起動しているだけです。

このクラスの特徴は、QtService<QApplication>の継承と以下のメソッドです。

  • void start();
    -- サービスが開始した時のこのメソッドが動きます。
  • void stop();
    -- サービスが停止した時のこのメソッドが動きます。
  • void pause();
    -- サービスが一時停止した時のこのメソッドが動きます。
  • void resume();
    -- サービスが再開した時のこのメソッドが動きます。
  • void processCommand(int code);
    -- サービスがユーザからのコマンドを受信した時のこのメソッドが動きます。

22行目あたりのコンストラクタで、
サービスの概要を登録(setServiceDescription),
サービスの機能の設定(setServiceFlags)-一時停止を可と設定している,
ぐらいでしょうか。

あとは、解説するほどのソースコードではないように思います。

[A simple Service Controller]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
int processArgs(int argc, char **argv)
{
    if (argc > 2) {
        QString arg1(argv[1]);
	if (arg1 == QLatin1String("-i") ||
            arg1 == QLatin1String("-install")) {
	    if (argc > 2) {
                QString account;
                QString password;
		QString path(argv[2]);
                if (argc > 3)
                    account = argv[3];
                if (argc > 4)
                    password = argv[4];
                printf("The service %s installed.\n",
                       (QtServiceController::install(path, account, password) ? "was" : "was not"));
                return 0;
            }
        } else {
            QString serviceName(argv[1]);
            QtServiceController controller(serviceName);
            QString option(argv[2]);
            if (option == QLatin1String("-u") ||
                option == QLatin1String("-uninstall")) {
                printf("The service \"%s\" %s uninstalled.\n",
                            controller.serviceName().toLatin1().constData(),
                            (controller.uninstall() ? "was" : "was not"));
                return 0;
            } else if (option == QLatin1String("-s") ||
                       option == QLatin1String("-start")) {
                QStringList args;
                for (int i = 3; i < argc; ++i)
                    args.append(QString::fromLocal8Bit(argv[i]));
                printf("The service \"%s\" %s started.\n",
                       controller.serviceName().toLatin1().constData(),
                            (controller.start(args) ? "was" : "was not"));
                return 0;
            } else if (option == QLatin1String("-t") ||
                       option == QLatin1String("-terminate")) {
                printf("The service \"%s\" %s stopped.\n",
                       controller.serviceName().toLatin1().constData(),
                       (controller.stop() ? "was" : "was not"));
                return 0;
            } else if (option == QLatin1String("-p") ||
                    option == QLatin1String("-pause")) {
                printf("The service \"%s\" %s paused.\n",
                       controller.serviceName().toLatin1().constData(),
                       (controller.pause() ? "was" : "was not"));
                return 0;
            } else if (option == QLatin1String("-r") ||
                       option == QLatin1String("-resume")) {
                printf("The service \"%s\" %s resumed.\n",
                       controller.serviceName().toLatin1().constData(),
                       (controller.resume() ? "was" : "was not"));
                return 0;
            } else if (option == QLatin1String("-c") ||
                       option == QLatin1String("-command")) {
                if (argc > 3) {
                    QString codestr(argv[3]);
                    int code = codestr.toInt();
                    printf("The command %s sent to the service \"%s\".\n",
                           (controller.sendCommand(code) ? "was" : "was not"),
                           controller.serviceName().toLatin1().constData());
                    return 0;
                }
            } else if (option == QLatin1String("-v") ||
                    option == QLatin1String("-version")) {
                bool installed = controller.isInstalled();
                printf("The service\n"
                        "\t\"%s\"\n\n", controller.serviceName().toLatin1().constData());
                printf("is %s", (installed ? "installed" : "not installed"));
                printf(" and %s\n\n", (controller.isRunning() ? "running" : "not running"));
                if (installed) {
                    printf("path: %s\n", controller.serviceFilePath().toLatin1().data());
                    printf("description: %s\n", controller.serviceDescription().toLatin1().data());
                    printf("startup: %s\n", controller.startupType() == QtServiceController::AutoStartup ? "Auto" : "Manual");
                }
                return 0;
            }
        }
    }
    printf("controller [-i PATH | SERVICE_NAME [-v | -u | -s | -t | -p | -r | -c CODE] | -h] [-w]\n\n"
            "\t-i(nstall) PATH\t: Install the service\n"
            "\t-v(ersion)\t: Print status of the service\n"
            "\t-u(ninstall)\t: Uninstall the service\n"
            "\t-s(tart)\t: Start the service\n"
            "\t-t(erminate)\t: Stop the service\n"
            "\t-p(ause)\t: Pause the service\n"
            "\t-r(esume)\t: Resume the service\n"
            "\t-c(ommand) CODE\t: Send a command to the service\n"
            "\t-h(elp)\t\t: Print this help info\n"
            "\t-w(ait)\t\t: Wait for keypress when done\n");
    return 0;
}


ここでは、A simple Service Controllerの内部サブルーチンprocessArgsのみを抜粋しています。
mainルーチンでは、このサブルーチンを起動しているだけです。

このサブルーチンで、コマンドパラメータにより、分岐がありますが、ほとんど、解説は不要と思います。
ほとんどが、QtServiceController クラスオブジェクトに対して、メソッドをコールするだけです。

その結果に対して、コンソールへ結果表示を行っています。
簡単にQtServiceController クラスのメソッドを解説しておくと以下のとおりです。


  • bool start ( const QStringList & arguments )
    -- サービスを開始する時のこのメソッドを起動します。
  • bool start ()
    -- サービスを開始する時のこのメソッドを起動します。
  • bool stop ()
    -- サービスを停止する時のこのメソッドを起動します。
  • bool pause ()
    -- サービスを一時停止する時のこのメソッドを起動します。
  • bool resume ()
    -- サービスを再開する時のこのメソッドを起動します。
  • bool sendCommand ( int code )
    -- サービスへユーザからのコマンドを送信する時のこのメソッドを起動します。
  • bool uninstall ()
    -- サービスをサービス削除(uninstall)する時のこのメソッドを起動します。
  • bool install ( const QString & serviceFilePath, const QString & account = QString(), const QString & password = QString() )
    -- サービスをサービス登録(install)する時のこのメソッドを起動します。

細かいメソッドの動作については、ドキュメントを参照された方が間違いがないです。
http://doc.trolltech.com/solutions/4/qtservice/index.html

もっと、Qt関連について詳しく知りたい方は、以下の本なども良いと思います。
Qtに関する日本語の本が少ないですね。「入門書」は、さすがに、このページを読まれるくらいの方は不要だと思います。
やっぱり、本+ネット+試してみる!!の3本柱でやっていく以外にないように思います。


コメント

3 のコメントがあります。 “Qt (7) QtでWindows Serviceを作る”


  1. Lwin Htoo Ko
    2010年10月15日 @ 16:06:12

    I tried to compile the qtservice framework. But after nmake, I got the list of errors.

    I am using Qt4.7, Visual Studio 2008 and Window XP.

    C:\Qt\QtService2.6_1>qmake

    C:\Qt\QtService2.6_1>nmake

    Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
    Copyright (C) Microsoft Corporation. All rights reserved.

    cd buildlib\ && “C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\nma
    ke.exe” -f Makefile

    Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
    Copyright (C) Microsoft Corporation. All rights reserved.

    c:\Qt\Qt4.7WinCE\bin\qmake.exe -o Makefile buildlib.pro
    “C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\nmake.exe” -f Makef
    ile.Debug

    Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
    Copyright (C) Microsoft Corporation. All rights reserved.

    cl -c -nologo -Zm200 -Zc:wchar_t- -DDEBUG -D_DEBUG -Zi -MDd -EHs-c- -W3
    -w34100 -w34189 -DUNDER_CE -DWINCE -D_WINDOWS -D_UNICODE -DUNICODE -D_WIN32 -DQT
    _NO_PRINTER -DQT_NO_PRINTDIALOG -DARMV4I -D_ARMV4I_ -Darmv4i -D_ARM_ -DARM -D_M_
    ARM -DARM -D__arm__ -DQ_OS_WINCE_WM -DQT_NO_PRINTER -DQT_NO_PRINTDIALOG -D_WIN32
    _WCE=0x502 -DQT_QTSERVICE_EXPORT -DQT_DLL -DQT_GUI_LIB -DQT_CORE_LIB -DQT_THREAD
    _SUPPORT -DQT_NO_DYNAMIC_CAST -I”..\..\Qt4.7WinCE\include\QtCore” -I”..\..\Qt4.7
    WinCE\include\QtGui” -I”..\..\Qt4.7WinCE\include” -I”..\src” -I”..\..\Qt4.7WinCE
    \include\ActiveQt” -I”debug” -I”..\..\Qt4.7WinCE\mkspecs\default” -Fodebug\ @C:\
    DOCUME~1\user\LOCALS~1\Temp\nm6.tmp
    qtservice_win.cpp
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2260) : error C2
    733: second C linkage of overloaded function ‘InterlockedIncrement’ not allowed
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2258) :
    see declaration of ‘InterlockedIncrement’
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2267) : error C2
    733: second C linkage of overloaded function ‘InterlockedDecrement’ not allowed
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2265) :
    see declaration of ‘InterlockedDecrement’
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2275) : error C2
    733: second C linkage of overloaded function ‘InterlockedExchange’ not allowed
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2272) :
    see declaration of ‘InterlockedExchange’
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2286) : error C2
    733: second C linkage of overloaded function ‘InterlockedExchangeAdd’ not allowe
    d
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2283) :
    see declaration of ‘InterlockedExchangeAdd’
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2295) : error C2
    733: second C linkage of overloaded function ‘InterlockedCompareExchange’ not al
    lowed
    C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\winbase.h(2291) :
    see declaration of ‘InterlockedCompareExchange’
    ..\src\qtservice_win.cpp(718) : error C2065: ‘WM_ENDSESSION’ : undeclared identi
    fier
    NMAKE : fatal error U1077: ‘”C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN
    \cl.EXE”‘ : return code ‘0x2’
    Stop.
    NMAKE : fatal error U1077: ‘”C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN
    \nmake.exe”‘ : return code ‘0x2’
    Stop.
    NMAKE : fatal error U1077: ‘cd’ : return code ‘0x2’
    Stop.

    Is there solution for that?

    Thanks

  2. 管理人
    2010年10月15日 @ 19:48:36

    Hi Ko,
    I could compile qt service 2.6.1 with Qt4.7, Visual Studio 2008 and Window XP.
    You might have problem with your environment , or forget ‘C:QtQtService2.6_1> configure -library’ before ‘qmake’.
    If you want static library, you should do it without parameter ( ex) ‘C:QtQtService2.6_1> configure’ ).

    OK?

  3. weihao.ma
    2010年12月28日 @ 02:03:48

    hi guy, the InteractiveService app works fine to me, but i can’t debug the code in InteractiveService::start() function, what’s the matter with this problem?

コメントをどうぞ







  • はてなブックマークへ追加する
  • Facebookでシェアする
  • twitter でつぶやく
  • Google Plusでシェアする
  • Pocketでシェアする
ページトップへ