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

わんくま同盟

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

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

ツリー一括表示

テキストボックス指定行の文字列につきまして /ak (18/04/07(Sat) 13:20) #87015
Re[1]: テキストボックス指定行の文字列につきまして /魔界の仮面弁士 (18/04/07(Sat) 14:53) #87018
  └ Re[2]: テキストボックス指定行の文字列につきまして /ak (18/04/07(Sat) 15:45) #87020
    └ Re[3]: テキストボックス指定行の文字列につきまして /ak (18/04/07(Sat) 16:03) #87021
      └ Re[4]: テキストボックス指定行の文字列につきまして /ak (18/04/07(Sat) 16:37) #87022
        └ Re[5]: テキストボックス指定行の文字列につきまして /魔界の仮面弁士 (18/04/07(Sat) 18:19) #87023
          └ Re[6]: テキストボックス指定行の文字列につきまして /ak (18/04/07(Sat) 19:09) #87024
            └ Re[7]: テキストボックス指定行の文字列につきまして /ak (18/04/09(Mon) 09:44) #87050 解決済み
              └ Re[8]: テキストボックス指定行の文字列につきまして /魔界の仮面弁士 (18/04/09(Mon) 13:49) #87051 解決済み
                └ Re[9]: テキストボックス指定行の文字列につきまして /ak (18/04/09(Mon) 15:20) #87053
                  └ Re[10]: テキストボックス指定行の文字列につきまして /ak (18/04/10(Tue) 02:28) #87058 解決済み


親記事 / ▼[ 87018 ]
■87015 / 親階層)  テキストボックス指定行の文字列につきまして
□投稿者/ ak (1回)-(2018/04/07(Sat) 13:20:01)

分類:[VB6 以前] 

お世話になります。

C# と VB.NET の質問掲示板とありましたが、分類にvb6以前のもありましたので投稿致しました。

テキストボックス指定行の文字列につきまして、ご質問いたします。

全て半角数字、文字での使い方ですと文字列が取得されます。
全角にしますと指定行の文字列の取得が思う通りに文字列が取得できませんでした。
全て半角数字、文字での使い方で考えられていると思われました。

質問
全角及び全角、半角の混合でも使えるようにすることはできませんでしょうか。
ご指導のほどを宜しくお願い致します。

下記のWEBページをそのまま参考にしています。
「複数行テキストボックスの指定行の文字列を取得する」
http://hanatyan.sakura.ne.jp/vb6/textbox06.htm
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit

'指定のウィンドウにメッセージを送る(750)
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
  ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
'メモリブロックをコピーする(1008)
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
  Destination As Any, Source As Any, ByVal Length As Long)
'複数行テキストボックスにおいてテキストの行数を取得する(799)
Private Const EM_GETLINECOUNT = &HBA
'  〃    指定行の先頭の文字インデックスを取得する(803)
Private Const EM_LINEINDEX = &HBB
'指定の行を取得する
Private Const EM_GETLINE = &HC4
'  〃    指定の文字インデックスを含む行インデックスを取得(802)
Private Const EM_LINEFROMCHAR = &HC9
'  〃    指定した行インデックスの一つ前の行までのバイト数を取得(803)
Private Const EM_LINELENGTH = &HC1

Private Sub Command1_Click()
On Error Resume Next
  Dim lngMaxRow As Long
  lngMaxRow = SendMessage(Text1.hwnd, EM_GETLINECOUNT, 0&, 0&)
  Dim No As Long
  No = CLng(Text2.Text)
  If No < 1 Or No > lngMaxRow Or Err.Number Then
    Beep
    Exit Sub
  End If
  Label1.Caption = fGetLineString(Text1, No - 1)
End Sub


