|
Delphi Programming / Object Pascal
[掲載 2011年01月16日] [更新 2022年10月23日] Delphi サンプルプログラム集 |
823_貼り付け操作可否のチェック ( クリップボードの監視 ) |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
一部,Windows 10 ビルド 19043 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32 |
|
823_PasteCheck.zip [4.5 MB] 2022年10月23日版 (EXE 同梱) |
貼り付け操作可否のチェック - クリップボードの監視 |
アプリケーションで [貼り付け] の操作を実装したいことがあります.Clipbrd.pas,ActiveX.pas 内の関数類を使用すると,[貼り付け] の機能を実装することができます.また,関連するインターフェイスを実装すれば,[821_アプリ間のドラッグ・ドロップ] のような機能も実現できます.
[貼り付け] ボタンをアプリケーションに配置したとします.クリップボードに [貼り付け] 可能なデータがある時だけ [貼り付け] ボタンが使用可能な状態となっている状態が望ましいでしょう.そのためにはクリップボードの内容を監視する必要があります.クリップボードの監視は,以下のような手順で行うことになります.
- (1) SetClipboardViewer 関数で,自アプリ等をクリップボードビューワとして登録
- (2) WM_DRAWCLIPBOARD メッセージでクリップボードの変更を受信
- (3) WM_CHANGECBCHAIN メッセージでビューワからの削除等を監視
- (4) 終了時は ChangeClipboardChain 関数でビューワとしての登録を解除
下の表は,クリップボードの監視で検出可能なクリップボードのフォーマットで,2011年01月16日現在,
MSDN の記事に書いてある全ての値です.実際にクリップボードからデータを取り出す方法は,データの種類によって異なります.それらの方法,手順は,ネット上に情報があります.
このページのプログラムは,Windows Vista 以降の場合,IDE 上 (管理者権限) で実行すると正常に動作しないことがあります.そのような場合は,作成した EXE を直接実行してみてください. |
[備考]
Windows Vista 以降では,一般ユーザ (標準ユーザ) がコピーしたクリップボードの内容は,管理者権限で起動しているアプリケーションには貼り付けできません.これは UIPI による制限です.
|
表1
標準クリップボードフォーマット
Delphi 2010 の Windows.pas で定義されているもの
クリップボードのデータの種類は Standard Clipboard Formats (MSDN) の記事による |
CF_BITMAP |
$0002 |
デバイス依存のビットマップハンドル (HBITMAP) を持つデータ |
CF_DIB |
$0008 |
デバイス非依存のビットマップデータ |
CF_DIBV5 |
$0011 |
BITMAPV5HEADER 構造体を持つデバイス非依存のビットマップデータ |
CF_DIF |
$0015 |
ソフトウェアアート社のデータ交換フォーマット |
CF_DSPBITMAP |
$0082 |
プライベート (独自) 形式のビットマップデータ |
CF_DSPENHMETAFILE |
$008E |
プライベート (独自) 形式の拡張メタファイルデータ |
CF_DSPMETAFILEPICT |
$0083 |
プライベート (独自) 形式のメタファイルデータ |
CF_DSPTEXT |
$0081 |
プライベート (独自) 形式のテキストデータ |
CF_ENHMETAFILE |
$000E |
拡張メタファイル |
CF_GDIOBJFIRST |
$0300 |
アプリケーション定義 GDI オブジェクトの整数値の開始
範囲の終わりは CF_GDIOBJLAST |
CF_GDIOBJLAST |
$03FF |
CF_GDIOBJFIRST 参照 |
CF_HDROP |
$000F |
ファイルのリスト
DragQueryFile 関数でファイルに関する情報を取得する |
CF_LOCALE |
$0010 |
データはクリップボード内のテキストに関連付けられているロケール識別子へのハンドル |
CF_METAFILEPICT |
$0003 |
メタファイル画像形式
METAFILEPICT 構造体にデータが格納される |
CF_OEMTEXT |
$0007 |
データは OEM 文字の文字を含むテキスト形式 |
CF_OWNERDISPLAY |
$0080 |
クリップボードの所有者が表示するオーナー表示形式 |
CF_PALETTE |
$0009 |
データはカラーパレットのハンドル |
CF_PENDATA |
$000A |
ペンコンピューティング用
ペン拡張機能のためのデータ |
CF_PRIVATEFIRST |
$0200 |
プライベートクリップボード形式の整数値の範囲の開始
範囲は CF_PRIVATELAST で終了する |
CF_PRIVATELAST |
$02FF |
CF_PRIVATEFIRST参照 |
CF_RIFF |
$000B |
RIFF 形式のオーディオデータ |
CF_SYLK |
$0044 |
マイクロソフトシンボリックリンク (SYLK) 形式のデータ |
CF_TEXT |
$0001 |
テキスト形式のデータ |
CF_TIFF |
$0006 |
TIFF 形式の画像データ |
CF_UNICODETEXT |
$000D |
Unicode のテキスト形式 |
CF_WAVE |
$000C |
11 kHz または 22 kHz の PCM のような標準的な WAVE 形式オーディオデータ |
01_各種コピー操作によるクリップボードのフォーマット |
[貼り付け] は,作成するアプリケーションによって,画像あるいはテキストの場合もあるでしょう.そのため,上の表のどのフォーマットが現在使用可能であるかを検出することになります.
このテストプログラムでは,[コピー] 操作で,どんなフォーマットのデータがクリップボードに格納されるかを確認します.対象のフォーマットが格納された場合に [貼り付け] ボタンを使用可能にすればいいことになります. |
|
図1
設計時画面 |
|
図2
エクスプローラでファイルを [コピー] |
|
図3
ビットマップ画像を [コピー] |
|
図4
メタファイル画像を [コピー] |
|
図5
ワードアートを [コピー] |
リスト1
直前の [コピー] 操作で,クリップボードに格納されるフォーマットを調べるテスト
クリックボードのデータ取得のサンプルではないので注意 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
NextWindow : HWND;
procedure WMChangeCBChain(var Message: TWMChangeCBChain); message
WM_CHANGECBCHAIN;
procedure WMDrawClipboard(var Message: TWMDrawClipboard); message
WM_DRAWCLIPBOARD;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses Clipbrd;
{$R *.dfm}
//=============================================================================
// このフォームをクリップボードビューワとして登録
// NextWindowは次のクリップボードビューワウィンドウのハンドル
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
NextWindow := SetClipboardViewer(Handle);
end;
//=============================================================================
// クリップボードビューワとしての登録を解除
// クリップボード監視のチェインから外す
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
ChangeClipboardChain(Handle, NextWindow);
end;
//=============================================================================
// クリップボードを空にする
// 単なる動作確認用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Clipboard.Clear;
end;
//=============================================================================
// ウィンドウがクリップボードチェインから削除されようとしている時に,チェイン
// のウィンドウに送られてくるメッセージ
//
// Remove : チェインから削除されるウィンドウのハンドル
// Nesxt : チェインから削除される次のウィンドウのハンドル
//
// チェイン内の次のウィンドウが削除されようとしている場合は,チェイン内の次の
// ウィンドウとして,Nextの値を保存する必要がある
// チェイン内の次のウィンドウが削除されるウィンドウでない場合,チェインの次の
// ウィンドウにメッセージをSendMessageへ渡さなければならない
//=============================================================================
procedure TForm1.WMChangeCBChain(var Message: TWMChangeCBChain);
begin
if (Message.Remove = NextWindow) then begin
NextWindow := Message.Next;
Message.Result := 0;
end else begin
SendMessage(NextWindow, WM_CHANGECBCHAIN, Message.Remove, Message.Next);
end;
end;
//=============================================================================
// クリップボード更新フック
// クリップボードの内容に更新がある時に,クリップボードチェインのウィンドウに
// 送られてくるメッセージ
// SetClipboradViewer関数で,クリップボードチェインに登録したウィンドウだけが
// このメッセージを受信できる
// このメッセージを受信したウィンドウは,クリップボードチェインの次のウィンド
// ウにこのメッセージを送信し,全てのビューワウィンドウにメッセージが届くよう
// にしなければならない
//
// 本サンプルでは,アプリ起動時にもこのメッセージを受信
//=============================================================================
procedure TForm1.WMDrawClipboard(var Message: TWMDrawClipboard);
const
CF_DIBV5 = 17; //Delphi 7以降では不要
begin
if not OpenClipboard(Handle) then Exit;
try
ListBox1.Items.BeginUpdate;
ListBox1.Items.Clear;
//メッセージが発生した日時を表示
ListBox1.Items.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', Now));
ListBox1.Items.Add('');
//クリップボードが空になった
if Clipboard.FormatCount = 0 then begin
ListBox1.Items.Add('Data Empty');
end else begin
with ListBox1.Items do begin
if Clipboard.HasFormat(CF_BITMAP) then Add('CF_BITMAP');
if Clipboard.HasFormat(CF_DIB) then Add('CF_DIB');
if Clipboard.HasFormat(CF_DIBV5) then Add('CF_DIBV5');
if Clipboard.HasFormat(CF_DIF) then Add('CF_DIF');
if Clipboard.HasFormat(CF_DSPBITMAP) then Add('CF_DSPBITMAP');
if Clipboard.HasFormat(CF_DSPENHMETAFILE) then Add('CF_DSPENHMETAFILE');
if Clipboard.HasFormat(CF_DSPMETAFILEPICT) then Add('CF_DSPMETAFILEPICT');
if Clipboard.HasFormat(CF_DSPTEXT) then Add('CF_DSPTEXT');
if Clipboard.HasFormat(CF_ENHMETAFILE) then Add('CF_ENHMETAFILE');
if Clipboard.HasFormat(CF_GDIOBJFIRST) then Add('CF_GDIOBJFIRST');
if Clipboard.HasFormat(CF_GDIOBJLAST) then Add('CF_GDIOBJLAST');
if Clipboard.HasFormat(CF_HDROP) then Add('CF_HDROP');
if Clipboard.HasFormat(CF_LOCALE) then Add('CF_LOCALE');
if Clipboard.HasFormat(CF_METAFILEPICT) then Add('CF_METAFILEPICT');
if Clipboard.HasFormat(CF_OEMTEXT) then Add('CF_OEMTEXT');
if Clipboard.HasFormat(CF_OWNERDISPLAY) then Add('CF_OWNERDISPLAY');
if Clipboard.HasFormat(CF_PALETTE) then Add('CF_PALETTE');
if Clipboard.HasFormat(CF_PENDATA) then Add('CF_PENDATA');
if Clipboard.HasFormat(CF_PRIVATEFIRST) then Add('CF_PRIVATEFIRST');
if Clipboard.HasFormat(CF_PRIVATELAST) then Add('CF_PRIVATELAST');
if Clipboard.HasFormat(CF_RIFF) then Add('CF_RIFF');
if Clipboard.HasFormat(CF_SYLK) then Add('CF_SYLK');
if Clipboard.HasFormat(CF_TEXT) then Add('CF_TEXT');
if Clipboard.HasFormat(CF_TIFF) then Add('CF_TIFF');
if Clipboard.HasFormat(CF_UNICODETEXT) then Add('CF_UNICODETEXT');
if Clipboard.HasFormat(CF_WAVE) then Add('CF_WAVE');
end;
end;
ListBox1.Items.EndUpdate;
finally
CloseClipboard;
end;
//次のクリップボードビューワウィンドウにメッセージを送る
SendMessage(NextWindow, WM_DRAWCLIPBOARD, 0, 0);
end;
end.
上のリストの CF_TEXT に相当するテキストをクリップボードから取得するために,この部分を以下のようにすると,
「クリップボード アクセスが拒否されました。 を開けません.」
というエラーが発生して,取得できません. |
if Clipboard.HasFormat(CF_TEXT) then S := Clipboard.AsText;
|
図6
アクセス拒否 ? |
既に述べたように,実際にクリップボードからデータを取り出す手順,方法はデータの種類によって異なります.実際にクリップボードからデータを取得するコードは,アプリケーションの [貼り付け] ボタン等を使用することになるでしょう
Delphi の TClipboard を使用して WM_DRAWCLIPBOARD メッセージ内でクリップボードのテキストを取得するには,例えば次のようなコードで取得できます.データを取得するコードは,プログラムのどの位置で取得するかによっても異なることがあります. |
リスト2
クリップボードのテキストを,TClipboard を使用して取得する |
procedure TForm1.WMDrawClipboard(var Message: TWMDrawClipboard);
var
LhClipboard : THandle;
begin
ClipBoard.Open;
if Clipboard.HasFormat(CF_TEXT) then begin
LhClipboard := Clipboard.GetAsHandle(CF_TEXT);
try
Memo1.Text := Clipboard.AsText;
finally
GlobalUnlock(LhClipboard);
end;
end;
Clipboard.Close;
//次のクリップボードビューワウィンドウにメッセージを送る
SendMessage(NextWindow, WM_DRAWCLIPBOARD, 0, 0);
end;
クリップボードの監視は,上のようなコードをアプリケーションのコードに追加してもいいのですが,Delphi の利点であるコンポーネントを作成して利用すると使い勝手がよくなります.ネット上には既にいくつかコンポーネント提供されています.これを利用したテストです.
このテストでは,中村の里にある,中村拓男さん作の TNkClipDetector を使用してみました.このコンポーネントは最終更新が 1997年5月8日となっていますが,十分使用できます.これもまた Delphi の特徴の1つです. |
|
図7
設計時画面
- 以下を配置
TPanel (この上にボタン類を配置)
TPanel (この上に TImage を配置)
TSplitter
TRichEdit
TNkClipDetector
|
|
図8
テストプログラム起動
|
|
図9
実行例
- 当サイトのロゴ画像を,ブラウザ上で [画像のコピー] を実行し,[画像貼り付け] ボタンをクリックした結果
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, NkClipDetector;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Splitter1: TSplitter;
RichEdit1: TRichEdit;
Image1: TImage;
Button1: TButton;
Button2: TButton;
Button3: TButton;
NkClipDetector1: TNkClipDetector;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure NkClipDetector1ClipChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses Clipbrd;
{$R *.dfm}
//=============================================================================
// 画像は左上隅から表示するものとする
// TNkClipDetectorのIgnoreFirstをオブジェクト・インスペクタでFalseにしておく
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Image1.Left := 0;
Image1.Top := 0;
Image1.Transparent := True;
Image1.AutoSize := True;
RichEdit1.ScrollBars := ssBoth;
end;
//=============================================================================
// [画像の貼り付け]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.Assign(Clipboard);
end;
//=============================================================================
// [テキストの貼り付け]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
//現在のキャレット(カーソル)位置から文字列を挿入
RichEdit1.SelText := Clipboard.AsText;
end;
//=============================================================================
// [クリア]ボタン
// クリップボードのクリア.ついでに表示中の画像とテキストもクリア
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
Clipboard.Clear;
Image1.Picture.Assign(nil);
RichEdit1.Lines.Clear;
end;
//=============================================================================
// TNkClipDetectorのOnClipChangeイベント
// クリップボードの監視イベント
//
// Button1は[画像貼り付け]
// Button2は[テキスト貼り付け]
// Button3はクリップボードのクリア
//=============================================================================
procedure TForm1.NkClipDetector1ClipChange(Sender: TObject);
begin
Button1.Enabled := Clipboard.HasFormat(CF_BITMAP) or
Clipboard.HasFormat(CF_ENHMETAFILE);
Button2.Enabled := Clipboard.HasFormat(CF_TEXT);
Button3.Enabled := Clipboard.FormatCount > 0;
end;
end.
エクスプローラで選択したフォルダ,ファイルに対して [コピー] を実行すると,クリップボードのフォーマットは CF_HDROP となりますが,この時クリップボードにはコピーしたファイルのリストが格納されます.このファイルリストは,DragQueryFile 関数を使用して取得できます.
このサンプルは,エクスプローラで [コピー] を実行すると,直ちにファイルリストを表示します.
このサンプルコードは,自分のファイルリスト表示用プログラムに,エクスプローラで [コピー] したファイルを追加表示する用途に利用できます. |
|
図10
コピーしたファイルのリスト |
リスト4
クリップボードにコピーされたファイルリストを表示 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, NkClipDetector;
type
TForm1 = class(TForm)
Panel1: TPanel;
RichEdit1: TRichEdit;
Button1: TButton;
NkClipDetector1: TNkClipDetector;
procedure Button1Click(Sender: TObject);
procedure NkClipDetector1ClipChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Clipbrd,
ShellAPI,
ShlObj,
ActiveX;
{$R *.dfm}
//=============================================================================
// ファイルリストを表示するRichEditの設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
RichEdit1.ScrollBars := ssBoth;
end;
//=============================================================================
// [クリア]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Clipboard.Clear;
RichEdit1.Lines.Clear;
end;
//=============================================================================
// TNkClipDetectorのOnClipChangeイベント
// クリップボードの監視イベント
// エクスプローラでファイルを[コピー]したら直ちにそのリストを表示
//=============================================================================
procedure TForm1.NkClipDetector1ClipChange(Sender: TObject);
var
CF_DROPEFFECT : UINT;
hMem : Pointer;
LdwEffect : Word;
LhDrop : THandle;
LCount : Integer;
LBuff : array [0..MAX_PATH - 1] of Char;
i : Integer;
begin
RichEdit1.Lines.Clear;
RichEdit1.Lines.BeginUpdate;
//---------------------------------------------------------------------------
// エクスプローラでの操作種類を検出
// コピー,切り取り,移動等の検出が不要ならこの部分は不要
//---------------------------------------------------------------------------
LdwEffect := 0;
CF_DROPEFFECT := RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
if Clipboard.HasFormat(CF_DROPEFFECT) then begin
LhDrop := Clipboard.GetAsHandle(CF_DROPEFFECT);
if LhDrop <> 0 then begin
hMem := Windows.GlobalLock(LhDrop);
try
LdwEffect := PWord(hMem)^;
finally
GlobalUnlock(LhDrop);
end;
end;
if (LdwEffect and DROPEFFECT_NONE) > 0 then RichEdit1.Lines.Add('NONE');
if (LdwEffect and DROPEFFECT_COPY) > 0 then RichEdit1.Lines.Add('COPY');
if (LdwEffect and DROPEFFECT_MOVE) > 0 then RichEdit1.Lines.Add('MOVE');
if (LdwEffect and DROPEFFECT_LINK) > 0 then RichEdit1.Lines.Add('LINK');
if (LdwEffect and DROPEFFECT_SCROLL) > 0 then RichEdit1.Lines.Add('SCROLL');
end;
//---------------------------------------------------------------------------
if Clipboard.HasFormat(CF_HDROP) then begin
//CF_HDROP内部構造体のハンドルを取得
LhDrop := Clipboard.GetAsHandle(CF_HDROP);
LCount := DragQueryFile(LhDrop, UINT(-1), nil, 0);
RichEdit1.Lines.Add('コピーファイル数 : ' + IntToStr(LCount));
for i := 0 to LCount - 1 do begin
DragQueryFile(LhDrop, i, LBuff, Length(LBuff));
//ファイル名をリストに追加(フォルダも含まれる)
RichEdit1.Lines.Add(LBuff);
end;
end;
RichEdit1.Lines.EndUpdate;
end;
end.
[01_各種コピー操作によるクリップボードのフォーマット] では標準フォーマットだけ取得して列挙しましたが,クリップボードのフォーマットには,アプリケーション独自のもの,システムが使用するフォーマット形式もあります.これらも列挙して表示するサンプルです.
このサンプルは,クリップボードの監視は行いません.アプリでコピーしたら,[Button1] をクリックして確認するようになっています.
いくつかのアプリケーションで,実際にどのようなフォーマット形式があるを見てみました.
これらのフォーマット形式のデータは,その形式のデータの構造が分からなければ,その内容を取得することはできません.自アプリに,独自のフォーマット形式を実装することもできます.
下のコードで使用しているフォーマット形式は,Delphi XE の Windows.pas で定義されている定数の全てです. |
|
図11
設計時画面 |
|
図12
ビットマップ画像のコピー
|
|
図13
文字列 (テキスト) のコピー
|
|
図14
ブラウザ Firefox でテキストをコピー
- 上と同じテキストであるが,ブラウザの HTML 関係の情報もクリップボードに格納される
|
|
図15
Delphi のフォームのコピー
- 開発環境の状態 (IDE上) でフォームをコピー
- Delphi Components (コンポーネント関係の情報) もクリップボードに格納される
|
|
図16
ワード 2010 の図形をコピー
- Art::GVML Clipformat がワードの画像や図形をクリップボードに格納するフォーマット形式
|
|
図17
エクセル 2010 のシートをコピー
- エクセルのシートにはいろいろな情報があるので,フォーマットの形式も多い
- Biff12 は Excel 2007 以降のファイル形式
- Biff8 は Excel97 から Excel 2003 のファイル形式
- Biff5 は Excel95 のファイル形式
- つまり Excel 2010 はどのファイル形式も扱える
|
|
図18
エクセル 2010 の画像をコピー
- ワードと同じ Art::GVML Clipformat がある
|
次の図は,Halbow 資料館の [クリップボードで任意のデータを受け渡す] のコンポーネント化したサンプル,List 4 を実行した結果です.
クリップボードのフォーマット形式の名前が HogeHogeRecord となっています.
このテストは,レコード型に格納した複数の可変長のテキストをコピーしていますが,CF_TEXT がありません.これは,全て HogeHogeRecord というフォーマットに格納しているためです. |
|
図19
Halbow 資料館のサンプルを実行 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses Clipbrd;
{$R *.dfm}
{ TForm1 }
//=============================================================================
// クリップボードのフォーマットの形式名を取得して表示
// GetClipBoardFormatName関数を使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
FmtID : Integer;
FmtName : array [0..MAX_PATH-1] of Char;
begin
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
for i := 0 to ClipBoard.FormatCount - 1 do begin
FmtID:= ClipBoard.Formats[i];
FillChar(FmtName, SizeOf(FmtName), #0);
if GetClipBoardFormatName(FmtID, FmtName,SizeOf(FmtName)) > 0 then begin
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' ' + FmtName);
end else begin
case ClipBoard.Formats[i] of
CF_TEXT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_TEXT');
CF_BITMAP:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_BITMAP');
CF_METAFILEPICT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_METAFILEPICT');
CF_SYLK:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_SYLK');
CF_DIF:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DIF');
CF_TIFF:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_TIFF');
CF_OEMTEXT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_OEMTEXT');
CF_DIB:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DIB');
CF_PALETTE:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_PALETTE');
CF_PENDATA:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_PENDATA');
CF_RIFF:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_RIFF');
CF_WAVE:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_WAVE');
CF_UNICODETEXT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_UNICODETEXT');
CF_ENHMETAFILE:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_ENHMETAFILE');
CF_HDROP:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_HDROP');
CF_LOCALE:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_LOCALE');
CF_DIBV5:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DIBV5');
CF_MAX_XP:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_MAX_XP');
CF_OWNERDISPLAY:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_OWNERDISPLAY');
CF_DSPTEXT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DSPTEXT');
CF_DSPBITMAP:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DSPBITMAP');
CF_DSPMETAFILEPICT:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DSPMETAFILEPIC');
CF_DSPENHMETAFILE:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_DSPENHMETAFILE');
CF_GDIOBJFIRST:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_GDIOBJFIRST');
CF_GDIOBJLAST:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_GDIOBJLAST');
CF_PRIVATEFIRST:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_PRIVATEFIRST');
CF_PRIVATELAST:
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' CF_PRIVATELAST');
else
Memo1.Lines.Add(IntToHex(ClipBoard.Formats[i], 4) + ' UnKown');
end;
end;
end;
Memo1.Lines.EndUpdate;
end;
end.
05_コピーとクリップボードからのデータ取得の参考コード |
クリップボードの画像またはテキストデータは,次のように,クリップボードのハンドルを取得してメモリストリームに格納することができます. |
リスト6
クリップボードのデータをメモリストリームに格納 |
procedure TForm1.Button1Click(Sender: TObject);
var
DataFormat : UINT;
dataStream : TMemoryStream;
hBuffer : THandle;
pBuffer : Pointer;
dwSize : Cardinal;
begin
//検出するクリップボードのデータ形式
DataFormat := CF_BITMAP;
//その形式だったらクリップボードからdataStreamにデータを格納
if Clipboard.HasFormat(DataFormat) then begin
dataStream := TMemoryStream.Create;
try
ClipBoard.Open;
hBuffer := Clipboard.GetAsHandle(DataFormat);
pBuffer := GlobalLock(hBuffer);
try
dwSize := GlobalSize(hBuffer);
if dwSize > 0 then begin
dataStream.WriteBuffer(pBuffer^, dwSize);
end;
finally
GlobalUnlock(hBuffer);
Clipboard.Close;
end;
finally
FreeAndNil(dataStream);
end;
end;
end;
このメモリストリームに格納されたデータは,指定フォーマット形式の情報です.ビットマップ画像の場合,上のコードで取得したデータだけではビットマップ画像としては表示できません.ビットマップヘッダ部の処理が必要です.
Delphi には,クリップボードから画像,テキストを取得する便利な関数やプロパティが実装されています.上の方のサンプルにも使用例がありますが,再掲しておきます. |
|
図20
設計時画面
|
|
図21
ワード 2010 の図形のコピーを取得
- サンプルコードでは,これをビットマップ画像として保存しているが,画像の形式を判定すれば,元の画像形式での保存も可能
- ワード 2010 の図形はメタファイルとして保存できる
|
|
図22
ビットマップ画像のコピー
- [Button2] をクリックして,ディスク上のビットマップ画像をコピーして,[Button1] クリックで取得して表示
|
|
図23
テキストのコピーと取得
- テキストをコピーして,それをクリップボードから取得して表示した結果
|
|
図24
エクセル 2010 のシートのコピーを取得
- 上のサンプルで確認できるように,シートの画像もクリップボードに格納されている
- テキストを取得すれば,セルの文字列のみの取得となる
|
リスト7
コピーと貼り付けの参考コード.クリップボードの監視機能は省略
扱う画像の種類によっては,uses に Jpeg, Gifimg, PngImage 等が必要 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses Clipbrd, Jpeg, GifImg, PngImage;
{$R *.dfm}
{ TForm1 }
//=============================================================================
// 貼り付け
// クリップボードから画像を取得
// このコードは画像の種類を区別しない場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
Pict : TPicture;
DestFilePath : String;
begin
if (Clipboard.HasFormat(CF_METAFILEPICT)) or
(Clipboard.HasFormat(CF_ENHMETAFILE)) or
(Clipboard.HasFormat(CF_METAFILEPICT)) or
(Clipboard.HasFormat(CF_BITMAP)) or
(Clipboard.HasFormat(CF_DIB)) then begin
Pict := TPicture.Create;
try
//クリップボードから画像データのみ取得
Pict.Assign(Clipboard);
//TImageに表示してビットマップ画像として保存
Image1.Picture.Assign(Pict);
DestFilePath := ExtractFilePath(Application.ExeName) + 'image1.bmp';
Pict.SaveToFile(DestFilePath);
finally
FreeAndNil(Pict);
end;
end;
end;
//=============================================================================
// コピー
// ディスク上の画像ファイルをクリップボードに格納
// 画像のオブジェクトのコピーは,TPictureの直接操作となる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
Pict : TPicture;
SrcFilePath : String;
begin
SrcFilePath := ExtractFilePath(Application.ExeName) + '501.bmp';
Pict := TPicture.Create;
try
Pict.LoadFromFile(SrcFilePath);
Clipboard.Assign(Pict);
finally
FreeAndNil(Pict);
end;
end;
//=============================================================================
// 文字列(テキスト)のコピーと貼り付け
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
strText : String;
begin
strText := 'クリップボード';
//コピー
Clipboard.AsText := strText + sLineBreak + 'テスト';
strText := '';
Sleep(300);
//貼り付け
if (Clipboard.HasFormat(CF_TEXT)) then begin
strText := Clipboard.AsText;
MessageBox(Handle, PChar(strText), '情報', MB_ICONINFORMATION);
end;
end;
end.
画像等のデータは,クリップボードのハンドルから次のようにして取得することもできます.このコードはビットマップ画像の場合です. |
[備考]
このコードでフォーマットに CF_DIB を指定しても,DIB のデータは取得できません.
Delphi の GetAsHandle は単に Windows API の GetClipboardData 関数を実行しているだけです.したがつて,この現象は GetClipboardData 関数の仕様と思われます. |
リスト8
GetAsHandle でデータのハンドルからデータを取得する例.ビットマップ画像の場合 |
procedure TForm1.Button4Click(Sender: TObject);
var
Bmp : TBitmap;
begin
if (Clipboard.HasFormat(CF_BITMAP)) then begin
Bmp := TBitmap.Create;
try
Bmp.Handle := Clipboard.GetAsHandle(CF_BITMAP);
Image1.Picture.Assign(Bmp);
finally
FreeAndNil(Bmp);
end;
end;
end;
06_TRichEdit のテキストの書式付きコピーと貼り付け |
TRichEdit には専用のコピー貼り付けのメソッドがあります.その確認です.
CopyToClipboard, PasteFromClipboard メソッドは,TDBImage,TCustomEdit にもあります.
TRichEdit の書式付のテキストは,TMemo にも貼り付けできますが,書式は失われます. |
|
図25
テキストを選択してコピー |
|
図26
貼り付け |
リスト9
TRichEdit のテキストの書式付きコピーと貼り付け
CF_OEMTEXT を検出する.クリップボードの監視機能は省略 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, Buttons;
type
TForm1 = class(TForm)
Panel1: TPanel;
RichEdit1: TRichEdit;
Splitter1: TSplitter;
RichEdit2: TRichEdit;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure SpeedButton2Click(Sender: TObject);
procedure SpeedButton3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses RichEdit, Clipbrd;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// リッチテキストファイルの読み込み
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
RichEdit1.Lines.LoadFromFile(ExtractFilePath(Application.ExeName) + 'RichText.RTF');
end;
//=============================================================================
// コピー
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
RichEdit1.CopyToClipboard;
end;
//=============================================================================
// 貼り付け
//=============================================================================
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
if Clipboard.HasFormat(CF_OEMTEXT) then begin
RichEdit2.PasteFromClipboard;
end;
end;
//=============================================================================
// 切り取り
//=============================================================================
procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
RichEdit1.CutToClipboard;
end;
end.
07_TRichEdit のテキストの書式付きコピーと貼り付け - TActionList |
上のサンプルでは,クリップボードの監視機能を省略しています.実際のアプリでは,本ページでテストした監視機能を追加することになります.
テキスト (プレインテキストまたは書式付テキスト) だけであれば,TActionList 等を使用することにより,クリップボードの監視を自動で行うことができます.その例です.
設計時にフォームに配置した TActionList で下図のような設定をします. |
|
図27
アクションの追加
- TActionList コンポをダブルクリック
- [編集] ダイアログでポップアップメニューを表示
- [標準アクションの新規作成] を選択
|
|
図28
アクションクラスの選択
- [編集] から以下の 3 つを選択
TEditCut
TEditCopy
TEditPaste
- [指定可能なアクションクラス] はインストールしているコンポーネントによって異なる
|
|
図29
カテゴリ [標準] が追加される |
上記の設定をしたら,各々のボタンの Action プロパティの値を,オブジェクトインスペクタで以下のように設定します.
- SpeedButton1.Action を EditCopy1 に
- SpeedButton2.Action を EditPaste1 に
- SpeedButton3.Action を EditCut1 に
すると,ボタンの Caption が下図のように,自動で設定されます.必要に応じて変更します. |
[備考]
ボタンのアクセラレーターキーを無効にするには,例えば,Caption の値を [コピー(&C)] から [コピー] に変更します.該当アクションクラス全体で無効にするには,該当クラスの Caption プロパティの値を同じように変更します. |
|
図30
Action プロパティを設定した結果 |
下図は,作成した EXE の実行例です.
TActionList の各クラスのオブジェクトには,メソッドやイベントも実装されています.各アクションが実行された時の処理を実装することができます.
TActionList の各アクションは,TPopupMenu の Action プロパティに指定できます.アプリケーションの開発では,TActionList を実装して利用すると便利です.
|
|
図31
EXE 起動時の画面 |
|
図32
テキストを選択
- [コピー] ボタンが使用可能になる
- [コピー] ボタンをクリックすると [貼り付け] ボタンが使用可能になる
|
|
図33
[貼り付け] の操作結果 |
リスト10
TRichEdit のテキストの書式付きコピーと貼り付け
TActionList を使用したクリップボードの監視機能付き |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, Buttons, StdActns, ActnList;
type
TForm1 = class(TForm)
Panel1: TPanel;
RichEdit1: TRichEdit;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
ActionList1: TActionList;
EditCut1: TEditCut;
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Clipbrd;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// リッチテキストファイルの読み込み
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//リッチテキストを読み込んで表示
RichEdit1.Lines.LoadFromFile(ExtractFilePath(Application.ExeName) + 'RichText.RTF');
//クリップボードをクリア
Clipboard.Clear;
end;
end.
08_クリップボードにコピーしたテキストを自動取得 |
本ページの最初のサンプルコードの下に,テキストをコピーすると自動的にクリップボードからテキストを取得して TMemo に表示するサンプルがあります.
そのサンプルで,Microsoft Edge ブラウザと Google Chrome ブラウザのアドレスバーの URL をコピーすると
「クリップボード アクセスが拒否されました。 を開けません.」
というエラーが発生します. |
|
図34
クリップボードのアクセス拒否
- Microsoft Edge または Google Chrome で URL をコピーした場合
- Firefox では発生しない
|
そこで,本サンプルでは,ClipBoard.Open の処理を次のようにしています.また,クリップボードからデータを取得したら,本サンプルのフォームを最前面に表示するようにしています.
ClipBoard.Open の処理がある WMDrawClipboard ( WM_DRAWCLIPBOARD メッセージ ) と,最前面の処理以外は最初のサンプルのコードと同じです. |
try
ClipBoard.Open;
except
Exit;
end;
下図は,Firefox と Google Chrome における実行結果です. |
|
図35
ブラウザの URL を自動取得
- ウェブブラウザのアドレスパーのテキストをコピーした結果
- Firefox と Google Chrome の場合
- 本サンプルの EXE を予め起動しておく
- テキストをコピーする前にリストをクリア
|
|
図36
一本的なテキストの場合 |
リスト11
クリップボードにコピーしたテキストを自動的に取得して TMemo に表示 |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
FNextWindow : HWND;
procedure WMChangeCBChain(var Message: TWMChangeCBChain); message
WM_CHANGECBCHAIN;
procedure WMDrawClipboard(var Message: TWMDrawClipboard); message
WM_DRAWCLIPBOARD;
public
{ Public 宣言 }
procedure BringToFrontForm(AHandle: HWND);
end;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd;
{$R *.dfm}
//=============================================================================
// このフォームをクリップボードビューワとして登録
// FNextWindow は次のクリップボードビューワウィンドウのハンドル
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FNextWindow := SetClipboardViewer(Handle);
end;
//=============================================================================
// クリップボードビューワとしての登録を解除
// クリップボード監視のチェインから外す
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
ChangeClipboardChain(Handle, FNextWindow);
end;
//=============================================================================
// クリップボードを空にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Clipboard.Clear;
end;
//=============================================================================
// 取得した内容をクリアする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Lines.Clear;
end;
//=============================================================================
// ウィンドウがクリップボードチェインから削除される時に送られてくるメッセージ
//=============================================================================
procedure TForm1.WMChangeCBChain(var Message: TWMChangeCBChain);
begin
if (Message.Remove = FNextWindow) then begin
FNextWindow := Message.Next;
Message.Result := 0;
end else begin
SendMessage(FNextWindow, WM_CHANGECBCHAIN, Message.Remove, Message.Next);
end;
end;
//=============================================================================
// クリップボード更新フック
// クリップボードの内容に更新がある時に送られてくるメッセージ
// CF_TEXT の時にテキストをクリップボードから自動的に取得
//
// 本サンプルでは,アプリ起動時にもこのメッセージを受信
//=============================================================================
procedure TForm1.WMDrawClipboard(var Message: TWMDrawClipboard);
var
LhClipbrd : THandle;
begin
// Microsoft Edge, Google Chrome の URL をコピーした時の対応策
// クリップボードが開けないエラーが発生する
try
ClipBoard.Open;
except
Exit;
end;
if Clipboard.HasFormat(CF_TEXT) then begin
LhClipbrd := Clipboard.GetAsHandle(CF_TEXT);
try
Memo1.Lines.Add(Clipboard.AsText);
finally
GlobalUnlock(LhClipbrd);
end;
end;
Clipboard.Close;
//次のクリップボードビューワウィンドウにメッセージを送る
SendMessage(FNextWindow, WM_DRAWCLIPBOARD, 0, 0);
BringToFrontForm(Handle);
end;
//-----------------------------------------------------------------------------
// 指定フォームを強制的に最前面に表示する関数
// この手続きを実行するウィンドウがアクティブでない場合用
//
// AHandle : 前面に表示するフォームのハンドル (メインフォーム以外も可)
// SPI_GETFOREGROUNDLOCKTIMEOUT, SPI_SETFOREGROUNDLOCKTIMEOUT はデバック時無効
//-----------------------------------------------------------------------------
procedure TForm1.BringToFrontForm(AHandle: HWND);
var
LhForeWnd : HWND;
LTimeOut : DWORD;
LThreadForeID : Cardinal;
LThreadSelfID : Cardinal;
begin
// 現在の最前面のウィンドウハンドルを取得
// 少しタイムラグがないとウィンドウハンドルが取得できないことがある
LhForeWnd := 0;
while True do begin
LhForeWnd := GetForegroundWindow;
if LhForeWnd <> 0 then Break;
Application.ProcessMessages;
Sleep(10);
end;
// 現在の最前面ウィンドウのプロセスIDの値を取得
LThreadForeID := GetWindowThreadProcessId(LhForeWnd);
// 前面表示するウィンドウのプロセスのスレッドIDの値を取得
// LThreadSelfID := GetWindowThreadProcessId(AHandle); でもOK (値は違う)
LThreadSelfID := GetCurrentThreadId;
try
// スレッドのアタッチ
AttachThreadInput(LThreadSelfID, LThreadForeID, True);
// タスクバーの点滅時間の設定値を取得 (フォアグランドにするまでの時間)
// デフォルト値は非常に大きな値.通常は前面に表示しないで点滅するだけ
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @LTimeOut, 0);
// タスクバーの点滅時間の設定
// 点滅時間を 0 にして直ちに最前面に表示するようにする
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, 0);
// 最小化されているときは元に戻す
if IsIconic(AHandle) then begin
SendMessage(AHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
end;
// 最前面に表示する
SetForegroundWindow(AHandle);
finally
// タスクバーの点滅時間の設定を元に戻す
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Pointer(LTimeOut), 0);
// 終了したらアタッチを切り離す
AttachThreadInput(LThreadSelfID, LThreadForeID, False);
end;
end;
end.
下図は,Microsof Edge における URL のコピーの結果です.URL をコピーする前に,リストをクリアしてテストしています.コピーとしての操作は 1 回だけですが,ほとんど場合,複数回の受信となっています.これにはいくつものパターンがあります.まれにですが,1 回だけの時もあります.
この現象は,Firefox, Google Chrome では発生しません.
本ページの他のサンプルのように,ボタンクリックでクリップボードのデータを取得するのであれば,最後のテキストだけを URL として取得することになります. |
|
図37
Microsoft Edge で URL をコピーした結果
- プロトコルなしの後に
プロトコル付き 2 回
- プロトコル付きが 3 回の時もある
|
|
図38
Microsoft Edge で URL をコピーした結果
|
|
図39
Microsoft Edge で URL をコピーした結果
|
Microsoft Edge の URL のコピーが,何故,上記のような仕様になっているのか,筆者は知りません.この場合,次のように,一定時間内に受信したメッセージを処理しないようにすれば,最初のメッセージだけを処理するようにできます. |
//=============================================================================
// クリップボード更新フック
// クリップボードの内容に更新がある時に送られてくるメッセージ
// CF_TEXT の時にテキストをクリップボードから自動的に取得
//
// 一定時間内に受信したメッセージを無視する例
//=============================================================================
procedure TForm1.WMDrawClipboard(var Message: TWMDrawClipboard);
{$WRITEABLECONST ON}
const
LGetTime : Cardinal = 0;
{$WRITEABLECONST OFF}
var
LhClipbrd : THandle;
LTimeSpane : Cardinal;
begin
LTimeSpane := timeGetTime - LGetTime;
if LTimeSpane < 100 then Exit;
// Microsoft Edge, Google Chrome の URL をコピーした時の対応策
// クリップボードが開けないエラーが発生する
try
ClipBoard.Open;
except
Exit;
end;
LGetTime := timeGetTime;
if Clipboard.HasFormat(CF_TEXT) then begin
LhClipbrd := Clipboard.GetAsHandle(CF_TEXT);
try
Memo1.Lines.Add(Clipboard.AsText);
finally
GlobalUnlock(LhClipbrd);
end;
end;
Clipboard.Close;
//次のクリップボードビューワウィンドウにメッセージを送る
SendMessage(FNextWindow, WM_DRAWCLIPBOARD, 0, 0);
BringToFrontForm(Handle);
end;
下図は,今回動作確認したウェブブラウザとそのバージョンです. |
|
図40
動作確認したブラウザとそのバージョン |
|