全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:4199
推到 Plurk!
推到 Facebook!

如何即時偵測剪貼簿變化?

 
領航天使
站長


發表:12216
回覆:4186
積分:4084
註冊:2001-07-25

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-08-09 15:37:30 IP:220.134.xxx.xxx 未訂閱
文章來源: 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

發送簡訊給我
#2 引用回覆 回覆 發表時間:2007-04-28 22:37:21 IP:125.225.xxx.xxx 未訂閱
這個範例只抓剪貼簿的文字,在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 到Unit1.h int Format[]={CF_TEXT,CF_BITMAP,CF_METAFILEPICT,CF_SYLK, CF_DIF,CF_TIFF,CF_OEMTEXT,CF_DIB,CF_PALETTE,CF_PENDATA, CF_RIFF,CF_WAVE,CF_UNICODETEXT,CF_ENHMETAFILE,CF_LOCALE}; char *FormatName[]={"CF_TEXT","CF_BITMAP","CF_METAFILEPICT", "CF_SYLK","CF_DIF","CF_TIFF","CF_OEMTEXT","CF_DIB", "CF_PALETTE","CF_PENDATA","CF_RIFF","CF_WAVE","CF_UNICODETEXT", "CF_ENHMETAFILE","CF_LOCALE"}; char szFormat [256]; int i,j,FormatNum=sizeof(Format)/sizeof(int); for(i=0 ; iFormatCount ; i ) {for(j=0 ; j<FormatNum ; j ) {if(Clipboard()->Formats[i]==Format[j]) {ListBox1->Items->Add(FormatName[j]); break; } } if(j==FormatNum) {GetClipboardFormatName(Clipboard()->Formats[i], szFormat, 256 ); ListBox1->Items->Add(szFormat); } } </textarea>

編輯記錄
bugmans 重新編輯於 2007-04-28 22:40:17, 註解 無‧
系統時間:2024-05-03 6:27:51
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!