tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルSetWindowLongの動作
記事No11851
投稿日: 2017/06/20(Tue) 11:02
投稿者KH
お世話になります。KHと申します。

VB6で作成しました、「テキストボックスに全角文字を入力すると半角カナを表示」する
プログラムをVB.NET(VB2005)に変換したのですが、
VB6のときには問題なく動作していたのが、VB.NETだと途中でエラーになってしまいます。
下記のソースコードの、StartEditの中の
m_lpOrg = SetWindowLong(m_txtSrc.handle, GWL_WNDPROC, AddressOf pWindowProc)
の行で、
「現在の場所のソースコードを表示できません」とエラー表示されますので
どういったことが考えられるかご教授いただけると幸いです。
よろしくお願い致します。


●入力フォームの文字入力部分
    Private Sub Kanji_KeyDown(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.KeyEventArgs) Handles Kanji.KeyDown
        Dim KeyCode As Short = eventArgs.KeyCode
        Dim Shift As Short = eventArgs.KeyData \ &H10000
        Module1.KeyDownEdit(Kanji, Kana)
    End Sub


●フリガナ取得部分
Module Module1
    Private fEditing As Boolean
    Private m_txtSrc As Object
    Private m_txtDest As Object
    Private m_lpOrg As Long
    Public Const GCS_RESULTREADSTR As Short = &H200s
    Public Const GCS_RESULTSTR As Short = &H800s
    Public Const WM_IME_COMPOSITION As Short = &H10Fs
    Public Const GWL_WNDPROC As Short = (-4)
    Public Const WM_CHAR As Short = &H102s
    
    Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As IntPtr, ByVal nIndex As Short, ByVal dwNewLong As WindowProcDelegate) As Long
    Declare Function SetWindowLong2 Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As IntPtr, ByVal nIndex As Short, ByVal dwNewLong As Short) As Short
    Public Delegate Function WindowProcDelegate(ByVal hwnd As IntPtr, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

    Public Sub EndEdit()
        '(メソッド)IMEふりがな入力の監視を終了する
        If fEditing = True Then
            '終了処理
            fEditing = False
            'UPGRADE_WARNING: オブジェクト m_txtSrc.hwnd の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。
            m_lpOrg = SetWindowLong2(m_txtSrc.hwnd, GWL_WNDPROC, m_lpOrg)
        End If
    End Sub
    
    Public Sub KeyDownEdit(ByRef txtSrc As Object, ByRef txtDest As Object)
        '(メソッド)キーボード入力のたびにふりがな入力を監視する
        'KeyDownイベントから呼び出します
        'txtSrc     漢字を入力するテキストボックス
        'txtDest    ふりがなを自動入力するテキストボックス
        
        'ふりがな監視/終了
        StartEdit(txtSrc, txtDest)
        System.Windows.Forms.Application.DoEvents()
        EndEdit()
        '連動消去
        If m_txtSrc.Text = "" Then m_txtDest.Text = ""
        
    End Sub
    
    Public Sub StartEdit(ByRef txtSrc As Object, ByRef txtDest As Object)
        '(メソッド)IMEふりがな入力の監視を開始する
        'txtSrc     漢字を入力するテキストボックス
        'txtDest    ふりがなを自動入力するテキストボックス
        '--準備
        If fEditing = True Then Call EndEdit() 'EndEditを忘れている場合の処理
        m_txtSrc = txtSrc
        m_txtDest = txtDest
        fEditing = True
        '--サブクラス化開始
        m_lpOrg = SetWindowLong(m_txtSrc.handle, GWL_WNDPROC, AddressOf pWindowProc)
    End Sub
    
    Public Function pWindowProc(ByVal hwnd As IntPtr, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

<ここで振り仮名を取得>
    
    End Function
End Module

[ツリー表示へ]
タイトルRe: SetWindowLongの動作
記事No11852
投稿日: 2017/06/20(Tue) 21:28
投稿者魔界の仮面弁士
> プログラムをVB.NET(VB2005)に変換したのですが、

このタイミングで 2005 とは…大変ですね。

ところで、提示頂いたコードは修正すべき箇所が多すぎるので、
変換前の VB6 コードを見せてもらえないでしょうか。
間違ったコードを手直しするよりは、そちらの方が手っ取り早そうで…。


> ●フリガナ取得部分
入力されたフリガナの取得を行いたいだけであれば、
Microsoft Visual Studio International Feature Pack 2.0 の
Yomigana Framework を利用する方法もあります。

https://www.microsoft.com/ja-jp/download/details.aspx?id=18970
https://www.microsoft.com/ja-jp/download/details.aspx?id=15251


エクスプローラーで
"C:\Program Files (x86)\Microsoft Visual Studio International Feature Pack 2.0\YomiganaFramework\YomiganaTextBox.dll"
を「コピー」してから、Visual Studio で Form1 のデザイナを開き、
ツールボックス上で「貼り付け」すると、ツールボックス上に YomiganaTextBox が追加されます。
(Microsoft.International.Windows.Forms.YomiganaTextBox クラス)


Form1 上に YomiganaTextBox と TextBox を貼り付けてから、
YomiganaTextBox1 の BindingControl プロパティに TextBox1 を割り当てれば完成。
YomiganaTextBox1 に漢字入力すると、TextBox1 にカナが入ります。

ただし、漢字入力後に編集した場合や、クリップボード経由での入力に追従できないなど
幾許かの制限があります。また、実行環境が 64bit 環境である場合には、
プログラムを x86 ビルドでコンパイルしておく必要があります



> VB6のときには問題なく動作していたのが、VB.NETだと途中でエラーになってしまいます。

Short(16bit)、Integer(32bit)、Long(64bit)、IntPtr(32/64) の使い分けが出鱈目だからでしょうね…。

少なくとも、Function SetWindowLong2 の戻り値を As Short で宣言しておきながら、
それを m_lpOrg As Long に代入している時点で整合性が取れていません。

SetWindowsLong では、何故か nIndex が Short 型で宣言されてしまっていますし(本来は32bit整数型)、
コールバックデリゲートについても、

× Public Delegate Function WindowProcDelegate(ByVal hwnd As IntPtr, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

○ Public Delegate Function WindowProcDelegate(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

となるべきでしょう。もちろん、AddressOf される Public Function pWindowProc の引数・戻り値も同様です。

EndEdit 内で呼び出しているハンドルの取得も、m_txtSrc.hwnd ではなく、m_txtSrc.Handle でしょうし、
データ型も m_txtSrc As Object ではなく m_txtSrc As TextBox が適当かと思います。



それに、そもそも .NET では、サブクラス化のために GWL_WNDPROC を呼ぶ必要がありません。

TextBox を継承したクラスを用意すれば、サブクラス化せずとも
WndProc メソッドを簡単かつ安全に処理できる仕組みが用意されていますし、
仮に TextBox を継承できない理由があったのだとしても、その場合は
NativeWindow クラスを通じて WndProc を処理することができるようになっています。



他にも、

・オーバーロードがサポートされるので、引数定義の異なる API を呼ぶ際に、わざわざ
 SetWindowLong2 のような別名を付与する必要は無い。

・Windows 9x 系をサポートするのでなければ、〜A 系 API ではなく、〜W 系 API を使うべき。

・VB6 と違って、AddressOf の対象となるプロシージャを Module 上に配置する必要は無い。

などなど…。

[ツリー表示へ]
タイトルRe^2: SetWindowLongの動作
記事No11853
投稿日: 2017/06/21(Wed) 11:14
投稿者KH
魔界の仮面弁士様、ご回答ありがとうございます。

いろいろ勉強不足でわからないことだらけですが、
教えていただきましたYomigana Frameworkや、データ型の違いなど
調べてみようと思います。

あと以下のソースがVB6で動作していた元のソースになりますので、
ご確認よろしくお願いします。


●入力フォームの文字入力部分
Private Sub Kanji_KeyDown(KeyCode As Integer, Shift As Integer)
    Module1.KeyDownEdit Kanji, Kana
End Sub

●フリガナ取得部分(Module1.bas)
Private fEditing      As Boolean
Private m_txtSrc      As Object
Private m_txtDest     As Object
Private m_lpOrg       As Long
Public Const GCS_RESULTREADSTR = &H200
Public Const GCS_RESULTSTR = &H800
Public Const WM_IME_COMPOSITION = &H10F
Public Const GWL_WNDPROC = (-4)
Public Const WM_CHAR = &H102

Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function ImmGetContext Lib "imm32.dll" (ByVal hwnd As Long) As Long
Declare Function ImmGetCompositionString Lib "imm32.dll" Alias "ImmGetCompositionStringA" (ByVal hIMC As Long, ByVal dw As Long, lpv As Any, ByVal dw2 As Long) As Long
Private m_StrConvMode As Long
Declare Function ImmReleaseContext Lib "imm32.dll" (ByVal hwnd As Long, ByVal hIMC As Long) As Long
Declare Function ImmGetOpenStatus Lib "imm32.dll" (ByVal hIMC As Long) As Long
Public Const WM_KEYUP = &H101
Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Sub EndEdit()
    '(メソッド)IMEふりがな入力の監視を終了する
    If fEditing = True Then
        '終了処理
        fEditing = False
        m_lpOrg = SetWindowLong(m_txtSrc.hwnd, GWL_WNDPROC, m_lpOrg)
    End If
End Sub

Public Sub KeyDownEdit(txtSrc As Object, txtDest As Object)
    '(メソッド)キーボード入力のたびにふりがな入力を監視する
    'KeyDownイベントから呼び出します
    'txtSrc     漢字を入力するテキストボックス
    'txtDest    ふりがなを自動入力するテキストボックス
    
    'ふりがな監視/終了
    StartEdit txtSrc, txtDest
    DoEvents
    EndEdit
    '連動消去
    If m_txtSrc.Text = "" Then m_txtDest.Text = ""

End Sub

Public Sub StartEdit(txtSrc As Object, txtDest As Object)
    '(メソッド)IMEふりがな入力の監視を開始する
    'txtSrc     漢字を入力するテキストボックス
    'txtDest    ふりがなを自動入力するテキストボックス
    '--準備
    If fEditing = True Then Call EndEdit    'EndEditを忘れている場合の処理
    Set m_txtSrc = txtSrc
    Set m_txtDest = txtDest
    fEditing = True
    '--サブクラス化開始
    m_lpOrg = SetWindowLong(m_txtSrc.hwnd, GWL_WNDPROC, AddressOf pWindowProc)
    
End Sub

Public Function pWindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    '[サブクラス化処理]メッセージを監視してふりがなを入力
    '(外部から呼び出さないでください)
    Static fCalling As Boolean
    Dim hIMC&
    Dim dwSize&
    Dim MyByte() As Byte
    Dim MyRead$, MyResult$, MyStr$
    Dim ConvMode&

    If fCalling = False Then
        fCalling = True
        Select Case uMsg
            'IME入力中にふりがなを得る
            Case WM_IME_COMPOSITION
                hIMC = ImmGetContext(hwnd)
                '表記(確定文字が全角英数の場合に使用)
                dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, ByVal 0&, 0)
                ReDim MyByte(dwSize)
                ImmGetCompositionString hIMC, GCS_RESULTSTR, MyByte(0), dwSize
                MyResult$ = MyByte()
                MyResult$ = StrConv(MyResult$, vbUnicode)
                
                If Not MyResult$ = Chr$(0) Then
                    'よみ
                    dwSize = ImmGetCompositionString(hIMC, GCS_RESULTREADSTR, ByVal 0&, 0)
                    ReDim MyByte(dwSize)
                    ImmGetCompositionString hIMC, GCS_RESULTREADSTR, MyByte(0), dwSize
                    MyRead$ = MyByte()
                    MyRead$ = StrConv(MyRead$, vbUnicode)
                    '追加する文字種の判断
                    If chrIsNarrow(StrConv(MyResult$, vbNarrow)) Then MyStr$ = MyResult$ Else MyStr$ = MyRead$
                    'テキストを追加
                    ConvMode = IIf(m_StrConvMode = 0, vbNarrow, m_StrConvMode)
                    MyStr$ = Left$(MyStr$, Len(MyStr$) - 1)  '最後のNullを取る
                    MyStr$ = StrConv(MyStr$, ConvMode)        'ふりがなの種類
                    'ふりがなの挿入位置
                    If m_txtSrc.SelStart > 0 Then m_txtDest.Text = m_txtDest.Text & MyStr$     '末尾にふりがなを追加
                    If m_txtSrc.SelStart = 0 Then m_txtDest.Text = MyStr$ & m_txtDest.Text     '先頭の場合はふりがなも先頭に追加
                End If
                ImmReleaseContext hwnd, hIMC
            Case WM_CHAR    '半角英語
                hIMC = ImmGetContext(hwnd)
                If ImmGetOpenStatus(hIMC) = 0 Then              '半角入力モードのときだけ
                    If wParam >= 32 Then                        '制御キャラクタは無視
                        m_txtDest.Text = m_txtDest.Text & Chr$(wParam)
                    End If
                End If
                Call ImmReleaseContext(hwnd, hIMC)
            Case WM_KEYUP   '操作キー(連動消去)
                If wParam = vbKeyBack Or wParam = vbKeyDelete Then  'キーでの削除監視
                    If m_txtSrc.Text = "" Then m_txtDest.Text = ""
                End If
        End Select
        fCalling = False
    End If

    pWindowProc = CallWindowProc(m_lpOrg, hwnd, uMsg, wParam, lParam)
End Function

Public Function chrIsNarrow(Target As String) As Boolean

    Dim NowPt&, nPt&, NowCode&
    Dim fNarrow As Boolean

    fNarrow = True
    nPt = Len(Target)
    For NowPt = 1 To nPt
        NowCode = Asc(Mid$(Target, NowPt, 1))
        If NowCode < 0 Or NowCode > 255 Then
            fNarrow = False
            Exit For
        End If
    Next NowPt

    chrIsNarrow = fNarrow

End Function

[ツリー表示へ]
タイトルRe^3: SetWindowLongの動作
記事No11854
投稿日: 2017/07/03(Mon) 19:25
投稿者魔界の仮面弁士
# KH さん、まだ見てるかな…?

> 教えていただきましたYomigana Frameworkや、データ型の違いなど
> 調べてみようと思います。

NativeWindow クラス経由でサブクラス化した実装が、下記にありました。
先の Yomigana Framework と比べてみてください。
http://www.atmarkit.co.jp/fdotnet/dotnettips/875imeyomi/imeyomi.html


> あと以下のソースがVB6で動作していた元のソースになりますので、
> ご確認よろしくお願いします。

サブクラス化の手順、DoEvents の不用意な利用とか、Unicode 非対応など、
そのまま VB.NET 向けに直訳するには、少々問題がありそうなコードでしたので
意訳して書き換えてみました。折角なのでサンプル投稿用掲示板に載せておきます。

http://hanatyan.sakura.ne.jp/patio/read.cgi?no=345

[ツリー表示へ]