C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

SendMessageの使用法

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■96397 / inTopicNo.1)  SendMessageの使用法
  
□投稿者/ 初心者 (3回)-(2020/11/19(Thu) 18:50:45)

分類:[.NET 全般] 

ネットでは SendMessageの使用法として、
ウィンドウに関連付けられたテキスト(ウィンドウタイトルやコントロールの内容など)を指定されたバッファにコピーします。
と記載してありますが、
何故か、ウィンドウタイトルが取得されます、内容を取得するにはどのように記述したら良いのでしょうか。
相手のソフトは、CADですが、古いバージョンなら、内容が取得出来ています。
対象物自体が、違うということでしょうか? Textboxのコントロールだと思っているのですが。
使い分けはどの様したらよいのでしょうか。
よろしくお願いします。
環境 Win10,VisualBasix Express2013



Public Function S1_GetTextMessage(ByVal hWin As Long, ByRef mes As String) As Long
Dim strResult As String ' 文字列を受け取るバッファ
Dim lenResult As Long ' バッファの長さ
Dim dmy As Long

' コマンドラインの文字列の長さ
lenResult = SendMessage(hWin, WM_GETTEXTLENGTH, 0, 0&)
' 文字列 +1 の長さの空白文字列を用意
strResult = Space$(lenResult * 2 + 1)
' SendMessage を使ってコマンドラインを得る
dmy = SendMessageString(hWin, WM_GETTEXT, lenResult * 2 + 1, strResult)
S1_GetTextMessage = dmy
mes = strResult
End Function
引用返信 編集キー/
■96400 / inTopicNo.2)  Re[1]: SendMessageの使用法
□投稿者/ Hongliang (1115回)-(2020/11/19(Thu) 20:03:34)
hWinは目的のテキストボックスのウィンドウハンドルになっていますか?
一度Spy++で対象のアプリケーションを調査してみてください。

// WPFなどで作った場合、コントロールが「ウィンドウ」ではない場合もあって、この場合はWM_GETTEXTではどうにもならないですが…。
引用返信 編集キー/
■96403 / inTopicNo.3)  Re[1]: SendMessageの使用法
□投稿者/ 魔界の仮面弁士 (2920回)-(2020/11/19(Thu) 20:09:14)
2020/11/19(Thu) 20:33:52 編集(投稿者)

No96397 (初心者 さん) に返信
> ネットでは SendMessageの使用法として、
> ウィンドウに関連付けられたテキスト(ウィンドウタイトルやコントロールの内容など)を指定されたバッファにコピーします。
上記は WM_GETTEXT メッセージの話でしょうか?


> 何故か、ウィンドウタイトルが取得されます、
指定しているウィンドウハンドルは、そのコントロールの HWND になっていますか?

ウィンドウレスコントロールが対象の場合には、SendMessage では受け取れないので、
UI オートメーションなどを使って値を取得する必要があるかもしれません。


> 内容を取得するにはどのように記述したら良いのでしょうか。
フォームのタイトルを受け取るのであれば、確かに WM_GETTEXT メッセージを送出すれば OK です。
また、16bit 版 Windows 時代の名残で、TextBox コントロール等は WM_GETTEXT で受け取れるます。

しかしほとんどのコントロールは、それぞれのコントールごとの専用メッセージを使うようになっています。
RichTextBox なら EM_GETTEXTEX メッセージ、ListBox なら LB_GETTEXT メッセージ、ListView なら LVM_GETITEMTEXT メッセージなど。


> Textboxのコントロールだと思っているのですが。
Microsoft Spy++ 等で確認済みですか?


> 使い分けはどの様したらよいのでしょうか。

構文自体は VB.NET のものになっていますが、API の呼び出し方が明らかに間違っています。
特にデータ型。VB.NET の API 宣言で As Long が登場することは稀です。

