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

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

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

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

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

Q&A

解決済

3回答

6071閲覧

電卓のボタンを特定できるようにしたい。[Win7/64][VC++/MFC][VS2015]

notgoodpg

総合スコア37

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

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

0グッド

0クリップ

投稿2018/01/22 02:33

編集2018/01/22 06:32

###わかったこと

  • Win7の電卓アプリはボタンに絵が表示されていてキャプションは空。
  • WM_GETTEXTはwstring型で受けたほうが良い
  • WM_GETTEXTで取得できる文字列は途中に'\0'を含むかもしれないので文字列を受ける側の変数にあらかじめWM_GETTEXTLENGTHで文字長を指定するなら取得後に再度文字長を定義したほうが良い

###前提・実現したいこと
UIオートメーションを実現するためにテストとして、とりあえずWindowsに標準でインストールされている電卓を操作しようとしています。
いま、取得できるハンドラのうちどれがどのボタンか判別ができずにいます。
そこで、WM_GETTEXTをGetMessage関数に指定してボタンのキャプションを取得しようしています。
ですが、以下のメッセージが発生しており如何ともならなくなっている状況です。
また、どのハンドラーがどのボタンであるかを判断する方法が他にあればGetMessage関数が使えなくとも良いです。
ボタンのキャプションを取得して判別しようとしましたがほとんどが空になっており終端文字'\0'しか取得できず目的を達成できません。
電卓のボタンをそれぞれ特定するために良い方法はないでしょうか?

よろしくお願いいたします。

以下は解決しました。
###発生している問題・エラーメッセージ

