如何在剪贴板上一起创建图片和HTML格式?

时间:2014-03-24 18:44:23

标签: delphi clipboard delphi-xe4

我需要在剪贴板上一起创建以下格式:

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.  

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

2 个答案:

答案 0 :(得分:4)

第一次使用TClipboard方法将数据放入剪贴板时,

TClipboard清空剪贴板(TClipboard.Assign()TClipboard.SetBuffer()TClipboard.SetAsHandle()等)致电Open()后。 TClipboard希望您只使用其方法来访问剪贴板,因此直接使用SetClpboardData()来存储字符串数据会绕过TClipboard的内部逻辑,从而调用{{ 1}}被视为第一个剪贴板写入,Assign()删除了您使用TClipboard存储的所有数据。

为避免这种情况,您可以选择以下几种方法:

    首先将
  1. SetClipboardData()您的图片放入剪贴板,然后用Assign()保存您的字符串项。

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

  3. 除非未定义SetClipboardData(),否则不要直接使用SetClipboardData()。请改用USEVCLCLIPBOARD

  4. 我建议#3。让TClipboard.SetAsHandle()完成所有工作:

    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

答案 1 :(得分:2)

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