質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1954閲覧

タスクバー常駐winapi Messageboxを多重起動させない方法

mercurian-teto

総合スコア75

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/02/18 14:24

タスクバー常駐アプリを作成しました。
タスクバーに表示されるtaskbar常駐アプリを左クリックするとコンテキストメニューが表示され、メッセージボックスが起動されるような仕様です。
ソフトウェア自体の多重起動は防ぐことはできるのですが
(下のコードに実装しています。)
タスクバーのコンテキストメニューでのメニュー選択によって表示される
messageboxの多重起動は防ぐことができません。
普段使用するようなタスクバー常駐のソフトウェアは多重起動できずにブロックするような仕組みがあると思うのですが、どう実装するのかがわかりません。回答お願いします。

winapi

1#include <Windows.h> 2#include <tchar.h> 3#include "resource.h" 4#include <Shlwapi.h> 5#include <locale.h> 6 7#define WM_TASKTRAY (WM_USER+1) 8#define ID_TASKTRAY 0 9#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) 10 11 12HWND g_hWnd; 13BOOL MultiplexStartingPrevention(VOID); 14 15 16LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 17{ 18 HANDLE hMSP; 19 hMSP = CreateMutex(NULL, TRUE, _T("MultiplexStartingPreventionTest")); 20 POINT po; 21 static HMENU tmp, hmenuR, hmenuL; 22 static UINT s_uTaskbarRestart; 23 24 25 switch (msg) 26 { 27 case WM_CREATE: 28 tmp = LoadMenu(((LPCREATESTRUCT)(lParam))->hInstance, TEXT("IDR_MENU1")); 29 hmenuR = GetSubMenu(tmp, 0); 30 /*hmenuL = GetSubMenu(tmp, 1);*/ 31 break; 32 33 34 case WM_TASKTRAY: 35 if (wParam == ID_TASKTRAY) 36 { 37 switch (lParam) { 38 39 case WM_RBUTTONUP: 40 41 GetCursorPos(&po); 42 ClientToScreen(hWnd, &po); 43 44 TrackPopupMenu(hmenuR, TPM_LEFTALIGN | TPM_BOTTOMALIGN, 45 po.x, po.y, 0, hWnd, NULL 46 ); 47 break; 48 49 } 50 51 } 52 break; 53 case WM_COMMAND: 54 switch (LOWORD(wParam)) 55 { 56 case ID_a: 57 //バージョン(A) 58 MessageBox(hWnd, _T("aaaaaaaaaaaaaaaaaaaa"), _T("id_a"), MB_OK); 59 60 break; 61 case ID_b: 62 MessageBox(hWnd, _T("bbbbbbbbbbbbbbbbbbbb"), _T("id_b"), MB_OK); 63 64 break; 65 } 66 break; 67 68 69 case WM_ENDSESSION: 70 71 72 // Do nothing. 73 break; 74 75 case WM_DESTROY: //!< ウインドウを閉じる 76 ::PostQuitMessage(0); //!< WM_QUITを送る 77 return ::DefWindowProc(hWnd, msg, wParam, lParam); 78 default: 79 80 return ::DefWindowProc(hWnd, msg, wParam, lParam); 81 } 82 return ::DefWindowProc(hWnd, msg, wParam, lParam); 83} 84 85BOOL Init(HINSTANCE hInstance) 86{ 87 /*HANDLE hMSP; 88 hMSP = CreateMutex(NULL, TRUE, _T("MultiplexStartingPreventionTest")); 89 90 if (GetLastError() == ERROR_ALREADY_EXISTS) { 91 92 93 ReleaseMutex(hMSP); 94 CloseHandle(hMSP); 95 return FALSE; 96 }*/ 97 if (MultiplexStartingPrevention() == EXIT_FAILURE)return EXIT_FAILURE; 98 99 100 // ウインドウ生成 101 WNDCLASS wc; //!< ウインドウクラス 102 // ウインドウクラスの登録 103 wc.style = CS_HREDRAW | CS_VREDRAW; 104 wc.lpfnWndProc = static_cast<WNDPROC>(WndProc); //!< メッセージを受け取るウインドウ関数 105 wc.cbClsExtra = 0; 106 wc.cbWndExtra = 0; 107 wc.hInstance = hInstance; 108 wc.hIcon = NULL; //!< アイコン登録 109 wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); 110 wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)); 111 wc.lpszMenuName = NULL; //!< メニューの登録 112 wc.lpszClassName = _T("taskbarapp"); 113 114 115 // ウインドウクラスの登録 116 if (!::RegisterClass(&wc)) 117 { 118 MessageBox(NULL, _T("windowsクラスの登録に失敗"), _T("バージョン情報"), MB_OK); 119 return EXIT_FAILURE; 120 121 } 122 // ウインドウを作成 123 g_hWnd = ::CreateWindow(_T("taskbarapp"), //!< クラス名 124 _T("gregrergere"), //!< タイトル名 125 WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, //!< スタイル 126 0, //!< X座標 127 0, //!< Y座標 128 100, //!< 横幅 129 100, //!< 高さ 130 NULL, //!< 親ウインドウのハンドル 131 NULL, //!< メニューハンドル 132 hInstance, //!< インスタンスハンドル 133 NULL); //!< lParam 134 135 // ウインドウの作成 136 if (!g_hWnd) 137 { 138 MessageBox(NULL, _T("window作成に失敗"), _T("バージョン情報"), MB_OK); 139 return EXIT_FAILURE; 140 } 141 // ウインドウの表示状態の設定 142 ::ShowWindow(g_hWnd, SW_SHOW); 143 // ウインドウの更新 144 ::UpdateWindow(g_hWnd); 145 146 147 NOTIFYICONDATA nif; 148 // タスクトレイに登録 149 nif.cbSize = sizeof(NOTIFYICONDATA); 150 nif.hIcon = NULL; 151 nif.hWnd = g_hWnd; 152 nif.uCallbackMessage = WM_TASKTRAY; 153 nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; 154 nif.uID = ID_TASKTRAY; 155 ::wcscpy_s(nif.szTip, 128, _T("takbarapp")); 156 157 Shell_NotifyIcon(NIM_ADD, &nif); 158 159 return EXIT_SUCCESS; 160} 161 162int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 163{ 164 MSG msg; 165 166 167 168 if (Init(hInstance) == EXIT_FAILURE) 169 { 170 MessageBox(NULL, _T("failure"), _T("failure"), MB_OK); 171 return EXIT_FAILURE; /*return EXIT_FAILURE*/ 172 } 173 174 while (::GetMessage(&msg, NULL, 0, 0)) 175 { 176 ::TranslateMessage(&msg); 177 ::DispatchMessage(&msg); 178 } 179 return EXIT_SUCCESS; 180} 181 182 183 184 185int MultiplexStartingPrevention() 186{ 187 HANDLE hMSP; /* 多重起動防止用Mutex Handle */ 188 hMSP = CreateMutex(NULL, TRUE, _T("MultiplexStartingPreventionTest")); 189 /* すでに起動しているか判定 */ 190 if (GetLastError() == ERROR_ALREADY_EXISTS) { 191 /* すでに起動している。*/ 192 /*MessageBox(NULL, _T("すでに起動しております。"), _T("Multiplex starting prevention Test C"), MB_OK);*/ 193 ReleaseMutex(hMSP); 194 CloseHandle(hMSP); 195 return EXIT_FAILURE; 196 } 197 return EXIT_SUCCESS; 198 199} 200

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