もしかして、VB.NET 向けのサンプルではなく、32bit 版 VBA 向けのソースコードを真似ていませんか?
たとえば Lib "SendMessageA" とか書いてありそうな予感が…。
オーバーロードを使わずに別名関数として API を宣言している点も、.NET っぽくないですし。


> Dim strResult As String ' 文字列を受け取るバッファ
一定の条件を満たせば、文字列バッファとして As String で受けられなくもないですが、
WM_GETTEXT であれば、普通は As StringBuilder を渡すようにします。


> Public Function S1_GetTextMessage(ByVal hWin As Long, ByRef mes As String) As Long

VB.NET の Long 型は、64bitの整数型です。

ウィンドウハンドルは、実行環境によって 32bit だったり 64bit だったりするため、
As Long ではなく、As HandleRef もしくは As IntPtr にするのが一般的です。


> lenResult = SendMessage(hWin, WM_GETTEXTLENGTH, 0, 0&)

第3第4引数が 0, 0& になっていますが、これは前者が Int32 型で、後者が Int64 型という
組合せになっています。明らかに不自然な呼び出し方です。

ソースコードの先頭に「Option Explicit On」宣言を指定して、データ型チェックを厳密化しておいてください。
その上で、現在の API 宣言も提示頂けないでしょうか。



SendMessage の宣言方法は色々ありますが、汎用的な宣言ですと、
たとえば下記のように書くことができます。

Private Declare Auto Function SendMessage Lib "user32" (hWnd As IntPtr, wMsg As UInteger, wParam As IntPtr, lParam As IntPtr) As IntPtr

第2引数を As Integer にすることも多いですし、第1引数を As HandleRef にすることもあります。
Declare ステートメントを使う代わりに、DllImport 属性による定義を用いる事もあります。
また、使用するメッセージによっては、第3引数や第4引数を
ByRef As Integer や ByVal As StringBuilder などにすることもあります。


たとえば WM_GETTEXT の場合であれば、これでどうでしょう。

Private Const WM_GETTEXTLENGTH As UInteger = &HE
Private Const WM_GETTEXT As UInteger = &HD
Private Declare Unicode Function SendMessage Lib "user32" Alias "SendMessageW" (hWnd As IntPtr, wMsg As UInteger, wParam As IntPtr, lParam As IntPtr) As IntPtr
Private Declare Unicode Function SendMessage Lib "user32" Alias "SendMessageW" (hWnd As IntPtr, wMsg As UInteger, wParam As IntPtr, lParam As System.Text.StringBuilder) As IntPtr

Public Shared Function GetText(hWin As IntPtr) As String
  Dim length = SendMessage(hWin, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)
  If length = IntPtr.Zero Then
    Return Nothing
  Else
    Dim buffer As New System.Text.StringBuilder(length.ToInt32() + 1)
    SendMessage(hWin, WM_GETTEXT, New IntPtr(buffer.Capacity), buffer)
    Return buffer.ToString()
  End If
End Function
引用返信 編集キー/
■96407 / inTopicNo.4)  Re[2]: SendMessageの使用法
□投稿者/ 初心者 (4回)-(2020/11/20(Fri) 12:35:52)
Hongliangさん、魔界の仮面弁士さん回答ありがとうございます。
VB6をVB.netに移植したので、記述としては、かなりいい加減なコードになっていると
ご指摘にとおり思いましたが、魔界の仮面弁士さんのコードを拝見し、知識のなさを実感しました。
問題点1
MSSpy++は持ち合わせていので、WinSpy++(フリー)にて判ったことは、(Textboxか判断できませんが)
CADとは、BricsCADでV13とV20(最新)の比較です。
WinSpy++のHandleとCaptionの値の違いで、
V13時は、Captionの値は、入力したコマンドの文字列を反映してましたので、正しく処理できていた
V20では、反映しておらず、"text"が常に取得されます。(コマンド文字列が取得できないので処理できない)
思うに、表示(ここではコマンド文字列の事ですが)しているものと実際と違い、何かテクニックを使用している
のではないかと推測します。
お願いとなりますが、色々試みましたが私には、理解不能です。
何方か、BricsCAD(体験版)をインストールして頂き、
解明してもらえないでしょうか。
目的は、入力されてコマンド文字列の取得です。

