如何即時偵測剪貼簿變化? |
|
領航天使
站長 發表:12216 回覆:4186 積分:4084 註冊:2001-07-25 發送簡訊給我 |
文章來源:
http://www.delphidabbler.com/articles.php?article=9 How to get notified when the content of the clipboard changes
Display printable version Why do it?
It is often useful to know when the clipboard's contents have changed. For example you may wish to enable or disable a Paste button on a toolbar according to whether the clipboard contains data in a format supported by your application or not. Alternatively you may wish to display details of the items on the clipboard. How it's done
Overview
Windows provides a method for applications to hook into the clipboard to receive notifications when its contents change. This is done by registering one of the application's windows to receive messages when the clipboard changes. These windows are known as clipboard viewers. Windows maintains a linked list of clipboard viewers – the clipboard chain. Each viewer window is responsible for passing on notifications to any window that follows it in the chain. Since Windows relies on the cooperation of viewer applications, a badly behaved application can bring down the whole notification system. It is therefore important to play by the rules. The viewer window should also be un-registered before the associated application terminates. When the content of the clipboard changes, Windows notifies the first window in the chain by sending it a WM_DRAWCLIPBOARD message. This window is responsible for passing the message to the next registered viewer window, and so on down the list. Consequently it can be seen that each clipboard viewer needs to record the identity of the next window in the chain. Windows also notifies the windows in the clipboard chain when clipboard viewers are removed from the list. This is done by passing a WM_CHANGECBCHAIN message to the first window in the chain. The parameters of WM_CHANGECBCHAIN identify the window being removed along with the window that follows it in the chain. The message is passed along the chain until the window preceding that being removed is found. That window then updates its record of the next window in the chain. In summary, here are the key steps in creating, managing and deleting a clipboard viewer: Register the viewer window by calling the SetClipboardViewer API function. This function returns the handle of the next window in the chain (or 0 if no such window)
Keep track of viewer windows that are removed by processing the WM_CHANGECBCHAIN message and passing it along the chain or updating the record of the next window in the chain as necessary.
Respond to clipboard changes by handling the WM_DRAWCLIPBOARD message and passing the message along the chain.
Before closing the application remove the viewer window from the clipboard chain by calling the ChangeClipboardChain API function.
Registering and un-registering the clipboard viewer
To register our window as a clipboard viewer we call the SetClipboardViewer API function. This returns the handle of the next window in the viewer chain which we must record for future reference (this handle may be 0 if we are the first viewer in the chain): var
OurWnd: HWND; // handle of window we are registering
NextWnd: HWND; // handle of next window in chain
...
NextWnd := SetClipboardViewer(OurWnd);Listing 1
To un-register our viewer window we use the ChangeClipboardChain API function as follows: ChangeClipboardChain(OurWnd, NextWnd);Listing 2
Notice how we pass the handle of our window to the function, along with the handle of the next window in the chain. Responding to clipboard messages
Every clipboard viewer must respond to the two clipboard messages discussed above. The window procedure of the viewer window must handle the messages as follows (where Msg is the message number and LParam and WParam are the parameters passed with the message):
case Msg of ... WM_CHANGECBCHAIN: begin // A window has been detached from clipboard chain if HWND(WParam) = NextWnd then // our next window has changed: record it // we don't pass the message on since we handled it NextWnd := HWND(LParam) else // our next window has not changed: pass message on // if next window exists (i.e. handle <> 0) if NextWnd <> 0 then SendMessage(NextWnd, WM_CHANGECBCHAIN, WParam, LParam); end; WM_DRAWCLIPBOARD: begin // Clipboard content has changed // do something here to handle change in clipboard ... // now pass message along if next window exists if NextWnd <> 0 then SendMessage(NextWnd, WM_DRAWCLIPBOARD, WParam, LParam); end; ... end;Listing 3 The WParam value of WM_CHANGECBCHAIN is the handle of the window to be removed while the LParam value is the handle of the window that follows it in the chain (or 0 if there is no following window). If the window that follows ours in the chain is the one to be removed we replace our record of its handle with the handle of the following window. In this case the message has been handled and there is no need to pass it further down the chain. However, if the window being removed is not the next window in the chain we simply forward the message on to the next window (if it exists). When a WM_DRAWCLIPBOARD message is received we first take some suitable action in response to the change and then pass the message and parameters on to the next window (if any) in the chain. Example project An example project is presented below. This project implements a very basic text editor that has buttons to cut, copy and paste text. The Paste button is enabled only when there is text on the clipboard. In order to implement the Paste button functionality, the program's main window is registered as a clipboard viewer. The Copy and Cut buttons are enabled only when some text is selected in the edit control. These buttons are presented only for completeness and their workings are not relevant to the purpose of this article. The demo application's source code is presented below. A zip file containing the source is also available for download. Project file The demo's project file – CBEdit.dpr – is defined below: program CBEdit; uses Forms, FmEditor in 'FmEditor.pas' {EditorForm}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TEditorForm, EditorForm); Application.Run; end.Listing 4 Form Unit The form is defined in a file named FmEditor.dfm as follows (nonessential items have been removed): object EditorForm: TEditorForm Left = 190 Top = 107 Width = 696 Height = 480 Caption = 'EditorForm' OnCreate = FormCreate OnDestroy = FormDestroy object Panel1: TPanel Align = alTop object btnCut: TButton Left = 8 Top = 8 Caption = 'Cut' OnClick = btnCutClick end object btnCopy: TButton Left = 88 Top = 8 Caption = 'Copy' OnClick = btnCopyClick end object btnPaste: TButton Left = 168 Top = 8 Caption = 'Paste' OnClick = btnPasteClick end end object RichEdit1: TRichEdit Align = alClient OnSelectionChange = RichEdit1SelectionChange end end.Listing 5 Finally, the code associated with the form is stored in FmEditor.pas and is as follows: unit FmEditor; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls, Clipbrd; type TEditorForm = class(TForm) Panel1: TPanel; btnCut: TButton; btnCopy: TButton; btnPaste: TButton; RichEdit1: TRichEdit; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure RichEdit1SelectionChange(Sender: TObject); procedure btnCutClick(Sender: TObject); procedure btnCopyClick(Sender: TObject); procedure btnPasteClick(Sender: TObject); private // next window in clipboard viewer chain fNextViewer: HWND; // handles change in clipboard chain procedure WMChangeCBChain(var Msg: TMessage); message WM_CHANGECBCHAIN; // handles change in clipboard content procedure WMDrawClipboard(var Msg: TMessage); message WM_DRAWCLIPBOARD; end; var EditorForm: TEditorForm; implementation {$R *.DFM} { Button event handlers } procedure TEditorForm.btnCopyClick(Sender: TObject); begin RichEdit1.CopyToClipboard; end; procedure TEditorForm.btnCutClick(Sender: TObject); begin RichEdit1.CutToClipboard; end; procedure TEditorForm.btnPasteClick(Sender: TObject); begin RichEdit1.PasteFromClipboard; end; { Window creation and destruction event handlers } procedure TEditorForm.FormCreate(Sender: TObject); begin // Enabled / disable buttons at start-up btnPaste.Enabled := Clipboard.HasFormat(CF_TEXT); btnCut.Enabled := RichEdit1.SelLength > 0; btnCopy.Enabled := btnCut.Enabled; // Register main window in clipboard viewer chain // we record handle of next viewer window in chain fNextViewer := SetClipboardViewer(Self.Handle); end; procedure TEditorForm.FormDestroy(Sender: TObject); begin // We're closing down: remove from clipboard chain ChangeClipboardChain(Self.Handle, fNextViewer); end; { Editor change event hander: updates copy/cut button } procedure TEditorForm.RichEdit1SelectionChange(Sender: TObject); begin btnCut.Enabled := RichEdit1.SelLength > 0; btnCopy.Enabled := RichEdit1.Enabled; end; { Clipboard change event handlers } procedure TEditorForm.WMChangeCBChain(var Msg: TMessage); begin // Clipboard chain changed if HWND(Msg.WParam) = fNextViewer then // next window was removed: record following window as next fNextViewer := HWND(Msg.LParam) else if fNextViewer <> 0 then // no change to next window: pass message on to next window SendMessage(fNextViewer, WM_CHANGECBCHAIN, Msg.WParam, Msg.LParam); end; procedure TEditorForm.WMDrawClipboard(var Msg: TMessage); begin // Clipboard content changed // enable paste button if text on clipboard btnPaste.Enabled := Clipboard.HasFormat(CF_TEXT); // pass message on the next viewer in clipboard chain, if any if fNextViewer <> 0 then SendMessage(fNextViewer, WM_DRAWCLIPBOARD, Msg.WParam, Msg.LParam); end; end.
------
~~~Delphi K.Top討論區站長~~~
附加檔案:76483_clipboard.rar
|
bugmans
高階會員 發表:95 回覆:322 積分:188 註冊:2003-04-12 發送簡訊給我 |
這個範例只抓剪貼簿的文字,在codeproject網站有個更強大的範例ClipSpy
http://www.codeproject.com/clipboard/clipspy.asp 這範例也是擷取WM_DRAWCLIPBOARD和WM_CHANGECBCHAIN這兩個訊息 要抓剪貼簿目前有哪些格式的資料,透過MFC的COleDataObject,呼叫GetNextFormat函式取得目前剪貼簿擁有的格式 我花了一些時間用BCB5寫個間單的範例,示範了如何取得目前的剪貼簿格式 <textarea class="cpp" rows="10" cols="60" name="code"> 先加入#include
編輯記錄
bugmans 重新編輯於 2007-04-28 22:40:17, 註解 無‧
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |