.NET TIPS

クリップボードの内容をリアルタイムに取得するには?[C#、VB]

デジタルアドバンテージ 岸本 真二郎
2009/03/05

 .NET Frameworkによるプログラミングでは、Clipboardクラス(System.Windows名前空間)を用いて、テキストや画像をクリップボードにセットしたり、逆にクリップボードから取得したりできる。クリップボードの標準的なアクセス方法については、「TIPS:クリップボードへデータを送るには?」や「TIPS:クリップボードからデータを受け取るには?」などで解説している。

 しかしながら、クリップボードの変更をリアルタイムに検知して、クリップボードの内容を取得するような処理は、Clipboardクラスだけでは実現できない。これには、Win32 APIに含まれるSetClipboardViewerという関数を呼び出すことにより、アプリケーションをクリップボードのビューアとして登録し、さらにWindowsメッセージを直接処理することで、クリップボードにデータが登録されたことを検知できる。

 ここでは、NativeWindowクラス(System.Windows.Forms名前空間)を用いることにより、極力Windowsメッセージを意識しないで、リアルタイムにクリップボードからテキストを取得する例を示す。クリップボードにデータが登録(コピー)されたら、イベントハンドラを用いてクリップボードの内容を引き渡す。

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ClipboardViewer
{
  public class ClipboardEventArgs : EventArgs
  {
    private string text;

    public string Text
    {
      get { return this.text; }
    }

    public ClipboardEventArgs(string str)
    {
      this.text = str;
    }
  }

public delegate void cbEventHandler(
                        object sender, ClipboardEventArgs ev);

  [System.Security.Permissions.PermissionSet(
        System.Security.Permissions.SecurityAction.Demand,
        Name = "FullTrust")]
  internal class MyClipboardViewer : NativeWindow
  {
    [DllImport("user32")]
    public static extern IntPtr SetClipboardViewer(
            IntPtr hWndNewViewer);

    [DllImport("user32")]
    public static extern bool ChangeClipboardChain(
            IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32")]
    public extern static int SendMessage(
            IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

    private const int WM_DRAWCLIPBOARD = 0x0308;
    private const int WM_CHANGECBCHAIN = 0x030D;
    private IntPtr nextHandle;

    private Form parent;
    public event cbEventHandler ClipboardHandler;

    public MyClipboardViewer(Form f)
    {
      f.HandleCreated
                  += new EventHandler(this.OnHandleCreated);
      f.HandleDestroyed
                  += new EventHandler(this.OnHandleDestroyed);
      this.parent = f;
    }

    internal void OnHandleCreated(object sender, EventArgs e)
    {
      AssignHandle(((Form)sender).Handle);
      // ビューアを登録
      nextHandle = SetClipboardViewer(this.Handle);
    }

    internal void OnHandleDestroyed(object sender, EventArgs e)
    {
      // ビューアを解除
      bool sts = ChangeClipboardChain(this.Handle, nextHandle);
      ReleaseHandle();
    }

    protected override void WndProc(ref Message msg)
    {
      switch (msg.Msg) {

        case WM_DRAWCLIPBOARD:
          if (Clipboard.ContainsText()) {
            // クリップボードの内容がテキストの場合のみ
            if (ClipboardHandler != null) {
              // クリップボードの内容を取得してハンドラを呼び出す
              ClipboardHandler(this,
                  new ClipboardEventArgs(Clipboard.GetText()));
            }
          }
          if ((int)nextHandle != 0)
            SendMessage(
                nextHandle, msg.Msg, msg.WParam, msg.LParam);
          break;

        // クリップボード・ビューア・チェーンが更新された
        case WM_CHANGECBCHAIN:
          if (msg.WParam == nextHandle) {
            nextHandle = (IntPtr)msg.LParam;
          } else if((int)nextHandle != 0)
            SendMessage(
                nextHandle, msg.Msg, msg.WParam, msg.LParam);
          break;
      }
      base.WndProc(ref msg);
    }
  }
}
Imports System.Windows.Forms
Imports System.Runtime.InteropServices

Public Class ClipboardEventArgs
  Inherits EventArgs
  Private m_text As String

  Public ReadOnly Property Text() As String
    Get
      Return Me.m_text
    End Get
  End Property

  Public Sub New(ByVal str As String)
    Me.m_text = str
  End Sub
End Class

Public Delegate Sub cbEventHandler(ByVal sender As Object, _
     ByVal ev As ClipboardEventArgs)

<System.Security.Permissions.PermissionSet( _
    System.Security.Permissions.SecurityAction.Demand, _
    Name:="FullTrust")> _
Friend Class MyClipboardViewer
  Inherits NativeWindow

  <DllImport("user32")> _
  Public Shared Function SetClipboardViewer( _
        ByVal hWndNewViewer As IntPtr) As IntPtr
  End Function

  <DllImport("user32")> _
  Public Shared Function ChangeClipboardChain( _
        ByVal hWndRemove As IntPtr, _
        ByVal hWndNewNext As IntPtr) As Boolean
  End Function

  <DllImport("user32")> _
  Public Shared Function SendMessage( _
        ByVal hWnd As IntPtr, ByVal Msg As Integer, _
        ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
  End Function

  Private Const WM_DRAWCLIPBOARD As Integer = &H308
  Private Const WM_CHANGECBCHAIN As Integer = &H30D
  Private nextHandle As IntPtr

  Private parent As Form
  Public Event ClipboardHandler As cbEventHandler

  Public Sub New(ByVal parent As Form)
    AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
    AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
    Me.parent = parent

  End Sub

  Friend Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
    AssignHandle(DirectCast(sender, Form).Handle)
    ' ビューアを登録
    nextHandle = SetClipboardViewer(Me.Handle)
  End Sub

  Friend Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
    ' ビューアを解除
    Dim sts As Boolean = ChangeClipboardChain(Me.Handle, nextHandle)
    ReleaseHandle()
  End Sub

  Protected Overloads Overrides Sub WndProc(ByRef msg As Message)
    Select msg.Msg

      Case WM_DRAWCLIPBOARD
        ' クリップボードの内容がテキストの場合
        If Clipboard.ContainsText() Then
          ' クリップボードの内容を取得してハンドラを呼び出す
          RaiseEvent ClipboardHandler( _
                  Me, New ClipboardEventArgs(Clipboard.GetText()))
        End If

        If CInt(nextHandle) <> 0 Then
          SendMessage(nextHandle, msg.Msg, msg.WParam, msg.LParam)
        End If
        Exit Select

      ' クリップボード・ビューア・チェーンが更新された
      Case WM_CHANGECBCHAIN
        If msg.WParam = nextHandle Then
          nextHandle = DirectCast(msg.LParam, IntPtr)
        ElseIf CInt(nextHandle) <> 0 Then
          SendMessage(nextHandle, msg.Msg, msg.WParam, msg.LParam)
        End If
        Exit Select

    End Select
    MyBase.WndProc(msg)
  End Sub
End Class
リアルタイムにクリップボードの内容を通知する処理(上:C#、下:VB)
クリップボード・ビューア・チェーンとは、本プログラムのようなクリップボードを監視するアプリケーションが登録されているリストで、Windowsにより管理されている。

 この処理では、まずClipboardEventArgsクラスを定義して、イベントハンドラの引数として使用するクラスを定義する。この中のTextプロパティを使って、クリップボードのテキストを取得する。

 MyClipboardViewerクラスは、NativeWindowの派生クラスとして宣言する。これにより、このクラスを“サブクラス化”できる。サブクラス化することで、元のウィンドウに送られてくるWindowsメッセージをサブクラス(ここではMyClipboardViewer)側で先に受け取ることができ、元のウィンドウのメッセージ処理には手を加えずに、Windowsメッセージ(ここで利用するのはクリップボード関連のメッセージ)を処理できるようになる。

 このMyClipboardViewerクラスでは、Win32 APIのSetClipboardViewer関数を用いて、クリップボードのビューアを登録する。そして、Windowsメッセージ・ハンドラ(WndProcメソッド)でクリップボードにデータが登録された際(WM_DRAWCLIPBOARDメッセージが送られたとき)に、クリップボードからテキストを取り出して、イベントハンドラを使って文字列を渡すようにしている。

 このクラスを利用する側は、MyClipboardViewerオブジェクトの作成と、そのClipboardHandlerイベントのイベントハンドラの設定を行っておけば、クリップボードにテキストがコピーされるたびにイベントハンドラが呼び出されるようになる。

using System;
using System.Windows.Forms;

namespace ClipboardViewer
{
  public partial class Form1 : Form
  {
    private MyClipboardViewer viewer;

    public Form1()
    {
      viewer = new MyClipboardViewer(this);

      // イベントハンドラを登録
      viewer.ClipboardHandler += this.OnClipBoardChanged;
      InitializeComponent();
    }

    // クリップボードにテキストがコピーされると呼び出される
    private void OnClipBoardChanged(object sender, ClipboardEventArgs args)
    {
      this.textBox1.Text = args.Text;
    }
  }
}
Public Class Form1
  Private viewer As MyClipboardViewer

  Public Sub New()
    viewer = New MyClipboardViewer(Me)
    ' イベントハンドラを登録
    AddHandler viewer.ClipboardHandler, AddressOf OnClipBoardChanged

    ' この呼び出しは、Windows フォーム デザイナで必要です。
    InitializeComponent()
  End Sub

  ' クリップボードにテキストがコピーされると呼び出される
  Private Sub OnClipBoardChanged(ByVal sender As Object, ByVal args As ClipboardEventArgs)
    TextBox1.Text = args.Text
  End Sub

End Class
MyClipboardViewerクラスを利用したWindowsフォーム・アプリケーションの例(上:C#、下:VB)

 ここではまず、フォームのコンストラクタでMyClipboardViewerクラスのインスタンスを生成し、次にイベントハンドラを登録する。例として、上記のイベントハンドラでは、フォームに配置したテキストボックスに、取得したクリップボードの内容をセットしている。

 この例では、クリップボードにテキストがコピーされた場合だけ処理しているが、ほかにも画像がクリップボードにコピーされた場合の処理を追加するには、ClipboardEventArgsクラスに画像を保持するメンバを追加し、WndProcメソッド内で画像を取得するようにすればよい。End of Article

カテゴリ:Windowsフォーム 処理対象:クリップボード
カテゴリ:クラス・ライブラリ 処理対象:Win32 API
使用ライブラリ:Clipboardクラス(System.Windows名前空間)
使用ライブラリ:NativeWindowクラス(System.Windows.Forms名前空間)
関連TIPS:クリップボードへデータを送るには?
関連TIPS:クリップボードからデータを受け取るには?

この記事と関連性の高い別の.NET TIPS
クリップボードへデータを送るには?
クリップボードからデータを受け取るには?
Visual StudioのコードをHTML形式でクリップボードにコピーするには?
Visual Studioでクリップボード・リングを使用するには?
WindowsフォームでWindowsメッセージを直接処理するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間