'指定行の文字列を取得する関数
Private Function fGetLineString(ByRef txtBox As TextBox, ByVal LineNo As Long) As String
  Dim Buffer()  As Byte '取得する文字列のバッファー
  Dim LineLength As Long '1行当たりのバイト数
  Dim LineIndex As Long '指定行の先頭の文字インデックス
  'バッファーのサイズ用に1行当たりのバイト数を事前に取得
  '指定行の先頭の文字インデックスを取得
  LineIndex = SendMessage(txtBox.hwnd, EM_LINEINDEX, LineNo, ByVal 0&)
  '指定行のバイト数を求める
  LineLength = SendMessage(txtBox.hwnd, EM_LINELENGTH, LineIndex, ByVal 0&)
  If LineLength < 1 Then
    fGetLineString = ""
    Exit Function  '改行のみ場合等は、処理を抜ける
  End If
  ReDim Buffer(1 + LineLength)
  'バッファーの最初の2バイトに最大バイト数を設定する
  CopyMemory Buffer(0), LineLength, 2
  '半角1バイト・全角2バイト換算での取得バイト数と文字列を取得
  '(ここで取得するバイト数は、上記で取得するバイト数と同じになります。)
  LineLength = SendMessage(txtBox.hwnd, EM_GETLINE, LineNo, Buffer(0))
  'バッファーより指定バイト数分の文字を取り出し Unicode に変換
  fGetLineString = StrConv(LeftB$(Buffer, LineLength), vbUnicode)
End Function
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'

[ □ Tree ] 返信 編集キー/

▲[ 87015 ] / ▼[ 87020 ]
■87018 / 1階層)  Re[1]: テキストボックス指定行の文字列につきまして
□投稿者/ 魔界の仮面弁士 (1622回)-(2018/04/07(Sat) 14:53:22)
No87015 (ak さん) に返信
> 全て半角数字、文字での使い方ですと文字列が取得されます。
> 全角にしますと指定行の文字列の取得が思う通りに文字列が取得できませんでした。

手元の VB4 + Win7 だと問題が確認できませんでした。

どういう文字列を設定して、
何行目を取得したときに、
どういう結果になってしまうのか、
具体的な情報を頂けると助かります。


> 下記のWEBページをそのまま参考にしています。
引数の txtBox は ByRef にする必要は無いような…。(本題とは無関係の事ですが)


> 全て半角数字、文字での使い方で考えられていると思われました。
手元に VB6 環境が無かったので検証できてはいませんが、API によっては、
マニフェストによって動作が変わってしまう事もあるようです。
今回の件に関係あるのかは分かりませんが。
https://support.microsoft.com/ja-jp/help/418099
http://hanatyan.sakura.ne.jp/vbhlp/manifest.htm


> 全角及び全角、半角の混合でも使えるようにすることはできませんでしょうか。
Win95/98/98SE/ME で動作しなくても構わないのであれば、
SendMessageA ではなく SendMessageW での実装に切り替えてみては如何でしょうか。
[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87018 ] / ▼[ 87021 ]
■87020 / 2階層)  Re[2]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (3回)-(2018/04/07(Sat) 15:45:41)
魔界の仮面弁士 さん

返答して頂きましてありがとうございました。

>手元の VB4 + Win7 だと問題が確認できませんでした。
はい、問題ないことが分りましたので、何か設定、使い方に問題があるのでしょうか?。
Windows XP/7 (32bit) / VB6.0(SP6)で確かめました。

>どういう文字列を設定して、
>何行目を取得したときに、
>どういう結果になってしまうのか、
>具体的な情報を頂けると助かります。
はい、分りました。

Text1
 ↓
あいうえお
かきくけこ
さしすせそ
たちつてと

Text2
 ↓
1入力(1行目)

Command1
 ↓
クリック

範囲選択
 ↓
あいうえお
かきく

Text2
 ↓
2入力(2行目)

Command1
 ↓
クリック

範囲選択
 ↓
さしすせそ


Text1
 ↓
111111111
222222222
333333333
444444444
555555555


Text2
 ↓
1入力(1行目)

Command1
 ↓
クリック

範囲選択
 ↓
111111111

Text2
 ↓
2入力(2行目)

Command1
 ↓
クリック

範囲選択
 ↓
222222222

>引数の txtBox は ByRef にする必要は無いような…。(本題とは無関係の事ですが)
問題ないとのことでありますので参考と同じくこのままにしておきます。

>Win95/98/98SE/ME で動作しなくても構わないのであれば、
はい、Win95/98/98SE/ME で動作することはありません。
>SendMessageA ではなく SendMessageW での実装に切り替えてみては如何でしょうか。

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
"SendMessageA"⇒"SendMessageW" 1箇所
"SendMessageA"⇒"SendMessageW"に変更しますと以下に変わりました。

Text2
 ↓
1入力(1行目)

Command1
 ↓
クリック

範囲選択
 ↓
あいうえお

Text2
 ↓
2入力(2行目)

Command1
 ↓
クリック

範囲選択
 ↓
さしす

以上につき説明が不十分な所があり返答して頂けますと幸いであります。