| Micorosoft Visual C++ RuntimeLibrary | | Debug Error! Program : ......\UIAuto.exe HEAP CORRUCTION DETECTED: after Normal block(#570) at 0x0000000000031F0D0. CRT detected that the application wrote to memory after end of heap buffer. (press Retry to debug the application) | | 中止 | 再試行 | 無視 |

以下のコードの位置のブレークポイントでとめた後、ひとつステップを進めると上記の内容でエラーダイアログが表示されます。

###該当のソースコード

c++

1void CUIAutoDlg::OnBnClickedOk() 2{ 3 // TODO: ここにコントロール通知ハンドラー コードを追加します。 4 CWnd* cWnd = FindWindow(_T("CalcFrame"), _T("電卓")); 5 6 try { 7 if (cWnd == NULL) throw std::exception("cant find calc."); 8 } 9 catch(std::exception e){ 10 MessageBox(CString(e.what()), NULL, 0); 11 return; 12 } 13 HWND hWnd = cWnd->GetSafeHwnd(); 14 15 int cnt = 0; 16 cWnd->SetForegroundWindow(); 17 EnumChildWindows(hWnd, EnumChildProc, (LPARAM)&cnt); 18 ::UpdateWindow(hWnd); 19 20 //CDialogEx::OnOK(); 21} 22 23BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) 24{ 25 int bufLen = 0; 26 bufLen = SendMessage(hwnd, WM_GETTEXTLENGTH, NULL, NULL) + 1; // +1は文字末端\0用 27 if (bufLen > 1) { 28 // std::vector<char> buf[bufLen]; ご指摘を受けて改善 29 // SendMessage(hwnd, WM_GETTEXT, bufLen, (LPARAM)&buf[0]); 30 std::wstring buf; //wstringに変更 std::string buf; 31 buf.resize(bufLen); 32 SendMessage(hwnd, WM_GETTEXT, bufLen, (LPARAM)&buf[0]); 33 buf.resize(std::char_traits<wchar_t>::length(&buf[0])); //wstringに変更 char>::length(&buf[0])); 34 35 int hoge = 0; // ブレークポイント設置用の行。 // ここのブレークポイントから先でエラー 36 } 37 return TRUE; 38} 39

###試したこと

上記のコードで文字列を受けるバッファにchar配列を用いたりしましたが同じ現象で最後まで走りきりませんでした。
定義はchar *buf = new char[bufLen];で初期化、デバッガで内容を確認する行をはさんですぐdelete [] buf;していたので何も問題ないだろうと思ったのですが・・・。

GetWindowText( hwnd, CString ,int)関数でキャプションを取得する方法も試しましたがほとんどが空欄でボタン特定の参考にはなりませんでした。

###補足情報(言語/FW/ツール等のバージョンなど)

  • Windows7/64bit SP1
  • VisualStudio2015 SP1
  • VC++/MFC

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

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

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

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

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

guest

回答3

0

ベストアンサー

電卓のボタンはテキストを持っていないようです。
イメージ説明

操作対象のWindowsアプリケーションが

  • 電卓などアプリ決め打ちでよい
  • 各コントロールのIDは固定で振られている(動的にIDが変わるような作りではない普通のアプリ)

であれば、あらかじめ各ボタンのIDを調べておき、実行時に::GetDlgCtrlID(各ウインドウハンドル)でIDを取得、比較することでボタン識別できそうです。

なお、ウインドウの調査にはVisual Studio付属ツールSpy++が便利ですので活用しましょう。

投稿2018/01/22 05:58

can110

総合スコア38258

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

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

notgoodpg

2018/01/22 06:16

ご回答ありがとうございます。 取り方の問題じゃなくて本当にテキストが無いとは・・・
guest

0

UI Automationというそのままの名前のCOM APIがあります。
それを使用すれば電卓のボタンのテキストが取得できます。
以下はWindows SDKに付属しているinspectというツールでUI Automationで取得できる情報を表示できます。
イメージ説明
Windows標準のアプリは視覚障がい者向けの対応がなされているはずなので何かしらの方法でテキスト情報は得られると思います。

投稿2018/01/22 14:19

編集2018/01/22 14:26
hmmm

総合スコア818

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

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

notgoodpg

2018/01/23 00:36

ご回答ありがとうございます。 やりようはあるのですね。どうにかそのNameのプロパティにたどり着けるよう工夫してみます。
guest

0

std::vector<char> buf[bufLen];

それは何がしたいんだ・・・

cpp

1std::wstring buf; 2buf.resize(bufLen); 3::SendMessage(hwnd, WM_GETTEXT, bufLen, (LPARAM)&buf[0]); 4buf.resize(std::char_traits<wchar_t>::length(buf.c_str());

では

投稿2018/01/22 02:42

編集2018/01/22 05:19
yumetodo

総合スコア5850

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

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

notgoodpg

2018/01/22 03:41 編集

ご回答ありがとうございます。 WM_GETTEXTの処理は行えました。 > それは何がしたいんだ・・・ [このサイト](https://cpprefjp.github.io/reference/vector/op_constructor.html)の(3)としてあらわされているコンストラクタを利用しているつもりでしたが、誤った表記のようですね。気をつけます。 ところで、疑問に思ったのですが `buf.resize(std::char_traits::length(buf.c_str());` の行はなぜ文字列を取得した後で再度`.resize`をしているのですか? よろしければご教授ください。 また、申し訳ありませんが質問の電卓のボタンの判別はできないという結果が得られましたのでベストアンサーには選ぶ事ができそうにありません。ご容赦ください。
episteme

2018/01/22 03:43

stringは文字列中に'\0'を含むことを許されている。 ためにこの処理を行わないと メンバ関数 length() と C文字列としての長さが一致しなくなる。
notgoodpg

2018/01/22 04:00 編集

ご回答ありがとうございます。 理解できたと思います。 もし'\0'が文字列中に含まれた場合 普通のchar型配列なら要素数を超えてエラーになるけど std::string型ならば特に気にする必要は無い でも‘.resize‘で大きさを指定しているからchar_traits::length関数で取得した長さで再設定する必要がある と理解しました。
episteme

2018/01/22 04:10

ときにコレ、stringでいいの? wstringじゃないのね?
notgoodpg

2018/01/22 04:41 編集

ご指摘ありがとうございます。 std::stringをstd::wstringに変更しました。 現象は改善されず取得できた文字列もstd::stringと同様だったので本件に関しては特に影響はないと思います。
yumetodo

2018/01/22 05:17

あ、wstringですね、指摘ありがとうございます
episteme

2018/01/22 05:29

んー...お試しに使ってる電卓のボタン、たとえば全部bitmapで描かれてたらWindowText取れないよね。 明らかにTextで書かれた自前のMFCアプリならどうなるんだろ。
yumetodo

2018/01/22 05:31

素直に画像認識に書けるべき事案だと思うんですよね・・・、数値のOCRは十分精度出ますし
notgoodpg

2018/01/22 06:14

ご指摘ありがとうございます。 他の自作アプリでボタンを判別しようとしたら出来ましたので、どうやらボタンの表示はテキストではないようです。 わざわざ電卓のボタンのテキストにいちいちbitmap画像を用意してるかも・・・なんてパターンは考えもしませんでした。 画像認識は最後の手段として考えておきます。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問