普段使用するようなタスクバー常駐のソフトウェアは多重起動できずにブロックするような仕組みがあると思うのですが、どう実装するのかがわかりません。回答お願いします。

非常に残念な回答ですが、Windows API にはそのような仕組みはありません。メニューの再起表示を防ぐフラグは TrackPopupMenu にはあります(TPM_RECURSE)が、モーダルダイアログを表示する場合に再帰処理を防ぐオプションは用意されていません。各ソフトウェアがどのような処理になっているか?という問いについてはモーダルダイアログを表示しているかどうかのフラグをプログラム内に設けてそれで判断しているのが大半であると推測されます。

モーダルダイアログが表示されるとメッセージループがダイアログ側で処理されるようになり、WinMain側のメッセージループが使われなくなります。いろいろなフレームワークはそれをメインのメッセージループに組み込むようにいろいろ努力していますが・・・。結局のところウィンドウプロシージャだけでモーダルダイアログ表示中かの条件判断を実施せざるを得なくなるためフラグ制御が最も実装コストが低いという結果になります。


2019/02/20 追記

面白そうなので、市販ソフトがどのような制御をしているか調べてみました。とは言っても手持ちのソフトですが。

Becky!

このソフトではモーダル表示時は終了メニューだけ無効化されています。
Becky!
無効化されていないメニューはそのまま使えます。使い勝手を落とさずに矛盾した動作を防ぐなかなか賢い対応と思いますね。フラグ制御とか上で言っているものに近いと推測されます。

Steam

画像からはわかりませんが、ポップアップメニューがモーダル表示されてメニューを選択することができません。
Steam
かなり面白い実装になってそうな気配があります。おそらくメッセージループを停止(モーダルダイアログ以外のメッセージをディスパッチしない)しているものと推測されます。ATL/WTL の実装に近いですね。

JoyToKey

画像からはわかりませんが、このソフトはモーダル表示中もそのまま操作可能です。
JoyToKey
終了メニューを選択するとそのままモーダルダイアログごとプロセスが終了します。なお、標準の DialogBox/MessageBox 系 API を使うと PostQuitMessage で投げた WM_QUIT がダイアログボックス側のメッセージループに食われる問題は発生しません。モーダルダイアログのメッセージループを自分で作成したりする複雑なアプリケーションでは上記の問題に遭遇したりします。このソフトは素直な作りになっていると推測されます。

#まとめ
結果として手持ちのソフトではメニューから直接モーダルダイアログが開くソフトはありませんでした。すべてのソフトはモードレスダイアログを開いています。とは言いつつも各ソフトウェアの機能としてモーダルダイアログを表示できるソフトウェアは存在しており、モーダルダイアログ表示中にタスクバーからメニューを開いた際の動作はソフトウェア次第という感じですね。多重起動を防止するだけであれば、真ん中の Steam の動作が比較的近いと思いますが、これはモーダルダイアログ用のメッセージループを別途管理しているということであり、標準の MessageBox 等では処理できません。(もしくはフックで処理しているか)

私のおすすめとしてはやはりモーダルダイアログ表示前にフラグを立てて、モーダルダイアログが閉じられた後にはフラグを落とす。メニューはフラグに応じて処理を決定するってのが良いと思いました。

投稿2019/02/18 16:19

編集2019/02/19 16:57
atata0319

総合スコア881

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mercurian-teto

2019/02/25 07:29

<モーダルダイアログ表示前にフラグを立てて、モーダルダイアログが閉じら<れた後にはフラグを落とす。メニューはフラグに応じて処理を決定するって<のが良いと思いました。 この方法で実装してみようと思います。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問