
[code]

    while(true){ //<=実際には何かしらの条件
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
        //hwnd<-対象のハンドル
        var sb = new StruingBuilder();
        var length = SendMessage(hwnd,WM_GETTEXTLENGTH,0,0);
        sb.Capacity = length+1; //追記2
        Console.WriteLine("before length:"+ length);
        Thread.Sleep(200);  //無くても同じ
        length = SendMessage(hwnd,WM_GETTEXT,length+1,sb);
        Console.WriteLine("before length:"+ length);
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
    }
    
[/code]

上記プログラムを実行すると

[code]

    before length:212
    after length:91
    
[/code]

と何度もなってしまいます。  
また、ターゲットのアプリケーションの最新版ではどの状況でも文字数が一致するのですが、古いバージョンでは上記のように一致する時としない時とあります。  
どういった原因が考えられるでしょうか。以上、よろしくお願いいたします。

追記:  
sbには実際の文字列が途切れて格納されていました。

追記2:  
sbのCapacityは設定しておりましたが、転記ミスで抜けていました。せっかくご回答いただいたのに大変失礼いたしました。申し訳ございません。

追記3:  
下記のように、試しに取得する長さを変更(length * 10)しましたが、得られる文字列はやはり途切れておりました。

[code]

    while(true){ //<=実際には何かしらの条件
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
        //hwnd<-対象のハンドル
        var sb = new StruingBuilder();
        var length = SendMessage(hwnd,WM_GETTEXTLENGTH,0,0);
        sb.Capacity = length *10 +1;
        Console.WriteLine("before length:"+ length);
        Thread.Sleep(200);  //無くても同じ
        length = SendMessage(hwnd,WM_GETTEXT,length *10 +1,sb);
        Console.WriteLine("before length:"+ length);
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
    }
    
[/code]


CC BY-SA 3.0
2017-09-26T23:09:46.877
38214
2018-06-17T01:49:24.187
2017-09-28T03:28:04.197
25376
25376
question
c# windows .net winapi
SendMessageのWM_GETTEXTとWM_GETTEXTLENGTHで大幅に文字数が異なる


[`WM_GETTEXTLENGTH`](https://msdn.microsoft.com/en-
us/library/ms632628\(v=vs.85\).aspx)に説明されています。

> Under certain conditions, the DefWindowProc function returns a value that is
> larger than the actual length of the text. This occurs with certain mixtures
> of ANSI and Unicode, and is due to the system allowing for the possible
> existence of double-byte character set (DBCS) characters within the text.
> The return value, however, will always be at least as large as the actual
> length of the text; you can thus always use it to guide buffer allocation.
> This behavior can occur when an application uses both ANSI functions and
> common dialogs, which use Unicode.

WindowsアプリケーションはUnicodeとANSIの２種類が存在し、APIとしてもUnicodeとANSIの２バージョンが用意されています。アプリケーションの動作モードと`SendMessage`とが異なる場合、実際の文字数ではなく文字列を格納可能な大きめのバッファサイズを返すそうです。

* * *

> sbには実際の文字列が途切れて格納されていました。  
>  SendMessageは[DllImport("user32.dll", CharSet = CharSet.Auto)]としています

`WM_GETTEXTLENGTH`はANSIバージョンが呼び出された場合、当然ながらANSI文字列のバイト数を返しますし、`WM_GETTEXT`が`WPARAM`で要求するバッファサイズも呼び出したバージョンに依存します。ところが`StringBuilder`が保持するバッファのサイズは常にUnicodeの文字数です。質問文に挙げられているコードはバッファサイズの単位が一致しておらず非常に不安定です。

Unicodeに統一すると共に、`WM_GETTEXT`呼び出し前に`StringBuilder`のバッファサイズを確保すべきです。なお、`StringBuilder`の既定のバッファサイズは16文字ですのでたいていの場合にあふれています。

[code]

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wparam, StringBuilder lparam);
    
    while(true){ //<=実際には何かしらの条件
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
        //hwnd<-対象のハンドル
        var length = SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
        Console.WriteLine("before length:"+ length);
        var sb = new StringBuilder(length); /* length分のバッファサイズを確保 */
        length = SendMessage(hwnd, WM_GETTEXT, new IntPtr(length+1), sb);
        Console.WriteLine("before length:"+ length);
        /*何らかの処理、ただし、hwndのテキストはコード側ではいじらない*/
    }
    
[/code]


CC BY-SA 3.0
2017-09-26T23:42:41.773
38215
2017-09-27T03:41:52.907
2017-09-27T03:41:52.907
4236
4236
38214
answer


> どういった原因が考えられるでしょうか。

次の条件のもとでは、両戻り値に矛盾が無い場合も考えられます。

(1)取得される側ウインドウがMBCS(=ANSI)で作られている。  
(2)取得する側ウインドウがUNICODEで作られている。

この場合、

(1)側は、WM_GETTEXTLENGTHに対してはMBCS単位での文字数(=BYTE数)を戻す。  
これをlength(=BYTE数)に代入します。  
後にWM_GETTEXT送付時の、バッファのUNICODE文字数として渡していることに注意。

ところが、(1)側は、WM_GETTEXTの引数のwParamをバッファのBYTE数と解釈する  
(UNICODE文字数でない点に注意)

従って、(1)側は、自身の保持するMBCS文字列をUNICODEに変換した結果をsbに戻すとき、  
「バッファ量が足りない(実際には足りている)」と解釈し、  
およそ半分程度のUNCODE変換後の文字列をコピーして、  
「そのMBCS文字数(=BYTE数)」を返却する。

ではないでしょうか。


CC BY-SA 3.0
2017-09-27T04:14:16.207
38223
2017-09-27T04:14:16.207
3793
38214
answer