BSOne.SFC/Tocsg.Lib/VCL/Tocsg.Clipboard.pas

636 lines
16 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.Clipboard }
{ }
{ Copyright (C) 2019 kku }
{ }
{*******************************************************}
unit Tocsg.Clipboard;
// 잠깐 만들던건.. 여기 Tocsg.Clipboard_Old
interface
uses
System.SysUtils, Vcl.Clipbrd, Winapi.Windows, Winapi.Messages, System.Classes;
//const
// arrCbImgFormat: array [0..26] of DWORD = (
// CF_DIB, CF_DIBV5, CF_BITMAP,
// // 윈도우 기본 캡쳐 시 사용되는 이미지 포맷 25_0306 16:31:21 kku
// 491661, 49350, 50384, 50209, 48171,
// // 엑셀 셀 복사 시 사용하는 이미지 포맷
// 16,
// // 엑셀, 파워포인트에서 사용하는 이미지 포맷
// 49718,
// // 파워포인트에서 사용하는 이미지 포맷
// 50706, 49350, 49523, 49161, 50707, 49348,
// 49171, 49431, 50703, 49436, 50152, 49166,
// 49349, 49935, 49360, 49298, 14
// );
// 클립보드 수행시 뜨는 포맷 목록들
//Text
//Debug Output: OnDrawClipboard() - FormatCount : 4 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(0) : 13 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(1) : 16 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(2) : 1 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(3) : 7 Process kku Assister.exe (4708)
//Capture
//Debug Output: OnDrawClipboard() - FormatCount : 3 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(0) : 2 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(1) : 8 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(2) : 17 Process kku Assister.exe (4708)
//File
//Debug Output: OnDrawClipboard() - FormatCount : 11 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(0) : 49161 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(1) : 49359 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(2) : 50043 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(3) : 50044 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(4) : 49439 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(5) : 49414 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(6) : 49463 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(7) : 15 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(8) : 49158 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(9) : 49159 Process kku Assister.exe (4708)
//Debug Output: OnDrawClipboard() - Format(10) : 49171 Process kku Assister.exe (4708)
type
// AllocateHWnd사용으로 thread에서 사용주의
TDrawClipboard = class;
TNotifyClipboard = procedure(Sender: TDrawClipboard) of object;
TDrawClipboard = class(TClipboard)
private
hWnd_: HWND;
hNextCBWnd_,
hAllocHWND_: HWND;
bReseting_: Boolean;
procedure WndProc(var msg: TMessage); override;
protected
evDrawCB_: TNotifyClipboard;
procedure SetDrawCBEvent(evDrawCB: TNotifyClipboard);
procedure process_WM_CHANGECBCHAIN(var msg: TMessage);
procedure process_WM_DRAWCLIPBOARD(var msg: TMessage);
// procedure process_WM_CLIPBOARDUPDATE(var msg: TMessage);
public
Constructor Create(h: HWND = 0);
Destructor Destroy; override;
procedure Stop;
procedure Reset;
property OnDrawClipboard: TNotifyClipboard read evDrawCB_ write SetDrawCBEvent;
property NextCBWnd: HWND read hNextCBWnd_ write hNextCBWnd_;
end;
TClipboardHelper = class helper for TClipboard
public
function GetSize: Integer;
end;
function CopyToClipboard(const S: String; bDoClear: Boolean): Boolean;
function ClearClipboard(bImageOnly: Boolean = false): Boolean;
function OpenCloseClipboardTest: Boolean;
function IsClipboardEmpty: Boolean;
function GetClipboardSize: Integer;
function GetClipboardHandles: String;
var
CF_HTML: DWORD;
CF_BS1Empty: DWORD;
{
Clipboard has 8 Formats.
49161 = DataObject
49268 = Shell IDList Array
15 = CF_hDrop
49158 = FileName
49159 = FileNameW
49389 = Preferred DropEffect
49380 = Shell Object Offsets
49171 = Ole Private Data
}
implementation
uses
Vcl.Forms, Tocsg.Exception, Tocsg.Trace, Tocsg.Safe, Vcl.Graphics,
System.Generics.Collections, Tocsg.Strings;
{ TDrawClipboard }
Constructor TDrawClipboard.Create(h: HWND = 0);
begin
Inherited Create;
hWnd_ := h;
bReseting_ := false;
hAllocHWND_ := 0;
Reset;
// hAllocHWND_ := AllocateHWnd(WndProc);
// ASSERT(AddClipboardFormatListener(hAllocHWND_) = true);
end;
Destructor TDrawClipboard.Destroy;
begin
Stop;
// RemoveClipboardFormatListener(hAllocHWND_);
// DeallocateHWnd(hAllocHWND_);
// hAllocHWND_ := 0;
Inherited;
end;
procedure TDrawClipboard.Stop;
begin
if hAllocHWND_ <> 0 then
begin
if hNextCBWnd_ <> 0 then
begin
ChangeClipboardChain(hAllocHWND_, hNextCBWnd_);
hNextCBWnd_ := 0;
end;
DeallocateHWnd(hAllocHWND_);
hAllocHWND_ := 0;
end else
if hWnd_ <> 0 then
begin
if hNextCBWnd_ <> 0 then
begin
ChangeClipboardChain(hWnd_, hNextCBWnd_);
hNextCBWnd_ := 0;
end;
end;
end;
procedure TDrawClipboard.Reset;
begin
// bReseting_ := true; // 초기화 할때 재감지 되도록 다시 롤백 24_0716 18:06:45 kku
try
Stop;
if hWnd_ <> 0 then
begin
hNextCBWnd_ := SetClipboardViewer(hWnd_);
end else begin
hAllocHWND_ := AllocateHWnd(WndProc);
hNextCBWnd_ := SetClipboardViewer(hAllocHWND_);
end;
finally
bReseting_ := false;
end;
end;
procedure TDrawClipboard.SetDrawCBEvent(evDrawCB: TNotifyClipboard);
begin
if @evDrawCB_ <> @evDrawCB then
evDrawCB_ := evDrawCB;
end;
procedure TDrawClipboard.WndProc(var msg: TMessage);
begin
case msg.Msg of
WM_CHANGECBCHAIN : process_WM_CHANGECBCHAIN(msg);
WM_DRAWCLIPBOARD : process_WM_DRAWCLIPBOARD(msg);
// WM_CLIPBOARDUPDATE : process_WM_CLIPBOARDUPDATE(msg);
else
msg.Result := DefWindowProc(hAllocHWND_, msg.Msg, msg.WParam, msg.LParam);
end;
end;
procedure TDrawClipboard.process_WM_CHANGECBCHAIN(var msg: TMessage);
begin
with TWMChangeCBChain(msg) do
begin
if (Remove = hNextCBWnd_) then
hNextCBWnd_ := Next
else if hNextCBWnd_ <> 0 then
SendMessage(hNextCBWnd_, WM_CHANGECBCHAIN, Remove, Next);
end;
end;
procedure TDrawClipboard.process_WM_DRAWCLIPBOARD(var msg: TMessage);
begin
if bReseting_ then
exit;
// msg as TWMDrawClipboard
if Assigned(evDrawCB_) then
evDrawCB_(Self);
if hNextCBWnd_ <> 0 then
SendMessage(hNextCBWnd_, msg.Msg, msg.WParam, msg.LParam);
// SendMessage(hNextCBWnd_, WM_DRAWCLIPBOARD, 0, 0);
end;
//procedure TDrawClipboard.process_WM_CLIPBOARDUPDATE(var msg: TMessage);
//begin
// if Assigned(evDrawCB_) then
// evDrawCB_(Self);
//end;
{
// 클립보드에 CF_HTML 형태로 입력
// _____________________________________________________________________________
procedure SetClipBoardHTML(s: String);
const
START_FRAGMENT='<!--StartFragment-->';
END_FRAGMENT ='<!--EndFragment-->';
var
gMem: HGLOBAL;
pBytes: PByteArray;
i: Integer;
tmpStr: String;
LBuffer: TBytes;
begin
// HTML Format에 맞게 세팅
tmpStr:='Version:1.0'+#13#10;
tmpStr:=tmpStr+'StartHTML:~~~~~~~~'+#13#10;
tmpStr:=tmpStr+'EndHTML:########'+#13#10;
tmpStr:=tmpStr+'StartFragment:~~~~~~~~'+#13#10;
tmpStr:=tmpStr+'EndFragment:########'+#13#10;
tmpStr:=tmpStr+'<html><body>'+#13#10;
tmpStr:=tmpStr+'<!--StartFragment-->'+s+'<!--EndFragment-->'+#13#10;
tmpStr:=StringReplace(tmpStr, '~~~~~~~~', Format('%.08d', [Pos('<html', tmpStr)-1]), [rfReplaceAll]);
tmpStr:=StringReplace(tmpStr, '########', Format('%.08d', [Pos(END_FRAGMENT, tmpStr)+Length(END_FRAGMENT)]), [rfReplaceAll]);
// 버퍼에 복사
SetLength(LBuffer, Length(tmpStr)+1);
for i:=0 to Length(tmpStr) do LBuffer[i]:=Byte(tmpStr[i+1]);
// 클립보드용 버퍼에 복사
gMem:=GlobalAlloc(GHND, Length(LBuffer)*2);
pBytes:=GlobalLock(gMem);
try
for i:=0 to Length(LBuffer)+1 do pBytes[i]:=LBuffer[i];
finally
GlobalUnlock(gMem);
end;
// 클립보드에 쓰기
OpenClipBoard(0);
EmptyClipBoard;
SetClipBoardData(RegisterClipBoardFormat('HTML Format'), gMem);
CloseClipBoard;
end;
// 클립보드의 CF_HTML 포맷데이터 가져오기
// _____________________________________________________________________________
function GetClipBoardHTML: String;
var
gMem: HGLOBAL;
pBytes: PByteArray;
lenBuffer: DWORD;
i: Integer;
CF_HTML: UINT;
begin
Result:='';
// CF_HTML 등록
CF_HTML:=RegisterClipboardFormat('HTML Format');
// 클립보드를 열어 CF_HTML 데이터가 있으면 읽어와서 반환
OpenClipBoard(0);
if ClipBoard.HasFormat(CF_HTML) then begin
gMem:=GetClipboardData(CF_HTML);
pBytes:=GlobalLock(gMem);
try
lenBuffer:=Length(PChar(pBytes))*2;
SetLength(Result, lenBuffer);
for i:=0 to lenBuffer do Result[i+1]:=Chr(pBytes[i]);
Result:=TEncoding.UTF8.GetString(TBytes(pBytes), 0, lenBuffer);
finally
GlobalUnlock(gMem);
end;
end;
CloseClipBoard;
end;
}
{ TClipboardHelper }
const
{$IF DEFINED(UNICODE)}
CTextFormat = CF_UNICODETEXT;
{$ELSE}
CTextFormat = CF_TEXT;
{$ENDIF}
function TClipboardHelper.GetSize: Integer;
var
Data: THandle;
begin
Result := 0;
try
Open;
Data := GetClipboardData(CTextFormat);
try
if Data <> 0 then
Result := GlobalSize(Data);
finally
if Data <> 0 then
GlobalUnlock(Data);
Close;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. GetSize()');
end;
end;
{ Function }
function OpenAndClearClipboard: Boolean;
begin
Result := OpenClipboard(0);
if Result then
EmptyClipboard;
end;
function CopyToClipboard(const S: String; bDoClear: Boolean): Boolean;
var
nDataSize, nBufferSize: Integer;
hData: HGLOBAL;
pData: Pointer;
begin
nDataSize := Length(S) * 2;
if nDataSize > 0 then
begin
if bDoClear then
begin
if not OpenAndClearClipboard then
begin
Result := false;
exit;
end;
end;
nBufferSize := nDataSize + 2;
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, nBufferSize);
if hData <> 0 then
begin
pData := GlobalLock(hData);
if pData <> nil then
begin
Move(S[1], pData^, nBufferSize);
GlobalUnlock(hData);
SetClipboardData(CF_UNICODETEXT, hData);
end;
end;
if bDoClear then
CloseClipboard;
end;
Result := True;
end;
procedure RemoveClipboardImageFormat;
type
PCbEnt = ^TCbEnt;
TCbEnt = record
uType: UINT;
pData: Pointer;
nSize: Integer;
end;
var
uFormat: UINT;
CbTxtFmtList: TList<PCbEnt>; // 해제 필요
pEnt: PCbEnt;
hData: THandle;
hCopy: THandle;
pData: Pointer;
i: Integer;
begin
CbTxtFmtList := TList<PCbEnt>.Create;
try
uFormat := EnumClipboardFormats(0);
while uFormat <> 0 do
begin
case uFormat of
CF_TEXT,
CF_OEMTEXT,
CF_UNICODETEXT : ;
else begin
if uFormat <> CF_HTML then
begin
uFormat := EnumClipboardFormats(uFormat);
continue;
end;
end;
end;
if (uFormat <> CF_HTML) and (uFormat > 16) then
begin
uFormat := EnumClipboardFormats(uFormat);
continue;
end;
New(pEnt);
ZeroMemory(pEnt, SizeOf(TCbEnt));
pEnt.uType := uFormat;
CbTxtFmtList.Add(pEnt);
uFormat := EnumClipboardFormats(uFormat);
end;
for pEnt in CbTxtFmtList do
begin
hData := GetClipboardData(pEnt.uType);
if hData <> 0 then
begin
try
pEnt.nSize := GlobalSize(hData);
except
// 사이즈 가져올때 오류 발생 가능성
continue;
end;
if pEnt.nSize > 0 then
begin
pEnt.pData := AllocMem(pEnt.nSize);
pData := GlobalLock(hData);
try
Move(pData^, pEnt.pData^, pEnt.nSize);
finally
GlobalUnlock(hData);
end;
end;
end;
end;
// 클립보드 비우기
EmptyClipboard;
// 기존 클립보드 데이터를 복원
for pEnt in CbTxtFmtList do
begin
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, pEnt.nSize);
if hData <> 0 then
begin
try
pData := GlobalLock(hData);
try
CopyMemory(pData, pEnt.pData, pEnt.nSize);
finally
GlobalUnlock(hData);
end;
SetClipboardData(pEnt.uType, hData);
except
GlobalFree(hData);
end;
end;
if pEnt.pData <> nil then
begin
FreeMem(pEnt.pData);
pEnt.pData := nil;
end;
end;
finally
FreeAndNil(CbTxtFmtList);
end;
end;
function ClearClipboard(bImageOnly: Boolean = false): Boolean;
var
i: Integer;
hData: THandle;
pData: Pointer;
begin
Result := false;
try
// Guard(CB, TClipboard.Create);
// CB.AsText := '';
// Result := CB.AsText = '';
// CB.Clear
//bImageOnly := true;
if not OpenClipboard(0) then
begin
// todo : 실패하면 재시도 할지... 25_0310 10:12:10 kku
{$IFDEF DEBUG}
TTgTrace.T('ClearClipboard() .. Fail .. OpenClipboard()');
{$ENDIF}
exit;
end;
try
if bImageOnly then
begin
// TTgTrace.T('ClearClipboard() .. Image');
RemoveClipboardImageFormat;
end else begin
EmptyClipboard;
end;
{$IFDEF _HE_}
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, 1);
if hData <> 0 then
begin
try
pData := GlobalLock(hData);
try
CopyMemory(pData, PAnsiChar(#0), 1);
SetClipboardData(CF_BS1Empty, hData);
finally
GlobalUnlock(hData);
end;
except
GlobalFree(hData);
end;
end;
{$ENDIF}
Result := true;
finally
CloseClipboard;
end;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. ClearClipboard()');
end;
end;
function OpenCloseClipboardTest: Boolean;
begin
Result := false;
try
if not OpenClipboard(0) then
exit;
CloseClipboard;
Result := true;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. OpenCloseClipboardTest()');
end;
end;
function IsClipboardEmpty: Boolean;
var
uFormat: UINT;
i, nCnt: Integer;
begin
// 사용하지 않음. 나중에 지워도 됨 25_0310 10:11:31 kku
Result := false;
if not OpenClipboard(0) then
exit;
try
nCnt := 0;
while uFormat <> 0 do
begin
Inc(nCnt);
uFormat := EnumClipboardFormats(uFormat);
end;
Result := nCnt > 0;
finally
CloseClipboard;
end;
end;
function GetClipboardSize: Integer;
var
uFormat: UINT;
hData: THandle;
begin
// 사용하지 않음. 나중에 지워도 됨 25_0310 10:11:31 kku
Result := 0;
if not OpenClipboard(0) then
exit;
try
uFormat := EnumClipboardFormats(0);
while uFormat <> 0 do
begin
hData := GetClipboardData(uFormat);
try
if hData <> 0 then
Inc(Result, GlobalSize(hData));
except
{$IFDEF DEBUG}
TTgTrace.T('Fail .. GetClipboardSize() .. GlobalSize() .. hData = %d', [hData]);
{$ENDIF}
end;
uFormat := EnumClipboardFormats(uFormat);
end;
finally
CloseClipboard;
end;
end;
function GetClipboardHandles: String;
var
uFormat: UINT;
hData: THandle;
begin
// 사용하지 않음. 나중에 지워도 됨 25_0310 10:11:31 kku
Result := '';
if not OpenClipboard(0) then
begin
TTgTrace.T('Fail .. GetClipboardHandles() .. 1, Error=%d', [GetLastError]);
exit;
end;
try
uFormat := EnumClipboardFormats(0);
if uFormat = 0 then
TTgTrace.T('Fail .. GetClipboardHandles() .. 2');
while uFormat <> 0 do
begin
hData := GetClipboardData(uFormat);
SumString(Result, IntToStr(hData), '|');
uFormat := EnumClipboardFormats(uFormat);
end;
finally
CloseClipboard;
end;
end;
Initialization
CF_HTML := RegisterClipboardFormat('HTML Format');
CF_BS1Empty := RegisterClipboardFormat('BSOne CB Empty'); { Do not localize }
end.