2014-03-24 24 views
2

我需要在剪贴板上创建以下格式都在一起:如何在剪贴板上一起创建图片和HTML格式?

CF_BITMAP
CF_DIB
CF_DIB5
HTML格式

这是一个可以制作图片格式或控制台程序HTML格式,但不是全部放在剪贴板上:

program CopyImageFromFile; 

{$APPTYPE CONSOLE} 
{$R *.res} 

uses 
    Winapi.Windows, 
    Vcl.Clipbrd, 
    Vcl.ExtCtrls, 
    Vcl.Imaging.pngimage, 
    System.SysUtils; 

function FormatHTMLClipboardHeader(HTMLText: string): string; 
const 
    CrLf = #13#10; 
begin 
    Result := 'Version:0.9' + CrLf; 
    Result := Result + 'StartHTML:-1' + CrLf; 
    Result := Result + 'EndHTML:-1' + CrLf; 
    Result := Result + 'StartFragment:000081' + CrLf; 
    Result := Result + 'EndFragment:°°°°°°' + CrLf; 
    Result := Result + HTMLText + CrLf; 
    Result := StringReplace(Result, '°°°°°°', Format('%.6d', [Length(Result)]), []); 
end; 

procedure CopyHTMLAndImageToClipBoard(const str, APngFile: AnsiString; const htmlStr: AnsiString = ''); 
var 
    gMem: HGLOBAL; 
    lp: PChar; 
    Strings: array[0..1] of AnsiString; 
    Formats: array[0..1] of UINT; 
    i: Integer; 

    ThisImage: TImage; 
    MyFormat: Word; 
    Bitmap: TBitMap; 
    AData: THandle; 
    APalette: HPALETTE; 
begin 
    gMem := 0; 
    //{$IFNDEF USEVCLCLIPBOARD} 
    //Win32Check(OpenClipBoard(0)); 
    //{$ENDIF} 
    Clipboard.Open; 
    try 
    //most descriptive first as per api docs 
    Strings[0] := FormatHTMLClipboardHeader(htmlStr); 
    Strings[1] := str; 
    Formats[0] := RegisterClipboardFormat('HTML Format'); 
    Formats[1] := CF_TEXT; 
    {$IFNDEF USEVCLCLIPBOARD} 
    Win32Check(EmptyClipBoard); 
    {$ENDIF} 
    for i := 0 to High(Strings) do 
    begin 
     if Strings[i] = '' then Continue; 
     //an extra "1" for the null terminator 
     gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(Strings[i]) + 1); 
     {Succeeded, now read the stream contents into the memory the pointer points at} 
     try 
     Win32Check(gmem <> 0); 
     lp := GlobalLock(gMem); 
     Win32Check(lp <> nil); 
     CopyMemory(lp, PChar(Strings[i]), Length(Strings[i]) + 1); 
     finally 
     GlobalUnlock(gMem); 
     end; 
     Win32Check(gmem <> 0); 
     SetClipboardData(Formats[i], gMEm); 
     Win32Check(gmem <> 0); 
     gmem := 0; 
    end; 

    ThisImage := TImage.Create(nil); 
    try 
     ThisImage.Picture.LoadFromFile(APngFile); 
     // Comment this out to copy only the HTML Format: 
     Clipboard.Assign(ThisImage.Picture); 
     {MyFormat := CF_PICTURE; 
     ThisImage.Picture.SaveToClipBoardFormat(MyFormat, AData, APalette); 
     ClipBoard.SetAsHandle(MyFormat, AData);} 
    finally 
     ThisImage.Free; 
    end; 
    finally 
    //{$IFNDEF USEVCLCLIPBOARD} 
    //Win32Check(CloseClipBoard); 
    //{$ENDIF} 
    Clipboard.Close; 
    end; 
end; 

var 
    HTML: string; 

begin 
    try 
    // Usage: CopyImageFromFile.exe test.png 
    // test.png is 32 bit with alpha channel 
    if ParamCount = 1 then 
    begin 
     if FileExists(ParamStr(1)) then 
     begin 
     if LowerCase(ExtractFileExt(ParamStr(1))) = '.png' then 
     begin 
      HTML := '<img border="0" src="file:///' + ParamStr(1) + '">'; 
      CopyHTMLAndImageToClipBoard('test', ParamStr(1), HTML); 
     end; 
     end; 
    end; 
    except 
    on E: Exception do 
    begin 
     Writeln(E.ClassName, ': ', E.Message); 
     Readln; 
    end; 
    end; 

end. 

那么如何在剪贴板上一起创建所有这些格式呢?

+1

呼叫SetClipboardData一次为每个格式。 –

+0

你会如何做所有的ThisImage.Picture全球内存的东西? – user1580348

+0

好吧,我不会把所有的代码混合成这样的一个大的glob。我将它分解为具有特定和不同任务的功能。 –

回答

4

TClipboard清空剪贴板中的第一次使用一个TClipboard方法调用Open()后把剪贴板上(TClipboard.Assign()TClipboard.SetBuffer()TClipboard.SetAsHandle()等)的数据。 TClipboard希望你只使用它来访问剪贴板的方法,让你的SetClpboardData()直接使用存储您的字符串数据绕过TClipboard的内部逻辑,因此你要Assign()呼叫被看作是第一代剪贴板写入和TClipboard抹了任何数据你用SetClipboardData()存储。

为了避免这种情况,你有几个选择:

  1. Assign()图像到剪贴板中,然后再保存SetClipboardData()后您的字符串的项目。

  2. 根本不使用Assign()。直接使用TPicture.SaveToClipboardFormat(),然后致电SetClipboardData()

  3. 请勿直接使用SetClipboardData(),除非USEVCLCLIPBOARD未定义。改为使用TClipboard.SetAsHandle()

我建议#3。让TClipboard做所有的工作:

var 
    CF_HTML: UINT = 0; 

// TClipboard.SetBuffer() allows a format and an arbitrary buffer 
// to be specified and handles the global memory allocation. 
// However, it is protected, so using an accessor class to reach it. 
// 
// TClipboard.AsText and TClipboard.SetTextBuf() always use 
// CF_(UNICODE)TEXT, and TClipboard.SetAsHandle() requires manual 
// allocation... 
// 
type 
    TClipboardAccess = class(TClipboard) 
    end; 

procedure CopyHTMLAndImageToClipBoard(const str, APngFile: AnsiString; const htmlStr: AnsiString = ''); 
var 
    TmpHtmlStr: AnsiString; 
    ThisImage: TPicture; 
begin 
    Clipboard.Open; 
    try 
    //most descriptive first as per api docs 

    TmpHtmlStr := FormatHTMLClipboardHeader(htmlStr); 
    TClipboardAccess(Clipboard).SetBuffer(CF_HTML, PAnsiChar(TmpHtmlStr)^, Length(TmpHtmlStr) + 1); 
    TClipboardAccess(Clipboard).SetBuffer(CF_TEXT, PAnsiChar(Str)^, Length(Str) + 1); 

    ThisImage := TPicture.Create; 
    try 
     ThisImage.LoadFromFile(APngFile); 
     Clipboard.Assign(ThisImage); 
    finally 
     ThisImage.Free; 
    end; 
    finally 
    Clipboard.Close; 
    end; 
end; 

initialization 
    CF_HTML := RegisterClipboardFormat('HTML Format'); 

如果你真的需要支持{$IFNDEF USEVCLCLIPBOARD},那么你不能使用TClipboard可言,如:

var 
    CF_HTML: UINT = 0; 