問題点2(文法そのものに問題があり)
APIの定義はこんな感じです。ネットから引っ張り出して、最終的にこうなりました。
Public Declare Function SendMessageString Lib "User32.dll" Alias "SendMessageA" (ByVal hWnd As Int32, _
ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As Int32

<System.Runtime.InteropServices.DllImport("user32.dll", CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
Public Function SendMessage( _
ByVal hWnd As Int32, _
ByVal Msg As Int32, _
ByVal wParam As Int32, _
ByVal lParam As Int32) As Integer
End Function

以下、回答
> 上記は WM_GETTEXT メッセージの話でしょうか?
はい、そうです


> 指定しているウィンドウハンドルは、そのコントロールの HWND になっていますか?
直接入力したりして試しているので間違いはないです

> ウィンドウレスコントロールが対象の場合には、SendMessage では受け取れないので、
> UI オートメーションなどを使って値を取得する必要があるかもしれません。
??

>>オーバーロードを使わずに別名関数として API を宣言している点も、.NET っぽくないですし。
オーバーロードの用法は分かりませんが、文法的に間違いが多いので、魔界の仮面弁士さんに作って
頂いたものを移植して、トライしていきます。
因みに、「Option Strict On」を指定したら、100個以上ありました、いや〜まいったな!

時間が掛かると思いますが、よろしくお願いいたします。


引用返信 編集キー/
■96409 / inTopicNo.5)  Re[3]: SendMessageの使用法
□投稿者/ 魔界の仮面弁士 (2921回)-(2020/11/20(Fri) 16:28:26)
No96407 (初心者 さん) に返信
> MSSpy++は持ち合わせていので、WinSpy++(フリー)にて判ったことは、(Textboxか判断できませんが)

WinSpy++ であれば、Class タブの Class Name を確認してみてください。
それが "Edit" か "Static" になっていれば、WM_GETTEXT で取得できると思います。
(ただし Static であっても、画像コントロールの場合は文字列を得られません)

General タブの Caption で読み書きできるのであれば、たぶん大丈夫じゃないかな…?


> 何方か、BricsCAD(体験版)をインストールして頂き、
> 解明してもらえないでしょうか。

ダウンロードにアカウント登録が必要なんですね。

私が協力できるかはお約束できないのですが、仮にそれをインストールした人が居たとしても、
そのアプリのどの画面のどの項目の値を取得したいのかが分からないと、
協力者が解析できないかもしれません。

すぐに分かるような項目なのであれば良いですが、分かりにくそうであれば、取得したい場所の画面を撮影し、
それを見られる URL (たとえば OneDrive の公開フォルダーなど)を貼った方が良いかも。



> 目的は、入力されてコマンド文字列の取得です。

画面を見たことが無いので分かりませんが、VB6 で言うところの
イミディエイト ウィンドウのようなものですかね?


> 問題点2(文法そのものに問題があり)
> APIの定義はこんな感じです。ネットから引っ張り出して、最終的にこうなりました。

Windows 9x 系が駆逐された今となっては、SendMessageA などの ANSI 系関数を呼び出すメリットは殆どありません。
基本的には、SendMessageW などの Unicode バージョンを前提にするか、Auto 指定にすることをお奨めします。

といっても、ANSI 系が廃止されたわけでは無いので、〜A 系関数のままでも一応動作します。
Shift_JIS に無い文字とかは化けてしまいますけれどね。


> Public Declare Function SendMessageString Lib "User32.dll" Alias "SendMessageA" (ByVal hWnd As Int32, _
>  ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As Int32

本来 IntPtr 型にすべき場所が、Int32 型になっているようですね。
正しい呼び出し方ではありませんが、x86 ビルドでコンパイルすれば、Int32 型のままでも一応大丈夫です。
あるいは AnyCPU ビルドでも、32bit 優先ビルドなら OK。


HWND 型、WPARAM 型 ⇒ Win16 では 16bit、Win32 では 32bit、Win64 では 64bit
https://www.wdic.org/w/TECH/HWND
https://www.wdic.org/w/TECH/WPARAM

LPARAM 型 ⇒ Win16 と Win32 では 32bit、Win64 では 64bit 
https://www.wdic.org/w/TECH/LPARAM



>>ウィンドウレスコントロールが対象の場合には、SendMessage では受け取れないので、
>>UI オートメーションなどを使って値を取得する必要があるかもしれません。
> ??

たとえば、Excel VBA のユーザーフォームに対して貼ったテキストボックスは
ウィンドウレス コントロールです。こうしたコントロールは HWND を持たないため(フォームにはある)、
SendMessage で操作できません。

WinSpy++ 等でハンドルの存在を確認できているなら OK です。
ただしそれが WM_GETTEXT 対応なウィンドウであるかは別の話。


>>> オーバーロードを使わずに別名関数として API を宣言している点も、.NET っぽくないですし。
> オーバーロードの用法は分かりませんが、

引数定義だけが異なる同名の Function (あるいは Sub) を複数用意することをオーバーロードと言います。

正しく呼び出せるなら、VB6 っぽいコードのままでも問題はないのですが、それでも
API を使うファイルだけでも、「Option Strict On」にしておいた方が安全だとは思います。
引用返信 編集キー/
■96414 / inTopicNo.6)  Re[4]: SendMessageの使用法
□投稿者/ 初心者 (5回)-(2020/11/21(Sat) 11:39:39)
> WinSpy++ であれば、Class タブの Class Name を確認してみてください。
> それが "Edit" か "Static" になっていれば、WM_GETTEXT で取得できると思います。
[wxWindow]です、これってEditですか?

> ダウンロードにアカウント登録が必要なんですね。
すいません。記載しておけば良かったですね。(要求されると躊躇しますよね)

> そのアプリのどの画面のどの項目の値を取得したいのかが分からないと、
> 協力者が解析できないかもしれません。
そうでした。まずは、以下のスナップショットをアップし、環境だけでも準備させて頂きます。
目的は、CAD側がコマンド受付状態(「: 」コロンの状態)であることを判断して、SendMessageで
CADにコマンドを送信(たとえば、線分を引く「LINE」など)します。
V13とV20の比較をアップします。
赤枠と黄枠は同じ内容で同期されているので、どちらから入力しても構いません。プロント履歴の方が
取得しやすかったのでこちらハンドルを使用しています。
V13の時のClassNameは、RICHEDIT50W   ←あ、こちらにはEDITがあります
V20は、wxWindow に代わっています。  ←こちらは無い、でもコマンドに入力した"LINE"はどこにあるの?

V13のCADの  
トップ画面  https://1drv.ms/b/s!AmLWi97VvBs0hRUbYrvB_RQ5SQ2Z?e=HEN8Nv
WinSpy++   https://1drv.ms/b/s!AmLWi97VvBs0hROIUTXWLjtLmXKL?e=8sHHa1
V20の  
CADのトップ画面 https://1drv.ms/b/s!AmLWi97VvBs0hRe4JDXNIJl2UMUh?e=1JhAG3
WinSpy++     https://1drv.ms/b/s!AmLWi97VvBs0hRKfLZSFntGS7duL?e=a7baVU
全体のウインドウ一覧  https://1drv.ms/b/s!AmLWi97VvBs0hRiNNjUXZR4H1mgA?e=hjpKmf
※OneDriveの共有はこれでいいのか?

> Windows 9x 系が駆逐された今となっては、SendMessageA などの ANSI 系関数を呼び出すメリットは殆どありません。
> 基本的には、SendMessageW などの Unicode バージョンを前提にするか、Auto 指定にすることをお奨めします。
> 本来 IntPtr 型にすべき場所が、Int32 型になっているようですね。
> 正しい呼び出し方ではありませんが、x86 ビルドでコンパイルすれば、Int32 型のままでも一応大丈夫です。
> あるいは AnyCPU ビルドでも、32bit 優先ビルドなら OK。
> たとえば、Excel VBA のユーザーフォームに対して貼ったテキストボックスは
> ウィンドウレス コントロールです。こうしたコントロールは HWND を持たないため(フォームにはある)、
> SendMessage で操作できません。
勉強になります。

> オーバーロードを使わずに別名関数として API を宣言している点も、.NET っぽくないですし。
前回提示して頂いたSendMessageですが同じもの2行ありエラーにならないのが解せなかったのですが、
そうゆうことだったんですか。使い道がピンときませんが、
例えば、引数にOptionalがありますが、そんなケース時に、再定義して使用するのでしょうか??
私は、市販のVisualBasic2013パーフェクトマスターを参考にしてます。
これには、IntPtrや、StringBuilderなんで言葉は出てきませんが、どの参考書を利用されているのでしょうか?
新しいことが多すぎて(コードにても記述が全然違う、OneDriveも使用したことがないですし、でも今回の
提案で便利かもしれません。以前は宅ファイルを活用してました。
改めて一寸、頭を整理しないといけない状況です。

引用返信 編集キー/
■96415 / inTopicNo.7)  Re[5]: SendMessageの使用法
□投稿者/ Azulean (1173回)-(2020/11/21(Sat) 17:57:02)
2020/11/21(Sat) 18:00:54 編集(投稿者)

No96414 (初心者 さん) に返信
> [wxWindow]です、これってEditですか?
見ての通り違います。


> V13の時のClassNameは、RICHEDIT50W   ←あ、こちらにはEDITがあります
これはリッチエディットテキストボックスなので、Edit コントロールの一種とは言えます。

> V20は、wxWindow に代わっています。  ←こちらは無い、でもコマンドに入力した"LINE"はどこにあるの?
おそらく、WxWidgets と呼ばれるライブラリを使ったものなのでしょうね。
( https://ja.wikipedia.org/wiki/WxWidgets )

Caption のところと見た目で見えている文字列が完全に別ものなので、「SendMessage を使ってテキストを取得する」は「できない」と考えてください。

見た目からは独自描画で文字列表示としているように見えるので、ほかの手段でも取得も難しいのではないか?と、個人的には思います。


(参考)
クラス登録あたり
https://github.com/wxWidgets/wxWidgets/blob/dd864cffe0daab175c4f807d096f469ea191934b/src/msw/app.cpp#L698

"wxWindow"の元
https://github.com/wxWidgets/wxWidgets/blob/dd864cffe0daab175c4f807d096f469ea191934b/src/msw/window.cpp#L504

"NR"サフィックスの元
https://github.com/wxWidgets/wxWidgets/blob/dd864cffe0daab175c4f807d096f469ea191934b/src/msw/app.cpp#L116
https://github.com/wxWidgets/wxWidgets/blob/dd864cffe0daab175c4f807d096f469ea191934b/include/wx/msw/app.h#L54
引用返信 編集キー/
■96442 / inTopicNo.8)  Re[6]: SendMessageの使用法
□投稿者/ 初心者 (8回)-(2020/11/23(Mon) 15:02:52)
Azulean さん ご回答ありがとうございます。
> Caption のところと見た目で見えている文字列が完全に別ものなので、「SendMessage を使ってテキストを取得する」は「できない」と考えてください。
そうですか。だめですか。残念です。
マルチプラットフォームに対応するには、この様なライブラリーを使用した方が、開発効率が良いのでしょうね
ある意味、解決済みなのでしょうけど、1週間ほど整理した後、結論を出したいと思います。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