###まとめ
- Win32APIでフォームアプリのメニューバーを選択したいときは
SendMessage(hWnd, WM_COMMAND, 選択したいメニュー項目のID, 0);
- 選択したいメニュー項目のIDは、
GetMenuItemInfo
関数を使うが、なんとこの関数、引数のMENUITEMINFO
構造体のfType
に指定した一項目ずつしか情報を取ってこない。 - なのでほしい情報が集まるまで
fType
を変更しつつ何度もGetMenuItemInfo
を投げなくてはならない。 GetMenuItemInfo
はMENUITEMINFO
にうまく情報を入れられなかった場合は0を返すので条件分岐はする。- MFCの
C~Dlg
クラスではWin32APIに呼びかける関数を使おうとするとマクロで別の処理に切り替わります。とりあえず::
を使って名前空間を明示的に指定することでどうにか目的の関数を呼べるようになる。
GetMenuItemInfo(MSDN)
MSDNのサイトで不足情報はここ参照
###前提・実現したいこと
MFCから他のアプリケーションのメニューバーを操作したい。
この質問のベストアンサーを参考に
Windows7標準搭載の電卓アプリをC++/MFCアプリから操作しようとしています。
電卓アプリのHWNDを取得 →HWNDからメニューバーのHMENUを取得 →HMENUからMENUITEMINFOを取得 →dwTypeDataから項目名"表示"と合致するかチェック →hSubMenuでサブメニューのHMENUを取得 →HMENUからMENUITEMINFOを取得 →dwTypeDataから項目名"プログラマ"と合致するかチェック →メニューバーのプログラマ(P)の項目のIDを取得 →SendMessageで選択を実行
という流れで処理を行おうとしたのですがdwTypeDataと項目名を比較しようとすると
0x000007FEEA1F8F22(ucrtbased.dll)で例外がスローされました
1(UIAuto.exe 内): 0xC0000005: 場所 0xFFFFFFFFFFFFFFFF の読み取り中にアクセス違反が発生しました 2この例外のハンドラーがある場合は、プログラムを安全に実行できます。 3```~~と例外が投げられます。(文字列の先頭アドレスが指定されないからでしょうか) 4HMENUをデバッガで確認すると`<メモリを読み取れません>`と表示されており 5たしかに処理できそうに見えません。~~ 6 7~~そこで、HMENUを正しく取得する方法 8もしくはHMENUを利用するほかに他のアプリケーションのメニューバーを操作する方法 9をご教授いただきたいです。 10よろしくお願いいたします。~~ 11上記の打ち消し線部の問題が解決すれば目的の動作を達成すると思っていました。 12ですがどうも動いている様子がありませんので質問を 13- MENUITEMINFOの取得方法 14- メニューバーの項目の選択方法(SendMessageで何のコマンドを利用すればよいか) 15に変え、引き続きお知恵をお貸しいただけますようお願いいたします。 16 17###該当のソースコード 18// 2018 0123 1727 ご指摘を受けて修正 修正前の内容は編集履歴をご確認ください 19// 2018 0124 1105 メニューに表示されいる文字列は取得できました 修正前の内容は編集履歴をご確認ください 20// 2018 0124 1430 これで動くはず 修正前の内容は編集履歴をご確認ください 21// 2018 0124 1500 これを直してる場合じゃない 修正前の内容は編集履歴をご確認ください 22```c++ 23void CUIAutoDlg::OnBnClickedOk() 24{ 25 HWND hWnd = ::FindWindow(_T("CalcFrame"), _T("電卓")); 26 27 if (hWnd == NULL) { 28 MessageBox(_T("電卓が閉じてるとかそういう理由でウィンドウハンドラが取れなかった"), NULL, 0); 29 return; 30 } 31 32 ::SetForegroundWindow(hWnd); 33 34 HMENU hMen = ::GetMenu(hWnd); // cWnd->GetMenu(); 35 if (hMen == NULL) { 36 MessageBox(_T("この電卓、メニューバーのウィンドウハンドラがないぞ・・・"), NULL, 0); 37 return; 38 } 39 40 MENUITEMINFO mii; 41 if (GetSubMenuInfo(hWnd, hMen, &mii, _T("表示(&V")) == FALSE) return; 42 if (GetSubMenuInfo(hWnd, mii.hSubMenu, &mii, _T("プログラマ(&P) Alt+")) == FALSE) return; 43 ::PostMessage(hWnd, WM_COMMAND, mii.wID, 0); 44} 45// メニューバーから目的の項目を見つける 46// 目的の項目の情報をMENUITEMINFOとして返す 47BOOL CUIAutoDlg::GetSubMenuInfo(const HWND winHnd,const HMENU menHnd, MENUITEMINFO* mii,const CString menTitle) { 48 if (menHnd == NULL) return FALSE; 49 50 mii->cbSize = sizeof(MENUITEMINFO); 51 int menuCnt = ::GetMenuItemCount(menHnd); 52 std::wstring buffer; 53 for (int i = 0; i < menuCnt; ++i) { 54 // 文字列取得 55 mii->fMask = MIIM_STRING; 56 mii->cch = 0; 57 if (::GetMenuItemInfo(menHnd, i, 1, mii) == 0) continue; 58 buffer.resize(mii->cch); 59 mii->dwTypeData = NULL; 60 mii->dwTypeData = &buffer[0]; 61 if (::GetMenuItemInfo(menHnd, i, 1, mii) == 0) continue; 62 63 if (mii->dwTypeData == menTitle) { 64 // ID取得 65 mii->fMask = MIIM_ID; 66 mii->wID = -1; 67 if (::GetMenuItemInfo(menHnd, i, 1, mii) == 0) continue; 68 // サブメニューハンドラ取得 69 mii->fMask = MIIM_SUBMENU; 70 mii->hSubMenu = NULL; 71 if (::GetMenuItemInfo(menHnd, i, 1, mii) == 0) continue; 72 73 return TRUE; 74 } 75 } 76 return FALSE; 77}
GetMenuItemInfo(MSDN)とかMENUITEMINFOについて解説してるサイトを見ながら修正
###試したこと
現状の方法で改善の方向性が見出せないため他の方法を探してみています。
wID
として取得できた値でWM_COMMAND
以外のコマンドをいろいろSendMessage
してみています。
以前の質問でUI Automationを教えていただいており、これを用いる方法は現在調査中です
###補足情報(言語/FW/ツール等のバージョンなど)
- Windows7/64 SP1
- VisualStudio2015 SP
- VC++/MFC
回答1件
あなたの回答
tips
プレビュー