クリップボードの内容をストリームに書き出すには?

解決


pken  2006-07-06 02:29:37  No: 22388

こんにちは。
現在、クリップボードのデータを扱うソフトを作っているのですが、クリップボード内のデータをストリームに書き出すことが上手くできません。
テキストデータ(CF_TEXT)ではいろいろなサンプルを参考にしつつできたのですが、必要なのはデータフォーマットに関係なくクリップボードの内容をストリームに書き出すことなのです。

今まで試したのは、QClipbrdにあるTClipboard.GetFormatや、WindowsAPIであるCopyMemoryを活用する方法です。

どうぞ宜しくお願いします。


ママん  2006-07-06 05:49:20  No: 22389

あいょ〜。

uses
  Clipbrd;

procedure GetClipbordDataToStream(var stream:TStream; CF:Integer);
var
  Data: THandle;
  PDevMode : ^TDevMode;
  Size:Int64;
begin
  Clipboard.Open;
  try
    Data := GetClipboardData(CF);
    if Data = 0 then Exit;
    PDevMode := GlobalLock(Data);
    Size := GlobalSize(Data);
    try
      stream.WriteBuffer(PDevMode^, Size);
    finally
      GlobalUnlock(Data);
    end;
  finally
    Clipboard.Close;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  mStream:TMemoryStream;
begin
  mStream:=TMemoryStream.Create;
  GetClipbordDataToStream(TStream(mStream),CF_TEXT);
  mStream.SaveToFile('test.txt');
  mStream.Free;
end;


pken  2006-07-06 08:25:44  No: 22390

ママんさん
早速のお返事ありがとうございます。
この方法は見たことがあるのですが、TDevModeが初見でした。ヘルプ等でプリンタ関連であることはわかったのですが、ここではどのような意図で使われているのでしょうか?

また、たとえば
  GetClipbordDataToStream(TStream(mStream),CF_BITMAP);
というふうに別のフォーマットにすると、メモリストリームに何も書き込まれなくなってしまうのですが、やはりフォーマット別に動作を分けるしかないのでしょうか。

一度に申し訳ありませんが、どうかお力添えをお願いします。


ママん  2006-07-06 23:46:47  No: 22391

うーむ。
GlobalLockがnilを返すタイプ(CF_BITMAP等)はオブジェクト参照しているのでGetObjectで取得する必要がありますが、
GetObjectするにも対象Objectのサイズが分からないと取得しようがありません。
すみませんが私には分からないです。
今晩でも調べてみますが、多分ムリポ。

PDevMode : ^TDevMode;
うはっこれはコピペ元がそうであっただけで、ポインタなら何でもOKです。普通なら
mem : Pointer;
とかするのが正解ですね。スミマセン。


ん?  2006-07-07 01:59:11  No: 22392

これみたいなこと?
https://www.petitmonte.com/bbs/answers?question_id=3354


ママん  2006-07-07 03:03:19  No: 22393

一応保身の為に補足ですが、にしのさんのサンプルでも
オブジェクト参照しているデータは取れていません。
//...
//        if hMem <> 0 then
//        begin
//          dwSize := GlobalSize(hMem);
//          Stream.Write(dwSize, SizeOf(dwSize));
//
          if uiFormatId=CF_BITMAP then
            showmessage(inttostr(dwSize));

//          p := GlobalLock(hMem);
//          Stream.Write(p^, dwSize);
//          GlobalUnlock(hMem);
//       end
//...
と入れてみるとdwSizeが0であることが分かります。


pken  2006-07-07 08:48:57  No: 22394

ん? さん
はい、このような感じでメモリストリームに書き込みができれば理想です。
過去ログ中でもこれはチェックし損ねていたようです。ありがとうございます。

ママんさん
回答ありがとうございます。
TDevModeに関してはこちらの早とちりで失礼しました。自分の見ていたところでもちゃんとポインタ型と書いてあったのに……。

画像などのデータはそもそも取得すら難しいということでしょうか。
ん? さんに教えていただいたところの最後では、テキストや画像で上手くいっているとのことだったのでいくらか試してみたのですが、画像はもとよりテキストですら上手くいかない状況です。
この関数の機能は自分の求めるものと同一であるので、どうにか動かせないかチャレンジしてみたいと思います。


ん?  2006-07-07 18:21:25  No: 22395

環境:Windows XP SP1 + Delphi5

提示したリンク先のにしのさんのコードを改造し、各フォーマットごとに
ストリームにファイルに書き出すようにしてみました。

(一部抜粋)

var
  fName:array[0..127] of Char;
  //p:PCHAR;
  ptr:PCHAR;

  hMem := GetClipboardData(uiFormatId);
  if hMem <> 0 then begin
    FillChar(fName, 128, 0);
    if GetClipboardFormatName(uiFormatId, @fName, 128) > 0 then begin
      Memo1.Lines.Add(IntToStr(uiFormatId) + ':' + PChar(@fName));
    end
    else begin
      Memo1.Lines.Add(IntToStr(uiFormatId) + ':てきとうななにか');
    end;

    dwSize := GlobalSize(hMem);
    if dwSize > 0 then begin
      fStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) +
                                    IntToStr(uiFormatId) + '.dat',
                                    fmCreate);

      ptr := GlobalLock(hMem);
      fStream.Write(ptr^, dwSize);
      fStream.Free;
      GlobalUnlock(hMem);
    end;
  end;

