tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトル外部アプリのコンボボックスのテキスト取得
記事No15777
投稿日: 2013/08/09(Fri) 14:24
投稿者REX
環境 VB6 WindowsXP

 Delphiで作成された外部アプリのコンボボックスの、現在表示
されているテキストを取得しようとしています。
そこでまず次のように考えました。

1.コンボボックスのハンドルを取得
2.Sendmessage APIのCB_GETCURSELでリストのindexを取得
3.同じくCB_GETLBTEXTでテキストを取得


 しかし、アプリには同様なコンボボックスがいくつもあり、かつ
Captionも無いためコンボボックスを特定することができませんで
した。そこで次のように考えました。


1.EnumChildWindows APIですべてのChildWindowの中から、
  クラス名がTComboBoxである時
2.CB_GETLBTEXTでIndex0のテキストを見て予想されるものと
  一致したものを対象となるコンボボックスであると断定する。
3.CB_GETCURSELでIndexを取得
4.CB_GETLBTEXTでテキストを取得

この方法だと3番まではいいのですが4番でVBがダウンします。


【EnumChildProcの一部】

if ClassName="TComboBox" then
  lngRet=SendMessate(hwnd,CB_GETLBTEXT,0,ItemText)
  ↑ここでダウン

 str=left(ItemText,Instr(ItemText,vbNullChar)-1)
  
  if str="予想されるText" then
    Index=SendMessage(hwnd,CB_GETCURSEL,0,0)
    lngRet=SendMessate(hwnd,CB_GETLBTEXT,Index,ItemText)
    str=left(ItemText,Instr(ItemText,vbNullChar)-1)
  end if
end if

そこで質問です。
1.この方法が正しいのか
2.ほかの方法でするべき
3.不可能

1.2については正しい方法を教えてください。
できれば3でないといいのですが。

よろしくお願いします。

[ツリー表示へ]
タイトルRe: 外部アプリのコンボボックスのテキスト取得
記事No15778
投稿日: 2013/08/09(Fri) 19:41
投稿者魔界の仮面弁士
>  Delphiで作成された外部アプリのコンボボックスの、
検証できそうな Delphi アプリが手元に無いので、当方でチェックできるかは
分かりませんが、可能であれば、第三者がテスト可能なコードを頂けると
具体的な回答を付けやすいです。


> 1.コンボボックスのハンドルを取得
> 2.Sendmessage APIのCB_GETCURSELでリストのindexを取得
> 3.同じくCB_GETLBTEXTでテキストを取得
この方法については、動作したのですね。


> この方法だと3番まではいいのですが4番でVBがダウンします。
3と5の間に、CB_GETLBTEXTLEN を含めるべきかと思います。


>   lngRet=SendMessate(hwnd,CB_GETLBTEXT,0,ItemText)
>   ↑ここでダウン

『ダウン』とは、具体的にはどういう状態のことでしょうか。

SendMessage をスペルミスしているから、という理由では無いにしても、
lngRet がエラー値になるのか、実行時エラーになるのか、あるいは
バッファオーバフローでクラッシュしているのか、それとも…?


>  str=left(ItemText,Instr(ItemText,vbNullChar)-1)
Str関数と間違えそうな変数名はさておき。

上記のままだと、ItemText が vbNullString や "" だった場合に
引数エラーを起こしますので、直した方が良いでしょう。たとえば
 = Left(ItemText, InStr(1, ItemText & vbNullChar, vbNullChar) - 1)
もしくは
 = Split(ItemText & vbNullChar, vbNullChar, 2)(0)
などに修正されてみては如何でしょう。

[ツリー表示へ]
タイトルRe^2: 外部アプリのコンボボックスのテキスト取得
記事No15781
投稿日: 2013/08/10(Sat) 14:38
投稿者REX
> >  Delphiで作成された外部アプリのコンボボックスの、
> 検証できそうな Delphi アプリが手元に無いので、当方でチェックできるかは
> 分かりませんが、可能であれば、第三者がテスト可能なコードを頂けると
> 具体的な回答を付けやすいです。

これに関しましては購入品になるためコードの提示はできません。


> 『ダウン』とは、具体的にはどういう状態のことでしょうか。

すみません。クラッシュです。

> SendMessage をスペルミスしているから、という理由では無いにしても、
> lngRet がエラー値になるのか、実行時エラーになるのか、あるいは
> バッファオーバフローでクラッシュしているのか、それとも…?

バッファオーバーは取得データから考えてもないと思いますが
後者のvbNullstringでエラーの可能性が高いと推測します。

来週末まで手がつけれない状況なのですが、試し次第結果を報告させていただきます。

[ツリー表示へ]
タイトルRe^2: 外部アプリのコンボボックスのテキスト取得
記事No15793
投稿日: 2013/08/19(Mon) 13:28
投稿者REX
返信が遅れまして申し訳ありませんでした。


> > 1.コンボボックスのハンドルを取得
> > 2.Sendmessage APIのCB_GETCURSELでリストのindexを取得
> > 3.同じくCB_GETLBTEXTでテキストを取得
> この方法については、動作したのですね。

すみません、2番までしか動作しませんでした。
3番でクラッシュします。

次に実際の戻り値を表示します


【EnumChildProcの一部】--------------------------------------------

Dim strItemText as String * 4096

if ClassName="TComboBox" then
 Index=SendMessage(hwnd,CB_GETCURSEL,0,0) ’Index=1でした
    IF Index>=1 then  'Index=0の値はvbNullString
     lngLen=SendMessage(hwnd,CB_GETLBTEXTLEN,Index,0) ’lngLen=14でした
      IF lngLen>=2 then
     lngRet=SendMessate(hwnd,CB_GETLBTEXT,Index,strItemText)
      ↑ここでクラッシュ

--------------------------------------------------------------------

CB_GETLBTEXTLEN=1の時にCB_GETLBTEXTでクラッシュするということは
テキストを取得するのが不可能ということなのでしょうか?

[ツリー表示へ]
タイトルRe^3: 外部アプリのコンボボックスのテキスト取得
記事No15794
投稿日: 2013/08/19(Mon) 15:53
投稿者魔界の仮面弁士
> 3番でクラッシュします。
クラッシュの内容は何でしょうか。 Access Violation 系のエラーだったりはしませんか?

> lngLen=SendMessage(hwnd,CB_GETLBTEXTLEN,Index,0) ’lngLen=14でした
> IF lngLen>=2 then
>  lngRet=SendMessate(hwnd,CB_GETLBTEXT,Index,strItemText)
strItemText のバッファは、どの時点で確保されているのでしょうか。

せっかく CB_GETLBTEXTLEN から 14 というデータ長が返されているにも関わらず、
それがバッファ確保のために使われている様子がありません。

[ツリー表示へ]
タイトルRe^4: 外部アプリのコンボボックスのテキスト取得
記事No15795
投稿日: 2013/08/19(Mon) 16:10
投稿者REX
まずバッファの確保について教えてください。

上記の方法はネット上で配布されていたCB_GETLBTEXTの
例を使用したもので、この記述で確保できるものと
思っていたのですが違うのですね。
実際はどうすれば確保できるのでしょうか?


調べました。

strItemText=Space$(lngLen+1)
あっていますでしょうか?



だめでした。
「問題が発生したためVisualBasicを終了します」となります。

[ツリー表示へ]
タイトルRe^5: 外部アプリのコンボボックスのテキスト取得
記事No15796
投稿日: 2013/08/19(Mon) 19:53
投稿者魔界の仮面弁士
> 上記の方法はネット上で配布されていたCB_GETLBTEXTの
> 例を使用したもので、この記述で確保できるものと
String 型で受け取るとしたら、
 「Dim strItemText As String * 想定される最大長」
で宣言されるか、もしくは
 「Dim strItemText As String」
で宣言されたあと、String関数や Space関数などで、バッファを動的確保していたはずです。
Unicode 対応などの場合は、String の代わりに Byte 配列を使うパターンもありますね。


> strItemText=Space$(lngLen+1)
> あっていますでしょうか?

自アプリ内なら、同じメモリ空間で済むので Space$ でも String$ でも良いですが、
今回はプロセスを跨ぐメモリ空間なので、それだけでは不十分「かも」知れません。
各プロセスが読み書きするメモリ空間は、それぞれ個別に用意されているからです。

一部の Windows メッセージ(WM_GETETEXTなど)においては、プロセス間のメモリ管理を
自動的に行ってくれるものもあるのですが、CB_GETLBTEXT がそうであるかは未調査です。
少なくとも TreeView や ListView の場合は、ファイルマッピングオブジェクトなり
共有メモリなりを使ってやりとりする必要がありました。


