こんにちは。
現在、クリップボードのデータを扱うソフトを作っているのですが、クリップボード内のデータをストリームに書き出すことが上手くできません。
テキストデータ(CF_TEXT)ではいろいろなサンプルを参考にしつつできたのですが、必要なのはデータフォーマットに関係なくクリップボードの内容をストリームに書き出すことなのです。
今まで試したのは、QClipbrdにあるTClipboard.GetFormatや、WindowsAPIであるCopyMemoryを活用する方法です。
どうぞ宜しくお願いします。
あいょ〜。
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;
ママんさん
早速のお返事ありがとうございます。
この方法は見たことがあるのですが、TDevModeが初見でした。ヘルプ等でプリンタ関連であることはわかったのですが、ここではどのような意図で使われているのでしょうか?
また、たとえば
GetClipbordDataToStream(TStream(mStream),CF_BITMAP);
というふうに別のフォーマットにすると、メモリストリームに何も書き込まれなくなってしまうのですが、やはりフォーマット別に動作を分けるしかないのでしょうか。
一度に申し訳ありませんが、どうかお力添えをお願いします。
うーむ。
GlobalLockがnilを返すタイプ(CF_BITMAP等)はオブジェクト参照しているのでGetObjectで取得する必要がありますが、
GetObjectするにも対象Objectのサイズが分からないと取得しようがありません。
すみませんが私には分からないです。
今晩でも調べてみますが、多分ムリポ。
PDevMode : ^TDevMode;
うはっこれはコピペ元がそうであっただけで、ポインタなら何でもOKです。普通なら
mem : Pointer;
とかするのが正解ですね。スミマセン。
これみたいなこと?
https://www.petitmonte.com/bbs/answers?question_id=3354
一応保身の為に補足ですが、にしのさんのサンプルでも
オブジェクト参照しているデータは取れていません。
//...
// 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であることが分かります。
ん? さん
はい、このような感じでメモリストリームに書き込みができれば理想です。
過去ログ中でもこれはチェックし損ねていたようです。ありがとうございます。
ママんさん
回答ありがとうございます。
TDevModeに関してはこちらの早とちりで失礼しました。自分の見ていたところでもちゃんとポインタ型と書いてあったのに……。
画像などのデータはそもそも取得すら難しいということでしょうか。
ん? さんに教えていただいたところの最後では、テキストや画像で上手くいっているとのことだったのでいくらか試してみたのですが、画像はもとよりテキストですら上手くいかない状況です。
この関数の機能は自分の求めるものと同一であるので、どうにか動かせないかチャレンジしてみたいと思います。
環境: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さん
> 画像はもとよりテキストですら上手くいかない状況です。
何が、どう、うまくいかないのでしょう?
> > と入れてみるとdwSizeが0であることが分かります。
> したがって、これもそういう仕様なのでは?
> などと納得できそうな気もします。
あ〜、これが
> オブジェクト参照
ってことなのですか?
今頃気がつく orz
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;
ん? さん
検証ありがとうございます。
やはりハンドルをロックしてポインタをもらうこと自体が
不可能なフォーマットが多くあるのですね。
質問の文章不足申し訳ありません。上手くいかないというのは、
教えていただいたリンク先のにしのさんの関数を使っても
クリップボードの内容がテキストデータの場合は表示されるのが「.」だけとなり、
ビットマップの場合はTBitmap.Loadfromstreamを使って読み込もうとすると
「ビットマップイメージが不正です」というエラーが出てしまうことです。
ママんさん
ソースありがとうございます。
確かにこの方法ならクリップボード内の画像を表示させることができました。
TDIBSectionはビットマップを詳しく扱うためのものでしょうか。むぅ、難しいです。
自分でもいろいろと試してはいるのですが、DelphiやWindowsについて
細かいところまで理解できていないせいで皆様のお力添えに頼りきっている状態で恐縮です。
この週末でどうしても不可能な場合、仕様の変更も検討しています。
横から入って的外れだったらもうしわけないのですが、
TClipboard.GetFormat
って(ヘルプを見る限りでは)やろうとしていることピタリですよね。
GetFormatでクリップボードを取り出すサンプルコードは見つかんなかったから、なんともいえないのだけど。
なんだかさん
GetFormat、いろいろ調べて試してみたのですが上手く参考になる資料を
探し出すことができず、断念しました。情報ありがとうございました。
結果、万能に対応させることを諦め、とりあえずフォーマットを限定して対応させ、
開発が軌道に乗ってきた後に出来る限り対応させるフォーマットを増やしていくことにします。
ご助力くださった皆さん、本当にありがとうございました。
ありゃ、残念。
何とかなりそうなんだけど、何とかなら無いんだよなぁ・・・。
う〜ん難しい;
ツイート | ![]() |