[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87020 ] / ▼[ 87022 ]
■87021 / 3階層)  Re[3]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (4回)-(2018/04/07(Sat) 16:03:39)
魔界の仮面弁士 さん

自分に誤りがありました。
本ソフトに範囲選択を追加した為に不具合の問題が発生してしまいました。

本ソフトに範囲選択を追加したソフトをこの後、記載投稿します。

大変、申し訳ありませんでした。

[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87021 ] / ▼[ 87023 ]
■87022 / 4階層)  Re[4]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (5回)-(2018/04/07(Sat) 16:37:54)
魔界の仮面弁士 さん

(Label1)文字列の取得表示を(Text1)に範囲選択するように追加しましたが範囲選択に不具合であることは分りました。

'指定行の文字列を取得する関数
Private Function fGetLineString(ByRef txtBox As TextBox, ByVal LineNo As Long) As String
Dim Buffer() As Byte '取得する文字列のバッファー
    ・
    ・
' ↓ 以下を追加しましたが不具合により範囲選択が文字列の取得表示と一致しませんでした。
Text1.SetFocus: Text1.SelStart = LineIndex: Text1.SelLength = LineLength '指定行を範囲選択する

End Function


誤り⇒Text1.SetFocus: Text1.SelStart = LineIndex: Text1.SelLength = LineLength '指定行を範囲選択する

自作追加が問題の原因であることが分りました。

お手数をお掛けしまして大変、申し訳ありませんでした。

[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87022 ] / ▼[ 87024 ]
■87023 / 5階層)  Re[5]: テキストボックス指定行の文字列につきまして
□投稿者/ 魔界の仮面弁士 (1624回)-(2018/04/07(Sat) 18:19:12)
No87020 (ak さん) に返信
> Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
> ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
> "SendMessageA"⇒"SendMessageW" 1箇所
> "SendMessageA"⇒"SendMessageW"に変更しますと以下に変わりました。

EM_LINELENGTH の戻り値や EM_GETLINE に渡すバッファは TCHAR 単位なので、
A 系では「バイト数」、W 系では「文字数」で扱います。
ですからバッファサイズも見直さないと駄目ですよ。

'ReDim Buffer(1 + LineLength)
ReDim Buffer(1 + 2 * LineLength)




No87022 (ak さん) に返信
> 範囲選択に不具合であることは分りました。
SelStart / SelLength プロパティは、バイト数ではなく文字数単位での指定ですよ。

たとえば Text1 の中身が下記の 4 行だったとして

====
あさに
ごはん
たべた
はずだ
====

ここで「2 行目」を選択するなら、
 Text1.SelStart = 6
 Text1.SelLength = 3 または 5
でなければなりません。(末尾改行を含むなら 5 文字選択、含まないなら 3 文字選択)

EM_LINEINDEX で得られる値は、A 系では「6 バイト目」、W 系では「6 文字目」を意味しますし、
EM_LINELENGTH で得られる値は、A 系では「6 バイト」、W 系では「3 文字」を意味します。

なのでこの場合は、SendMessageW の方が手っ取り早いかも。


> Private Function fGetLineString(ByRef txtBox As TextBox, ByVal LineNo As Long) As String
操作するべきは Text1 ではなく、txtBox のはずでは?

それに、範囲選択することを目的とするのなら、
このネーミングのままにするのは不自然な気もします。
Sub SelectLine とかの方が良いかもしれません。
[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87023 ] / ▼[ 87050 ]
■87024 / 6階層)  Re[6]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (6回)-(2018/04/07(Sat) 19:09:29)
魔界の仮面弁士 さん

再度に渡り回答をして頂きましてありがとうございました。


>■No87020 (ak さん) に返信
>> Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
>> ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
>> "SendMessageA"⇒"SendMessageW" 1箇所
>> "SendMessageA"⇒"SendMessageW"に変更しますと以下に変わりました。


> ■No87020 (ak さん) に返信
> EM_LINELENGTH の戻り値や EM_GETLINE に渡すバッファは TCHAR 単位なので、
> A 系では「バイト数」、W 系では「文字数」で扱います。
> ですからバッファサイズも見直さないと駄目ですよ。

> 'ReDim Buffer(1 + LineLength)
> ReDim Buffer(1 + 2 * LineLength)

> ■No87022 
> > 範囲選択に不具合であることは分りました。
> SelStart / SelLength プロパティは、バイト数ではなく文字数単位での指定ですよ。