WinXP なら、VirtualAllocEx で対象プロセスの仮想アドレス空間にメモリ領域を確保し、
それをバッファとして WriteProcessMemory でその中身を編集(ゼロクリアなど)しておき、
確保したアドレスを SendMessage にてコントロールに送出した後、それによって
書き換えられたバッファの内容を ReadProcessMemory でローカルのメモリにコピーしてから
使い終わったメモリを VirtualFreeEx で解放する…という流れでどうでしょうか。
(Win9x 系をサポートする場合は少々異なる処理が必要になるかも)

それと、Unicode / ANSI 変換の有無についても忘れずに。
(この辺は、SendMessageA を使うか SendMessageW を使うかで変わってきます)


以下、Visual Basic 向けの情報ではないですが一応参考までに。
http://home.netyou.jp/cc/susumu/shrmem.html
http://japan.internet.com/developer/20050830/26.html
http://itpro.nikkeibp.co.jp/article/COLUMN/20071107/286607/
http://wisdom.sakura.ne.jp/system/winapi/win32/win150.html


> 「問題が発生したためVisualBasicを終了します」となります。
開発環境からではなく、EXE 単体で実行した場合に、何のエラーになったかが
知りたかったのですが、おそらくは文字列バッファへの書き込みに失敗し、
前回回答した「Access Violation」が発生しているのではないかと予想しています。
http://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E4%BF%9D%E8%AD%B7

想定されるエラーメッセージの例としては、このような物があります。
・「一般保護違反」あるいは「ページ違反」 (主に Win9x)
・「保護されているメモリに読み取りまたは書き込み操作を行おうとしました」
・「0x00000005: アクセスが拒否されました。」(ERROR_ACCESS_DENIED)
・「ハンドルされていない例外は "アプリ名.exe" にあります: 0xC0000005 : Access Violation」

# 0xC 系で始まるもの(10進だと -1 始まりのもの)は Win32 系のエラー
# 0x8 系で始まるもの(10進だと -2 始まりのもの)は ActiveX/COM 系のエラー

[ツリー表示へ]
タイトルRe^6: 外部アプリのコンボボックスのテキスト取得
記事No15797
投稿日: 2013/08/20(Tue) 09:36
投稿者REX
ありがとうございます。
まずエラーについての報告をさせていただきます。

エラーの内容はご指摘のとおり
「ハンドルされていない例外は "アプリ名.exe" にあります: 0xC0000005 : Access Violation」
でした。

簡単にググって見たのですが、アクセスしてはいけない領域にアクセスしているような
内容が書かれていました。
これ以上は何もできないのでしょうか?

前述の内容もしばらく勉強してみます。

[ツリー表示へ]
タイトルRe^7: 外部アプリのコンボボックスのテキスト取得
記事No15798
投稿日: 2013/08/20(Tue) 10:38
投稿者魔界の仮面弁士
> 簡単にググって見たのですが、アクセスしてはいけない領域にアクセスしているような
> 内容が書かれていました。

だとすると、やはり
 「別プロセスが管理しているメモリ領域」
への書き込みが問題なのでしょう。

Delphiアプリと VB アプリは別プロセスですので、
VBアプリで確保された strItemText に書き込もうとして、
Access Violation が発生しているものと予想されます。


> これ以上は何もできないのでしょうか?
先述したように、VirtualAllocEx を用いて、Delphiアプリのアドレス空間上に
メモリ領域を確保してみてください。それならば取得できると思います。


[追記]
以下に VirtualAllocEx を用いた VB6 向けのコードがありました。
CB_GETLBTEXT のサンプルではありませんが、参考までに。

http://oshiete.goo.ne.jp/qa/817941.html

[ツリー表示へ]
タイトルRe^8: 外部アプリのコンボボックスのテキスト取得
記事No15799
投稿日: 2013/08/20(Tue) 11:02
投稿者REX
ありがとうございます。
うまくいきましたらサンプルを添付して報告させていただきます。

[ツリー表示へ]
タイトル解決しました!
記事No15800
投稿日: 2013/08/20(Tue) 14:52
投稿者REX
VirtualAllocEx を用いた VB6 向けのコードがありました。
長々と付き合っていただいて本当にありがとうございました。


http://homepage2.nifty.com/nonnon/SoftSample/SampleEnumWindows.html

[ツリー表示へ]