{$IFDEF USEVCLCLIPBOARD} 
// TClipboard.SetBuffer() allows a format and an arbitrary buffer 
// to be specified and handles the global memory allocation. 
// However, it is protected, so using an accessor class to reach it. 
// 
// TClipboard.AsText and TClipboard.SetTextBuf() always use 
// CF_(UNICODE)TEXT, and TClipboard.SetAsHandle() requires manual 
// allocation... 
// 
type 
    TClipboardAccess = class(TClipboard) 
    end; 
{$ENDIF} 

procedure CopyHTMLAndImageToClipBoard(const str, APngFile: AnsiString; const htmlStr: AnsiString = ''); 
var 
    ThisImage: TPicture; 
    {$IFNDEF USEVCLCLIPBOARD} 
    ImgData: THandle; 
    ImgFormat: Word; 
    ImgPalette: HPALETTE; 
    {$ENDIF} 

    procedure SetAsText(Format: UINT; const S: AnsiString); 
    {$IFNDEF USEVCLCLIPBOARD} 
    var 
    gMem: HGLOBAL; 
    lp: PAnsiChar; 
    {$ENDIF} 
    begin 
    {$IFDEF USEVCLCLIPBOARD} 
    TClipboardAccess(Clipboard).SetBuffer(Format, PAnsiChar(S)^, Length(S) + 1); 
    {$ELSE} 
    //an extra "1" for the null terminator 
    gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(S) + 1); 
    Win32Check(gmem <> 0); 
    try 
     {Succeeded, now read the stream contents into the memory the pointer points at} 
     lp := GlobalLock(gMem); 
     Win32Check(lp <> nil); 
     try 
     CopyMemory(lp, PAnsiChar(S), Length(S) + 1); 
     finally 
     GlobalUnlock(gMem); 
     end; 
    except 
     GlobalFree(gMem); 
     raise; 
    end; 
    SetClipboardData(Format, gMem); 
    {$ENDIF} 
    end; 

begin 
    {$IFDEF USEVCLCLIPBOARD} 
    Clipboard.Open; 
    {$ELSE} 
    Win32Check(OpenClipBoard(0)); 
    {$ENDIF} 
    try 
    //most descriptive first as per api docs 
    SetAsText(CF_HTML, FormatHTMLClipboardHeader(htmlStr)); 
    SetAsText(CF_TEXT, Str); 

    ThisImage := TPicture.Create; 
    try 
     ThisImage.LoadFromFile(APngFile); 

     {$IFDEF USEVCLCLIPBOARD} 
     Clipboard.Assign(ThisImage); 
     {$ELSE} 
     ImgPalette := 0; 
     ThisImage.SaveToClipboardFormat(ImgFormat, ImgData, ImgPalette); 
     SetClipboardData(ImgFormat, ImgData); 
     if ImgPalette <> 0 then 
     SetClipboardData(CF_PALETTE, ImgPalette); 
     {$ENDIF} 
    finally 
     ThisImage.Free; 
    end; 
    finally 
    {$IFDEF USEVCLCLIPBOARD} 
    Clipboard.Close; 
    {$ELSE} 
    Win32Check(CloseClipBoard); 
    {$ENDIF} 
    end; 
end; 

initialization 
    CF_HTML := RegisterClipboardFormat('HTML Format'); 
+0

我没有在任何地方找到“Clipboard.SetAsBuffer”。它在哪里宣布? – user1580348

+0

对不起,我没有注意到它是'protected'。我调整了我的答案。 –

+0

谢谢,这真是太棒了!我从你身上学到了很多东西!我希望有一天我能够像帮助我一样帮助其他人! – user1580348

2

大卫是对的。你需要有一对open/close,并且只有一个EmptyClipboard。您需要遍历您的格式并为每个格式调用SetClipboardData。 RegisterClipboardFormat应该只调用一次,所以在一些初始化例程中这样做。
我也会尽量避免在打开剪贴板时做任何文件I/O操作,因为您不希望将其打开的时间超过必要的时间。即如果可能的话,首先从磁盘读取你的图片。