> たとえば Text1 の中身が下記の 4 行だったとして

> ====
> あさに
> ごはん
> たべた
> はずだ
> ====

> ここで「2 行目」を選択するなら、
>  Text1.SelStart = 6
>  Text1.SelLength = 3 または 5
> でなければなりません。(末尾改行を含むなら 5 文字選択、含まないなら 3 文字選択)

> EM_LINEINDEX で得られる値は、A 系では「6 バイト目」、W 系では「6 文字目」を意味しますし、
> EM_LINELENGTH で得られる値は、A 系では「6 バイト」、W 系では「3 文字」を意味します。

> なのでこの場合は、SendMessageW の方が手っ取り早いかも

>> Private Function fGetLineString(ByRef txtBox As TextBox, ByVal LineNo As Long) As String
> 操作するべきは Text1 ではなく、txtBox のはずでは?

> それに、範囲選択することを目的とするのなら、
> このネーミングのままにするのは不自然な気もします。
> Sub SelectLine とかの方が良いかもしれません。

以上のことから、範囲選択に関し全般的に見直しする必要があることが分りました。
特に、ご指摘につきましては一つ一つ時間はかかりますが調べてみます。
予定としましては、数日中に分りましたら、解決とさせてもらいます。
色々と、ご指導して頂きまして深謝申し上げます。

[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87024 ] / ▼[ 87051 ]
■87050 / 7階層)  Re[7]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (7回)-(2018/04/09(Mon) 09:44:58)
魔界の仮面弁士 さん

お世話になりました。

テキストボックス指定行の文字列の範囲選択につきまして、ご説明いたします。

  先頭行から最終行位置までのバイト計算では、
  パソコンにもよると思いますがWebページが[20000]バイトでは1〜3秒かかりました。
  そこで、検索を利用して指定行の文字列の範囲選択を行うことにしました。
  指定行[xx]行目の文字列を取得し、その文字列を文字検索し文字列サイズを範囲選択しました。
  指定行文字列の位置を取得し同じ文字列が複数でも文字検索された行位置と指定行を比較し一致
  した所で範囲選択としました。Webページが[20000]バイト最終行位置まで試しますと即、反映できました。
  他にも良い方法があると思いますが文字検索を利用して行うことにしました。

  色々と、ご指導をしてもらいまして、本当にありがとうございました。

解決済み
[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87050 ] / ▼[ 87053 ]
■87051 / 8階層)  Re[8]: テキストボックス指定行の文字列につきまして
□投稿者/ 魔界の仮面弁士 (1631回)-(2018/04/09(Mon) 13:49:49)
No87050 (ak さん) に返信
> 先頭行から最終行位置までのバイト計算では、
バイト計算せずに実装することもできますよ。
後述のコードを参考にしてみてください。


ちなみに SelLength プロパティは &H0000&〜&HFFFF& の範囲しか渡せないので、
本文が長くなってくると
  txtBox.SelStart = Len(txtBox.Text) '65536 以上になるとエラー
すら実行できなくなりますのでご注意ください。



No87023 (魔界の仮面弁士) の訂正
> EM_LINELENGTH の戻り値や EM_GETLINE に渡すバッファは TCHAR 単位なので、
> A 系では「バイト数」、W 系では「文字数」で扱います。
SendMessageA / SendMessageW の違いというわけでは無さそうですね。
手元の環境では、SendMessageA / SendMessageW のいずれを使ったとしても、
VB6 の TextBox に対して送出した場合は バイト単位
VB.NET の TextBox に対して送出した場合は文字数単位となりました。


ということで、SelStart / SelLength プロパティ(文字数単位)のかわりに
EM_SETSEL を使って選択する方法に切り替えてみました。
SelStart と違って 65535 文字を超えても大丈夫です。



Option Explicit
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" (ByVal hWnd As OLE_HANDLE, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const EM_SETSEL As Long = &HB1&
Private Const EM_LINEINDEX As Long = &HBB&
Private Const EM_LINELENGTH As Long = &HC1&
Private Const EM_SCROLLCARET As Long = &HB7&

