FindWindow() による二重起動防止

[戻る]
ゲキアツ 2014/07/24(木) 16:43:51
お世話になります。
以前、Mutex による二重起動防止についてご相談させて戴きました。
Windows 7/8.1 に搭載されている音声読み上げアプリ『ナレーター』の起動中に、自分が
作ったアプリを終了させると、タスクマネージャーのバックグラウンド・プロセスに自分
のアプリが残ったままになり、そのため Mutex が検出されて2回目の起動ができなかっ
たのです。
こちらです。
http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201403/14030001.txt

解決策として、二重起動防止に FindWindow() 関数を使うことにしました。
アプリの名称を仮に Gekiatsu とします。
コードは下記の通りです。

BOOL CGekiatsuApp::InitInstance()
{
    CWnd *pWnd;
    pWnd = CWnd::FindWindow(NULL, _T("Gekiatsu"));
    if (pWnd != NULL)
    {
        pWnd->ShowWindow(SW_RESTORE);
        pWnd->SetForegroundWindow();
        return FALSE;
    }
}

これで解決できたと思っていたら、このアプリの実行ファイルを Gekiatsu というフォル
ダの中に置くと、起動しないことが判明しました。(例:D:\MyApp\Gekiatsu)
フォルダも Window ということになるのでしょうか?
D:\Gekiatsu\MyApp というように直前が Gekiatsu でなければ起動します。

何かいい解決策はないでしょうか?
よろしくお願いいたします。

AR 2014/07/25(金) 13:45:32
 ちょっと語弊がありますが、FindWindow()の第2引き数は、トップレベルのウィンドウ
のキャプション文字列(タイトルバーの文字列)なので、どこかに「Gekiatsu」というタ
イトルバーを持ったアプリケーションが起動しているのだと思います。
 具体的には、エクスプローラーで該当フォルダーを開いてんじゃね?と予想しています。
 (エクスプローラーはコントロールパネルでタイトルバーのパス表記をどうするか指定
できます)


 簡単な方法は2重起動を防止するアプリケーションのタイトルバーにパスで使えない文
字を埋めておく、コッソリスペースを足しておくと原理が分かりやすいでしょう。

 次に簡単な方法は、第1引き数を使うことです。
 ドキュメント-ビュー構成であると仮定して、PreCreateWindow()に以下の記述でウィン
ドウのクラス名をユニークである事が保証できる文字列に書き換えてしまうとバッティン
グしなくなります。
 自前でウィンドウ作っているなら、生成時のクラス名を適当なものにすれば良いです。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
 if (cs.lpszName)
  WNDCLASS wc;
  ::GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wc);
  wc.lpszClassName = L"なんかユニークになる文字列、GUIDとかお奨め";
  AfxRegisterClass(&wc);
  cs.lpszClass = wc.lpszClassName;
 }
・・・


 他にもハンドルからプロセス名を取得して一致を見るとか、逆にプロセス一覧からハン
ドルを求めるとか、親プロセスとの関係から取得するとか、いろんな回避方法があります
ので工夫してみてください。
 (ですが、本来はMutexを使うのが王道だと思っています)

みい 2014/07/25(金) 16:24:25
SetPropで自身に目印をつけておいて、FindWindowで見つけだした
Windowに対してGetPropして目印つけてるWindowか判断している
って処理を見たことがあります。

ゲキアツ 2014/07/25(金) 18:46:50
[[解決]]
AR様、みい様 有り難うございます。
AR様に教えて戴いた、PreCreateWindow()でウィンドウのユニークなクラス名を定義する
方法でクリア出来ました。

古いスレッドには追加発言できません。