一度待避したクリップボードの内容を復元するには?

解決


goro  2005-10-29 06:58:58  No: 18334

お世話になります。

クリップボードの内容(※)を待避し、一旦、クリップボードの内容を変更した後、
再度、待避した内容をクリップボードに戻したいです。
対象がすべてテキストであったら、現在の私の知識で可能ですが、
それ以外はどうしたらいいのか検討がつきません。
これを実現するにはどうしたらいいのでしょうか?
※…テキスト、画像、他


えーと  2005-10-29 08:02:41  No: 18335

画像でビットマップに待避したなら

Clipboard.Assign(bmp);

という感じです。


通りすがり  2005-10-29 09:36:04  No: 18336

ん〜、待避するのは、自分のアプリで作ったデータではなく、全てに共通で使用されているデータってことかね?

例えば、Excelでコピーしたデータを、自分のアプリで一端待避し、クリップボードを使用。
その後、Excelのデータを戻すようなイメージかな?

だとしたら、クリップボードに保存されている全てのフォーマット形式を取得し、
(たぶんできたと思うけど、やり方は他の人のレスを待とう(^^ゞ)
FileStreamもMemoryStreamでも全部保存して(クリップボードからStreamへの
読み書きはあったと思うけど)、クリア。
あとは、自アプリで好きなように使って、復元すればいいと思われる。

具体的なコードは・・・出せませんm(v_v)m


goro  2005-10-29 10:18:40  No: 18337

みなさん、ありがとう。

#通りすがり さん
>例えば、Excelでコピーしたデータを、自分のアプリで一端待避し、クリップボードを使用。
>その後、Excelのデータを戻すようなイメージかな?
その通りです。

テキスト、画像あたりならなんとかなりそうですが、
他の形式の場合はどうするのかが分からなかったのです。

具体的な目的は、
(あらゆるアプリで)テキストの範囲選択しているところをクリップボードに取り込みたいのです。
しかし、APIによるSendMessageでは「Edit」コンポーネントしか取れないと、
ネットで見ました。
ですので、キー押下メッセージ(CTRL+C)を送ることにより範囲選択しているところをクリップボードに取り込もうとしています。


goro  2005-10-29 10:21:28  No: 18338

記述漏れましました。追記です。

主目的がこれですので、
自アプリケーションでクリップボードの内容を使用した後、元に戻したいという訳です。


ママん  2005-10-29 13:57:22  No: 18339

以前、全く同じことしました。
そのときのノウハウですが、エクセル等の一部のアプリはこの仕様では無理です。
クリップボードにポインタみたいなものを置いてたり、アプリ自体がクリップボードの変更を監視しているのは以外にありますからね。
完全対応はあきらめるが吉だとおもいます。


にしの  2005-11-01 01:54:21  No: 18340

作ってみました。
細かいエラー対策はしていません。

procedure ClipBoardSaveToStream(Stream: TStream);
procedure ClipBoardLoadFromStream(Stream: TStream);

implementation

procedure ClipBoardSaveToStream(Stream: TStream);
var
  hMem: THandle;//LongWord
  uiFormatId: UINT;
  dwSize: DWORD;
  p: PCHAR;
begin
  if OpenClipboard(0) then
  begin
    try
      uiFormatId := EnumClipboardFormats(0);
      while uiFormatId <> 0 do
      begin
        Stream.Write(uiFormatId, SizeOf(uiFormatId));
        hMem := GetClipboardData(uiFormatId);

        if hMem <> 0 then
        begin
          dwSize := GlobalSize(hMem);
          Stream.Write(dwSize, SizeOf(dwSize));

          p := GlobalLock(hMem);
          Stream.Write(p^, dwSize);
          GlobalUnlock(hMem);
        end
        else
        begin
          dwSize := 0;
          Stream.Write(dwSize, SizeOf(dwSize));
        end;
        uiFormatId := EnumClipboardFormats(uiFormatId);
      end;
    finally
      CloseClipboard;
    end;
  end;
end;

procedure ClipBoardLoadFromStream(Stream: TStream);
var
  hMem: THandle;
  uiFormatId: UINT;
  dwSize: DWORD;
  p: PCHAR;
begin
  if OpenClipboard(0) then
  begin
    EmptyClipboard;
    try
      while Stream.Read(uiFormatId, SizeOf(uiFormatId)) = SizeOf(uiFormatId) do
      begin
        Stream.Read(dwSize, SizeOf(dwSize));
        hMem := GlobalAlloc(GMEM_MOVEABLE, dwSize);
        p := GlobalLock(hMem);
        Stream.Read(p^, dwSize);
        GlobalUnlock(hMem);
        SetClipboardData(uiFormatId, hMem);
        GlobalFree(hMem);
      end;      
    finally
      CloseClipboard;
    end;
  end;
end;


りおりお  2005-11-01 02:20:28  No: 18341

>にしのさん

遅延レンダリングの場合、このアプリにメッセージが届くことになりますね?


にしの  2005-11-01 03:40:03  No: 18342

遅延レンダリングの場合、GetClipboardDataを呼び出した時点で、WM_RENDERFORMATが飛びませんか?
# 私の勘違いかも。

上のプロシージャでは、必ずメモリを確保してからSetClipboardDataを呼ぶので、このアプリがWM_RENDERFORMATを受け取ることはないと思います。


りおりお  2005-11-01 05:14:21  No: 18343

> GetClipboardDataを呼び出した時点で、WM_RENDERFORMATが飛びませんか?

おお、そうでした。すみません。


goro  2005-11-03 08:00:56  No: 18344

ママん さん、にしの さん、りおりお さん、ご親切にありがとうございます。

私のレベルではまだ理解不能な回答をいただいて、混乱気味で、正直断念しそうです。
しかし、せっかく教えていただいていますので、頑張って理解しようと思っています。
基本的なことをお伺いするようで、大変恐縮ですが、よろしくお願いいたします。

まず、にしの さんより提示していただいたソースを順次見ていきまして、
私が理解できなかったことです(というより、アホな私はそこから学ぶ必要があると・・・)

>procedure ClipBoardSaveToStream(Stream: TStream);
>(略)
>      uiFormatId := EnumClipboardFormats(0);
ここでクリップボード内に存在するデータが持つデータ形式を列挙することは、
なんとなく分かったのですが、
>        Stream.Write(uiFormatId, SizeOf(uiFormatId));
ここは何をしているのでしょうか?
(ヘルプを見ると、「Write メソッドは,ストリームにデータを書き込むための抽象メソッドを
導入します。」とありますが、なんとも理解不能でありまして)

また、とりあえず動かしながら理解を深めようと、
単純に、クリップボードの内容を待避してすぐ戻してみようと以下の処理を行ってみました、
うまく動きません。どのようのすればよろしいのでしょうか?
>procedure TForm1.Button1Click(Sender: TObject);
>var
>  Stream1 : TStream;
>begin
>  ClipBoardSaveToStream(Stream1);
>  ClipBoardLoadFromStream(Stream1);
>
>end;

本当に低レベルな私ですが、どうかよろしくお願いいたしますm(_ _)m


にしの  2005-11-03 21:15:56  No: 18345

> >        Stream.Write(uiFormatId, SizeOf(uiFormatId));
> ここは何をしているのでしょうか?
> (ヘルプを見ると、「Write メソッドは,ストリームにデータを書き込むための抽象メソッドを
> 導入します。」とありますが、なんとも理解不能でありまして)
読んだそのままです。
uiFormatIdの内容をStreamに書き込みます。

TStreamは基底クラスなので、実装はその継承クラスがおこなっています。
Writeメソッドは、指定されたデータを「書き込む」というメソッドです。
この「書き込む」が抽象化されていて、たとえば
TMemoryStream・・・メモリに書き込む
TFileStream・・・ファイルに書き込む
というように、継承クラスの実装によって振る舞いが変わってきます。

> 単純に、クリップボードの内容を待避してすぐ戻してみようと以下の処理を行ってみました、
> うまく動きません。どのようのすればよろしいのでしょうか?

では、簡単な例を。
# ここでは、メモリに確保し、復元する方法。

1.TForm1の、Private宣言に、ms: TMemoryStream;を用意する。
    { Private宣言 }
    ms: TMemoryStream;

2.TForm1のOnCreateイベントで、msのインスタンスを生成する。
    ms := TMemoryStream.Create;

3.TForm1.のOnDestroyイベントで、msのインスタンスを破棄する。
    ms.Free;

4.Button1のOnClickイベントで、クリップボードを待避する。
procedure TForm1.Button1Click(Sender: TObject);
begin
  ms.Clear; //確保したMemoryStreamのクリア
  ClipboardSaveToStream(ms); //待避
end;

5.Button2のOnClickイベントで、クリップボードを復元する。
procedure TForm1.Button2Click(Sender: TObject);
begin
  ms.Seek(0, soFromBeginning); //MemoryStreamの巻き戻し
  ClipboardLoadFromStream(ms); //復元
end;


goro  2005-11-04 22:26:38  No: 18346

にしの さん、ご丁寧な説明をありがとうございます。

テキスト文字、EXCELのセル、画像(ビットマップ)などで試したところ、
うまくいっています。
これから、自作のアプリに取り込んでみようと思います。

大変勉強になり、また、非常に助かりました。ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加