Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8901

C#でClipboard監視(Windows API)

$
0
0

追記

記事を書いたものの、Vista以降は別の方法(AddClipboardFormatListener)のほうが安定する(チェイン管理をOSがやってくれる)ようである。
クリップボードの監視
https://gist.github.com/glombard/7986317


概略

Windows APIであるSetClipboardViewerChangeClipboardChainを使ってクリップボード監視チェインへの登録・削除をし、
WindowメッセージWM_DRAWCLIPBOARDWM_CHANGECBCHAINを処理して、クリップボードの変更通知受け取りと、チェインの維持を行う。
キーボードフックとかに近い構造のようだが、チェインの維持をしないといけない点が異なる。

サンプルコード

参考サイトの1つ目をベースに作成。1
文字列をクリップボードにコピーするたびに、コンソールにその文字列を出力します。

usingSystem;usingSystem.Windows.Forms;usingSystem.Runtime.InteropServices;usingSystem.Security.Permissions;publicclassClipboardEventArgs:EventArgs{privatestringtext;publicstringText{get{returnthis.text;}}publicClipboardEventArgs(stringstr){this.text=str;}}publicdelegatevoidcbEventHandler(objectsender,ClipboardEventArgsev);[PermissionSet(SecurityAction.Demand,Name="FullTrust")]internalclassMyClipboardListener:NativeWindow{classNativeMethods{[DllImport("user32")]publicstaticexternIntPtrSetClipboardViewer(IntPtrhWndNewViewer);[DllImport("user32")]publicstaticexternboolChangeClipboardChain(IntPtrhWndRemove,IntPtrhWndNewNext);[DllImport("user32")]publicexternstaticintSendMessage(IntPtrhWnd,intMsg,IntPtrwParam,IntPtrlParam);}privateconstintWM_DRAWCLIPBOARD=0x0308;privateconstintWM_CHANGECBCHAIN=0x030D;privateIntPtrnextHandle;privateIntPtrnwHandle;//private Form parent;publiceventcbEventHandlerClipboardHandler;publicMyClipboardListener(Formf){f.HandleCreated+=OnHandleCreated;f.HandleDestroyed+=OnHandleDestroyed;//this.parent = f;}internalvoidOnHandleCreated(objectsender,EventArgse){AssignHandle(((Form)sender).Handle);// NativeWindowクラスへのForm登録(メッセージフック開始)nwHandle=this.Handle;nextHandle=NativeMethods.SetClipboardViewer(this.Handle);// クリップボードチェインに登録}internalvoidOnHandleDestroyed(objectsender,EventArgse){NativeMethods.ChangeClipboardChain(this.Handle,nextHandle);// クリップボードチェインから削除ReleaseHandle();// NativeWindowクラスの後始末(Formに対してのメッセージフック解除)}// ハンドル変更されるシーンがあるか不明。//override void OnHandleChange()//{//    ChangeClipboardChain(nwHandle, nextHandle); // クリップボードチェインから削除//    nwHandle = this.Handle;//    nextHandle = SetClipboardViewer(this.Handle); // クリップボードチェインに登録//}protectedoverridevoidWndProc(refMessagemsg){switch(msg.Msg){caseWM_DRAWCLIPBOARD:if(Clipboard.ContainsText()){// Note: ここを変更すれば、テキスト以外も通知可能// クリップボードの内容がテキストの場合のみif(ClipboardHandler!=null){// クリップボードの内容を取得してハンドラを呼び出すClipboardHandler(this,newClipboardEventArgs(Clipboard.GetText()));}}if(nextHandle!=IntPtr.Zero){NativeMethods.SendMessage(nextHandle,msg.Msg,msg.WParam,msg.LParam);}break;// クリップボード・ビューア・チェーンが更新されたcaseWM_CHANGECBCHAIN:if(msg.WParam==nextHandle){nextHandle=msg.LParam;}elseif(nextHandle!=IntPtr.Zero){NativeMethods.SendMessage(nextHandle,msg.Msg,msg.WParam,msg.LParam);}break;}base.WndProc(refmsg);}}publicclassClipboardMonitorTest:Form{MyClipboardListenerviewer;publicClipboardMonitorTest(){viewer=newMyClipboardListener(this);viewer.ClipboardHandler+=this.OnClipBoardChanged;// イベントハンドラを登録}// クリップボードにテキストがコピーされると呼び出されるprivatevoidOnClipBoardChanged(objectsender,ClipboardEventArgsargs){Console.WriteLine(args.Text);}[STAThread]staticvoidMain(string[]args){Application.Run(newClipboardMonitorTest());}}

注意点

試してみたところでは、SetClipboardViewerに登録すると、ユーザーがクリップボードにコピーする操作をしていなくても、クリップボードの更新通知(WM_DRAWCLIPBOARD)が来るようである。

参考サイト

  1. .NET TIPS クリップボードの内容をリアルタイムに取得するには?[C#、VB] - @IT
  2. クリップボードビューアを作る - Wisdom Soft・・・WindowsAPIの解説
  3. NativeWindowクラス - Microsoft Docs

  1. そのままだと64bit環境でハマる可能性あり。IntPtrintにキャストして0と比較しているとこは、intキャストをやめてIntPtr.Zeroとの比較に修正した。 


Viewing all articles
Browse latest Browse all 8901

Trending Articles