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

わんくま同盟

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

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

ツリー一括表示

クリップボードの内容をリアルタイムに取得する方法 /こここ (24/03/12(Tue) 20:22) #103004
Re[1]: クリップボードの内容をリアルタイムに取得する方法 /KOZ (24/03/13(Wed) 00:21) #103005
  └ Re[2]: クリップボードの内容をリアルタイムに取得する方法 /こここ (24/03/13(Wed) 16:09) #103010
    └ Re[3]: クリップボードの内容をリアルタイムに取得する方法 /KOZ (24/03/13(Wed) 19:11) #103012
      └ Re[4]: クリップボードの内容をリアルタイムに取得する方法 /こここ (24/03/22(Fri) 11:58) #103024 解決済み


親記事 / ▼[ 103005 ]
■103004 / 親階層)  クリップボードの内容をリアルタイムに取得する方法
□投稿者/ こここ (1回)-(2024/03/12(Tue) 20:22:54)

分類:[.NET 全般] 

Win11でVB.NET2015の環境で
プログラム開発を行っております。


プログラムを起動した状態で
テキストデータをCtrl+Cキーでコピーすると
自動でプログラムがコピーしたテキストの内容を処理して
クリップボードのテキストデータを別の内容に書き換えるプログラムを作っています。

リアルタイムでクリップボードを取得する方法に関しては
https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/848cbviewer/cbviewer.html
このページのコードでうまくいきました。


しかし、10〜20回に1回くらいの頻度で
テキストデータが取得できなかったり、
取得するのに10秒以上かかったりするなど
不思議な挙動をしています。

一体、なぜタイミングによってうまくいったり
うまくいかなかったりするのでしょうか?




[ □ Tree ] 返信 編集キー/

▲[ 103004 ] / ▼[ 103010 ]
■103005 / 1階層)  Re[1]: クリップボードの内容をリアルタイムに取得する方法
□投稿者/ KOZ (438回)-(2024/03/13(Wed) 00:21:33)
> リアルタイムでクリップボードを取得する方法に関しては
> https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/848cbviewer/cbviewer.html
> このページのコードでうまくいきました。


これは少し古いやり方です。
Windows Vista 以降であれば AddClipboardFormatListener/RemoveClipboardFormatListener を使って、もっと楽に制御できます。

「AddClipboardFormatListener 関数」
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener

「RemoveClipboardFormatListener 関数」
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-removeclipboardformatlistener


> しかし、10〜20回に1回くらいの頻度で
> テキストデータが取得できなかったり、
> 取得するのに10秒以上かかったりするなど
> 不思議な挙動をしています。


内容を書き換える部分はどのようにコーディングしているんでしょうか?
書き換えたことによりイベントが発生して無限ループに入ったりしていませんか?

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

▲[ 103005 ] / ▼[ 103012 ]
■103010 / 2階層)  Re[2]: クリップボードの内容をリアルタイムに取得する方法
□投稿者/ こここ (2回)-(2024/03/13(Wed) 16:09:11)
ありがとうございます。

まだ、新しいコードは試していないのですが、


Invoke(Sub()

GetClipboardText = Clipboard.GetText()

End Sub)


のコードでクリップボードの内容を取得した後に、


BeginInvoke(Sub()

Clipboard.SetText(" ")

End Sub)

のコードで、クリップボードの内容を書き換えています。

もしかして、書き換えたことで
再度クリップボードの取得プログラムが呼ばれているということがあり得るのでしょうか?

この場合、例えば、ブーリアン変数を宣言しておいて、
更新するまではリアルタイムでの取得を行わないようにするなどの
必要がありますか?


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

▲[ 103010 ] / ▼[ 103024 ]
■103012 / 3階層)  Re[3]: クリップボードの内容をリアルタイムに取得する方法
□投稿者/ KOZ (439回)-(2024/03/13(Wed) 19:11:54)
2024/03/13(Wed) 19:14:44 編集(投稿者)
No103010 (こここ さん) に返信

イベントはメッセージキューを介して通知されるので Invoke や BeginInvoke は必要ないです。

> もしかして、書き換えたことで
> 再度クリップボードの取得プログラムが呼ばれているということがあり得るのでしょうか?

呼ばれます。
書き換える前にフックを解除し、書き換えた後にフックを再開すればOKです。

下にサンプルを書きますが、クリップボードを他のプログラムが開いていると、Clipboard のメソッドは普通に失敗することがあるので、間隔を置いてリトライするなどしてください。


Imports System.Runtime.InteropServices

Public Class Form1

    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        MyBase.OnHandleCreated(e)
        AddClipboardFormatListener(Handle)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        Select Case m.Msg
            Case WM_NCDESTROY
                MyBase.WndProc(m)
                RemoveClipboardFormatListener(Handle)

            Case WM_CLIPBOARDUPDATE
                RemoveClipboardFormatListener(Handle)
                If Clipboard.ContainsText() Then
                    Dim clipboardText As String = Clipboard.GetText()
                    Dim replaceString = YourTask(clipboardText)
                    If clipboardText <> replaceString Then
                        Clipboard.Clear()
                        Clipboard.SetText(replaceString)
                    End If
                End If
                AddClipboardFormatListener(Handle)

            Case Else
                MyBase.WndProc(m)
        End Select
    End Sub

    Private Function YourTask(clipboardText As String) As String
        Return clipboardText & "A"
    End Function

    Private Const WM_NCDESTROY As Integer = &H82
    Private Const WM_CLIPBOARDUPDATE As Integer = &H31D

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function AddClipboardFormatListener(hwnd As IntPtr) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function RemoveClipboardFormatListener(hwnd As IntPtr) As Boolean
    End Function

End Class

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

▲[ 103012 ] / 返信無し
■103024 / 4階層)  Re[4]: クリップボードの内容をリアルタイムに取得する方法
□投稿者/ こここ (3回)-(2024/03/22(Fri) 11:58:06)
ありがとうございます。
こんな方法があったのですね
うまくいきました
解決済み
[ 親 103004 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -