retireSakiの日記

たぶん引退した?ソフトウエアエンジニアのブログ

GTKmm (3.0) と glade によるアプリ (2) - アプリケーションの多重起動の防止

 ランチャーに登録されているアプリケーションは、アイコンをシングルクリックで起動できるにもかかわらず、ダブルクリックしてアプリケーションを「二重起動(多重起動)してしまった!」なんてことはありませんか?
私はたまにというか、よくやってしまうのですよ(恥)


 そこで、今回は以前作成したアプリを若干修正して、アプリの多重起動を防止する機能を追加したいと思います。

元となったアプリケーションは以下から。
retiresaki.hatenablog.com

多重起動の防止をした実行結果 :

f:id:retireSaki:20181009211528j:plain
アプリケーションの多重起動の防止
※今回は実行ファイル名を "paned_test2" としてビルドしました。

 上の画像のように、アプリを起動しておいて、更にアプリを起動しようとすると、"このアプリは既に起動されています"のようなメッセージを出力するものです。


 アプリケーションの多重起動を防止する方法にはMutexを使ったものなどいろいろ考えられています。

 今回の防止策は、システムの起動済のプロセス情報に起動しようとするプロセス名が既に存在しているかどうかチェックするものです。
なので、非常に簡単で、新たに1つの関数の追加をするだけです。

修正した main.cc

#include "MainWindow.h"

Glib::RefPtr<Gtk::Builder> refBuilder;

bool chk_multiple_startup(const gchar *);

/*******************************************************************************
    main
*******************************************************************************/

int main(int argc, char* argv[])
{
    Gtk::Main kit(&argc, &argv);
    
    // multiple startup check <- this !!
    const gchar *appname = g_get_application_name();
    if (!chk_multiple_startup(appname)){
        std::cout << "this application has already been started :"
                  << appname << std::endl;
        return 1;
    }

    try
    {
        refBuilder=Gtk::Builder::create_from_file("paned_test.glade");
    }
    catch(const Glib::FileError& ex)
    {
        std::cerr << "FileError: " << ex.what() << std::endl;
        return 1;
    }
    catch(const Glib::MarkupError& ex)
    {
        std::cerr << "MarkupError: " << ex.what() << std::endl;
        return 1;
    }
    catch(const Gtk::BuilderError& ex)
    {
        std::cerr << "BuilderError: " << ex.what() << std::endl;
        return 1;
    }

    MainWindow* m_main = nullptr;
    refBuilder->get_widget_derived("mainWindow", m_main);
    if (m_main){

        kit.run(*m_main);

        delete m_main;
        return 0;
    }

    std::cerr << "mainWindow load failed\n" << std::endl;
    delete m_main;
    return 1;
}

// add function <- this !!
bool chk_multiple_startup(const gchar *process_name)
{
    FILE *fp;
    char str[256];
    int  ncnt;

    ncnt=0;
    sprintf(str,"ps x | grep %s",process_name);
    if ((fp=popen(str,"r"))==NULL)
        return(true);
    while(1){
        if(fgets(str,255,fp)==NULL){
            break;
        }
        if (strstr(str,"grep")==NULL)
            ncnt++;
            if(feof(fp)){
                break;
        }
    }
    pclose(fp);
    if (ncnt>1){
        return(false);
    }
    return(true);
 }

 追加した関数名は
bool chk_multiple_startup(const gchar *);

chk_multiple_startup の引数にはプロセス名(実行ファイル名またはアプリ名)を指定します。
プロセス名は g_get_application_name を使って取得します。

 chk_multiple_startup では、ps x | grep <プロセス名> の出力情報から既にプロセスが存在しているかチェックしています。
このとき、"grep <プロセス名>" 自体もプロセスとして起動された状態での "ps x" 情報なので、それをカウントの除外としています。
ps コマンドは linuxのコマンドです。他のOSの場合若干修正するとよいでしょう。


 こんな使い方もできますよ。
chk_multiple_startup の引数を特定の文字列(例えば arvの一部)にして、多重起動は可能だが、同一の特定の文字列 (例えば arvの一部)では、多重起動はできない。
といように!
 使い方はいろいろできます。


 とっても簡単で、かつ明確な多重起動を防止する方法でした。


 一応 makefile も載せておきます。
今回は、1ファイルの一部修正だけですので、他のファイルやZIPファイルは掲載しません。

修正した makefile

CXXFLAGS = -o -Wall -Werror `pkg-config gtkmm-3.0 --cflags`
LDLIBS = -lpthread  `pkg-config gtkmm-3.0 --libs`
INCLUDE = .
PROG = paned_test2
SRCS = main.cc MainWindow.cc
OBJS = main.o MainWindow.o

$(PROG): $(OBJS)
		$(CXX) -o $(PROG) $(OBJS) $(LDLIBS)

deps:
		$(CXX) -MM $(SRCS) > deps.mak

clean:
		$(RM) $(OBJS) $(PROG)


 最後に、この機能は Qt でも使えます。(g_get_application_name ではなく特定文字列で使いました)
単純に引数に使うプロセス名取得関数以外は、標準ライブラリーだけを使っている C による処理なので意外に応用できます。

 そうそう、以前にかなり古いMS-Windowsでやろうとしたときは、Bugか何かで失敗したんだよね〜(笑)

 各自、ご自由に修正してお使いください。

注目記事

「Amazon.co.jpアソシエイト」または「[乙の名称を挿入]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。