2014-03-24 2 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

음, 모든 코드를 이처럼 하나의 큰 덩어리로 혼합하지는 않겠습니다. 나는 그것을 명확하고 뚜렷한 임무를 가진 함수들로 분리 할 것이다. –

답변

4

TClipboard 클립 보드 당신이 Open()를 호출 한 후 클립 보드 (TClipboard.Assign(), TClipboard.SetBuffer(), TClipboard.SetAsHandle() 등)에 데이터를 넣어 TClipboard 방법을 처음 사용할 때 웁니다. TClipboard은 클립 보드에 액세스하기 위해 해당 메서드 만 사용할 것으로 예상하므로 SetClpboardData()을 사용하여 문자열 데이터를 직접 저장하면 의 내부 논리를 무시하므로 Assign()에 대한 호출이 첫 번째 클립 보드 쓰기로 표시되고 TClipboard은 데이터를 지 웁니다. 너는 SetClipboardData()와 함께 저장했다. 다음, 먼저 클립 보드에

  1. Assign() 이미지를 나중에 SetClipboardData()로 문자열 항목을 저장합니다

    는 몇 가지 선택이있다,이를 방지합니다.

  2. Assign()을 전혀 사용하지 마십시오. TPicture.SaveToClipboardFormat()을 직접 사용하고 SetClipboardData()으로 전화하십시오.

  3. USEVCLCLIPBOARD이 정의되지 않은 한 SetClipboardData()을 직접 사용하지 마십시오. 대신 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'); 

을 당신이 정말로 당신이 전혀 TClipboard를 사용할 수 없습니다 {$IFNDEF USEVCLCLIPBOARD}를 지원해야하는 경우, 예를 들면 :

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

죄송 합니다만, 나는 그것이 '보호되어있다'는 것을 눈치 채지 못했습니다. 나는 내 대답을 조정했다. –

+0

고마워, 이건 정말 대단해! 나는 너에게서 많은 것을 배웠다. 언젠가 당신이 저를 도왔던 것과 같은 방식으로 다른 사람들을 도울 수 있기를 바랍니다. – user1580348

2

David이 맞습니다. 한 쌍의 열림/닫음과 한 개의 EmptyClipboard 만 있으면됩니다. 형식을 반복하고 각각에 대해 SetClipboardData를 호출해야합니다. RegisterClipboardFormat은 한 번만 호출해야하므로 일부 초기화 루틴에서 호출해야합니다.
필자는 클립 보드를 열었을 때 파일 I/O를하지 않으려합니다. 필자는 클립 보드를 오래 동안 필요 이상으로 열지 않으려 고합니다. 가능한 경우 먼저 디스크에서 사진을 읽습니다.