La técnica que he seguido para el primer ejemplo es compatible desde Windows 2000 en adelante y requiere tratar varios mensajes tras colocarnos en la lista de visores del Clipboard. El código está comentado para comprender bien como y porqué tratamos los mensajes.
El corazón es el mensaje WM_DRAWCLIPBOARD que recibiremos cuando se produzcan cambios en el Clipboard, tanto al cambiar éste como al entrar otro visor en la cola.
Código delphi:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Clipbrd; type TForm1 = class(TForm) Memo1: TMemo; procedure FormCreate(Sender: TObject); private NextViewer: HWND; procedure GetData; protected procedure WndProc(var Message: TMessage); override; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin //Clipboard.Clear; // Podemos limpiar o no el clipboard al comenzar // Nos registramos como ClipboardViewer y guardamos el anterior de la cadena NextViewer:= SetClipboardViewer(Handle); end; // Procesamos los mensajes a bajo nivel procedure TForm1.WndProc(var Message: TMessage); begin case Message.Msg of WM_DRAWCLIPBOARD: begin // Si tenemos un visor en la cadena, le reenviamos el mensaje if NextViewer <> 0 then SendMessage(NextViewer, WM_DRAWCLIPBOARD, 0, 0); // Miramos el ClipBoard GetData(); end; WM_DESTROY: // Notificamos que dejamos de ser visor del ClipBoard ChangeClipboardChain(Handle, NextViewer); WM_CHANGECBCHAIN: // Nos notifican que otra App deje de ser visor del ClipBoard if HWND(Message.WParam) = NextViewer then // Alctualizamos NextViewer NextViewer:= Message.LParam else if NextViewer <> 0 then // Si no se actualizó reenviamos el mensaje SendMessage(NextViewer, WM_CHANGECBCHAIN, Message.WParam, Message.LParam); end; inherited WndProc(Message); end; procedure TForm1.GetData; begin if Clipboard.HasFormat(CF_TEXT) then Memo1.Lines.Add(Clipboard.AsText); end; end.
El equivalente en BuilderC++ sería el siguiente:
//--------------------------------------------------------------------------- #ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TMemo *Memo1; private: // User declarations void __fastcall WndProc(Messages::TMessage &Message); void __fastcall GetData(); public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
#include <vcl.h> #pragma hdrstop #include "Unit1.h" #include <Clipbrd.hpp> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; HWND NextViewer; HWND OldViewer; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { //Clipboard()->Clear(); // Podemos limpiar o no el clipboard al comenzar // Nos registramos como ClipboardViewer y guardamos el anterior de la cadena NextViewer = SetClipboardViewer(Handle); } //--------------------------------------------------------------------------- // Procesamos los mensajes a bajo nivel void __fastcall TForm1::WndProc(Messages::TMessage &Message) { switch (Message.Msg){ case WM_DRAWCLIPBOARD: // Si tenemos un visor en la cadena, le reenviamos el mensaje if(NextViewer!=0) SendMessage(NextViewer, WM_DRAWCLIPBOARD, 0, 0); GetData(); break; case WM_DESTROY: // Notificamos que dejamos de ser visor del ClipBoard ChangeClipboardChain(Handle, NextViewer); break; case WM_CHANGECBCHAIN: // Nos notifican que otra App deje de ser visor del ClipBoard if((HWND)Message.WParam == NextViewer) // Alctualizamos NextViewer NextViewer = (HWND)Message.LParam; else if(NextViewer != NULL) // Si no se actualizó reenviamos el mensaje SendMessage(NextViewer, WM_CHANGECBCHAIN, Message.WParam, Message.LParam); break; } TForm::WndProc(Message); } //--------------------------------------------------------------------------- void __fastcall TForm1::GetData() { if(Clipboard()->HasFormat(CF_TEXT)) Memo1->Lines->Add(Clipboard()->AsText); }
En la siguiente entrada de este hilo explico como hacerlo más sencillo pero a partir de Windows Vista en adelante.
Saludos.