ここで、PrintScreen 後に実行すると、uiFormatIdは、
  CF_BITMAP = 2;
  CF_DIB = 8;
  CF_MAX = 17;

が取得でき、GlobalSize(hMem) > 0 になったのは、CF_DIB のみ。

Delphi 5 のIDEからソースコードをコピーすると
  CF_TEXT = 1;
  CF_OEMTEXT = 7;
  CF_UNICODETEXT = 13;
  CF_LOCALE = $10;
に加えて、49608:Borland IDE Block Type が含まれており、
全て、GlobalSize(hMem) > 0 になりました。

ちなみに、メタファイルに書き出すと
(探せばサンプルは見つかるでしょう)
  CF_METAFILEPICT = 3;
  CF_ENHMETAFILE = 14;

GlobalSize(hMem) > 0 は、CF_METAFILEPICTのみ。

クリップボードには、そのフォーマットのデータを含むものと、
「このフォーマットを含んでいるよ」みたいなフラグ的なものが
あったりするんじゃないかと、勝手に推測してみたりして。。。

ママん さん
>           if uiFormatId=CF_BITMAP then
>             showmessage(inttostr(dwSize));
> (略)
> と入れてみるとdwSizeが0であることが分かります。

したがって、これもそういう仕様なのでは?
などと納得できそうな気もします。

pkenさん
> 画像はもとよりテキストですら上手くいかない状況です。
何が、どう、うまくいかないのでしょう?


ん?  2006-07-07 18:23:24  No: 22396

> > と入れてみるとdwSizeが0であることが分かります。
> したがって、これもそういう仕様なのでは?
> などと納得できそうな気もします。

あ〜、これが
> オブジェクト参照
ってことなのですか?

今頃気がつく orz


ママん  2006-07-07 23:49:32  No: 22397

CF_BITMAPならこんな感じで取得可能ですが、
ここで言う「TDIBSection」が不明だと取得できないということです。
正確には私にはできないだけだと思います。
調べれば恐らくできると思うのですが、今のところ調べてません。

procedure TForm1.Button2Click(Sender: TObject);
var
  hdc,hWnd,hBitmap,hdc_mem:THandle;
  bm:Pointer;
  SrcDIB: TDIBSection;
begin
   if (IsClipboardFormatAvailable(CF_BITMAP)) then
   begin
     Clipboard.Open;
     hBitmap := Windows.GetClipboardData(CF_BITMAP);
     hdc_mem := CreateCompatibleDC(Canvas.Handle);
     SelectObject(hdc_mem, hBitmap);
     GetObject(hBitmap, sizeof(SrcDIB), @SrcDIB);
     BitBlt(Canvas.Handle, 0, 0, SrcDIB.dsBm.bmWidth, SrcDIB.dsBm.bmWidth, hdc_mem, 0, 0, SRCCOPY);
     DeleteDC(hdc_mem);
     Clipboard.Close;
   end;
end;


pken  2006-07-08 04:25:58  No: 22398

ん? さん
検証ありがとうございます。
やはりハンドルをロックしてポインタをもらうこと自体が
不可能なフォーマットが多くあるのですね。

質問の文章不足申し訳ありません。上手くいかないというのは、
教えていただいたリンク先のにしのさんの関数を使っても
クリップボードの内容がテキストデータの場合は表示されるのが「.」だけとなり、
ビットマップの場合はTBitmap.Loadfromstreamを使って読み込もうとすると
「ビットマップイメージが不正です」というエラーが出てしまうことです。

ママんさん
ソースありがとうございます。
確かにこの方法ならクリップボード内の画像を表示させることができました。
TDIBSectionはビットマップを詳しく扱うためのものでしょうか。むぅ、難しいです。

自分でもいろいろと試してはいるのですが、DelphiやWindowsについて
細かいところまで理解できていないせいで皆様のお力添えに頼りきっている状態で恐縮です。
この週末でどうしても不可能な場合、仕様の変更も検討しています。


なんだか  2006-07-08 22:52:37  No: 22399

横から入って的外れだったらもうしわけないのですが、
TClipboard.GetFormat
って(ヘルプを見る限りでは)やろうとしていることピタリですよね。

GetFormatでクリップボードを取り出すサンプルコードは見つかんなかったから、なんともいえないのだけど。


pken  2006-07-11 03:07:26  No: 22400

なんだかさん
GetFormat、いろいろ調べて試してみたのですが上手く参考になる資料を
探し出すことができず、断念しました。情報ありがとうございました。

結果、万能に対応させることを諦め、とりあえずフォーマットを限定して対応させ、
開発が軌道に乗ってきた後に出来る限り対応させるフォーマットを増やしていくことにします。

ご助力くださった皆さん、本当にありがとうございました。


なんだか  2006-07-11 06:19:55  No: 22401

ありゃ、残念。

何とかなりそうなんだけど、何とかなら無いんだよなぁ・・・。
う〜ん難しい;


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

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






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