'TextBox の指定行を選択状態にします
' txtBox : 処理対象の TextBox
' lineNo : 選択する行番号(1〜)
' autoScroll : 選択したあと、その場所までスクロールさせて表示するかどうか
' reverseSelect : True=行末から行頭に選択、False=行頭から行末に選択
Friend Sub SelectLine(ByVal txtBox As TextBox, ByVal lineNo As Long, Optional autoScroll As Boolean = True, Optional reverseSelect As Boolean = True)
  Dim fromPos As Long, toPos As Long

  Dim lineIndex As Long
  lineIndex = SendMessage(txtBox.hWnd, EM_LINEINDEX, lineNo - 1, 0&)
  If lineIndex < 0& Then
    fromPos = &H7FFFFFFF
    toPos = &H7FFFFFFF
  Else
    Dim lineLength As Long
    lineLength = SendMessage(txtBox.hWnd, EM_LINELENGTH, lineIndex, 0&)
    If reverseSelect Then
      fromPos = lineIndex + lineLength
      toPos = lineIndex
    Else
      fromPos = lineIndex
      toPos = lineIndex + lineLength
    End If
  End If
  SendMessage txtBox.hWnd, EM_SETSEL, fromPos, toPos
  If autoScroll Then
    SendMessage txtBox.hWnd, EM_SCROLLCARET, 0&, 0&
  End If

  On Error Resume Next
  txtBox.SetFocus 'Visible や Enabled が False だと実行時エラー
End Sub


引数の「ByVal txtBox As TextBox」を「ByVal txtBox As RichTextBox」に置き換えれば、
そのまま RichTextBox 版にもなります。
解決済み
[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87051 ] / ▼[ 87058 ]
■87053 / 9階層)  Re[9]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (8回)-(2018/04/09(Mon) 15:20:14)
魔界の仮面弁士 さん

ご回答をして頂きまして、ありがとうございました。

> ちなみに SelLength プロパティは &H0000&〜&HFFFF& の範囲しか渡せないので、
> 本文が長くなってくると
>   txtBox.SelStart = Len(txtBox.Text) '65536 以上になるとエラー
> すら実行できなくなりますのでご注意ください。
はい、分りました。65535 文字を超えては確かめてはいませんが、エラーになるのですね。
知りませんでした。大変、参考になりました。

>> EM_LINELENGTH の戻り値や EM_GETLINE に渡すバッファは TCHAR 単位なので、
>> A 系では「バイト数」、W 系では「文字数」で扱います。
>SendMessageA / SendMessageW の違いというわけでは無さそうですね。
>手元の環境では、SendMessageA / SendMessageW のいずれを使ったとしても、
>VB6 の TextBox に対して送出した場合は バイト単位
>VB.NET の TextBox に対して送出した場合は文字数単位となりました。
はい、分りました。
>ということで、SelStart / SelLength プロパティ(文字数単位)のかわりに
>EM_SETSEL を使って選択する方法に切り替えてみました。
>SelStart と違って 65535 文字を超えても大丈夫です。
SelStart / SelLengthの使用ですのでEM_SETSELを使用してみます。

ソフトを、ご提供して頂きまして感謝申し上げます。
ご提供ソフトで確かめてみます。
[ 親 87015 / □ Tree ] 返信 編集キー/

▲[ 87053 ] / 返信無し
■87058 / 10階層)  Re[10]: テキストボックス指定行の文字列につきまして
□投稿者/ ak (9回)-(2018/04/10(Tue) 02:28:23)
魔界の仮面弁士 さん

お世話になります。

ご提供してもらいました方法につきまして、ご報告いたします。

ご提供してもらいました下記の方法に

「複数行テキストボックスの指定行の文字列を取得する」
http://hanatyan.sakura.ne.jp/vb6/textbox06.htm

Command2ボタンを追加し、Text1に指定行を入力⇒Command2ボタンをクリックすることで、
txtBox(参考)指定行の文字列(半角、全角)全ての桁が行毎に選択状態になりました。
検索を利用して指定行の文字列選択から、ご提供してもらいました方法にさせてもらいました。
大変、スッキリなりました。

(参考)
======
あさに
ごはん
たべた
はずだ

111111111
222222222
333333333
444444444
555555555

あ1い23うえ4お5
===============

Option Explicit
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" (ByVal hWnd As OLE_HANDLE, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
   ・
   ・
   ・
End Sub

Private Sub Command2_Click()

Call SelectLine(Text1, Text2)

Dim stText As String

' 選択中の文字を取得する
stText = Me.Text1.SelText

' 取得した選択中の文字を表示する
Label1.Caption = stText

End Sub

再三に渡り、ご指導をして頂きまして本当にありがとうございました。

解決済み
[ 親 87015 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -