Как создать свой собственный не системный буфер обмена? - PullRequest
3 голосов
/ 12 февраля 2012

Возможно ли это, и если да, как бы вы реализовали свой собственный буфер обмена?

Под этим я имею в виду возможность копировать и вставлять что-либо в него и из него так же, как это делает буфер обмена Windows, но безна самом деле мешает работе с системным буфером обмена.

Чтобы лучше понять, я попробовал вот что:

uses
  ClipBrd;

...

procedure TMainForm.actCopyExecute(Sender: TObject);
var
  MyClipboard: TClipboard;
begin
  MyClipboard := TClipboard.Create;
  try
    MyClipboard.AsText := 'Copy this text';
  finally
    MyClipboard.Free;
  end;
end;

Это работает так, что скопирует строку «Копировать этот текст» в буфер обмена., но он перезаписывает все, что было в буфере обмена Windows.

Выше необходимо просто создать экземпляр буфера обмена Windows, а не создавать собственный.

Обратите внимание, что пользовательский буфер обмена может содержать любые данныене просто текст.Он должен работать так же, как буфер обмена Windows, но не мешать ему (теряя все, что было на нем).

Как этого можно достичь?

Спасибо.

Ответы [ 4 ]

5 голосов
/ 12 февраля 2012

Ваш вопрос сбивает с толку; вы говорите, что хотите сделать это, не затрагивая системный буфер обмена, но затем (из вашего собственного комментария к вашему вопросу) вы, похоже, хотите реализовать что-то вроде Paste Special в MS Office.

Если это первое, как говорили другие, вы не можете сделать это с помощью оболочки TClipboard; Вы должны реализовать свои собственные, и передача информации между приложениями будет очень сложной.

Если это второе, вы делаете это с помощью Windows API RegisterClipboardFormat , чтобы определить свой собственный формат.

type
  TForm1=class(TForm)
    YourCustomFormat: Word;
    procedure FormCreate(Sender: TObject);
  end;

implementation

constructor TForm1.FormCreate(Sender: TObject);
begin
  YourCustomFormat := RegisterClipboardFormat('Your Custom Format Name');
end;

Чтобы поместить информацию в буфер обмена в произвольном формате, вы должны использовать GlobalAlloc и GlobalLock для выделения и блокировки блока глобальной памяти, копирования данных в этот блок, разблокировки блок, используя GlobalUnlock , используйте TClipboard.SetAsHandle для переноса блока памяти в буфер обмена. Затем вам нужно позвонить GlobalFree , чтобы освободить блок памяти.

Чтобы получить данные в вашем пользовательском формате, вы делаете в основном то же самое, выполнив несколько шагов в обратном порядке. Вы используете GlobalAlloc / GlobalLock, как и раньше, используйте TClipboard.GetAsHandle, чтобы извлечь содержимое буфера обмена, скопировать его в локальную переменную и затем вызвать GlobalFree.

Вот старый пример размещения нестандартного формата (в данном случае RTF-текста) в буфер обмена - это из сообщения группы новостей от доктора Питера Белоу из TeamB . (Код и форматирование взяты из исходного поста; я не проверял и даже не компилировал его.) Из инструкций о том, что изменить выше, следует ясно поменять процесс, чтобы получить его обратно, и я оставляю это вам работать. :)

procedure TForm1.BtnSetRTFClick(Sender: TObject);
Const
  testtext: PChar = '{\rtf1\ansi\pard\plain 12{\ul 44444}}';
  testtext2: PChar = '{\rtf1\ansi'+
  '\deff4\deflang1033{\fonttbl{\f4\froman\fcharset0\fprq2 Times New Roman;}}'
                     +'\pard\plain 12{\ul 44444}}';
  flap: Boolean = False;
Var
  MemHandle: THandle;
  rtfstring: PChar;
begin
    If flap Then
      rtfstring := testtext2
    Else
      rtfstring := testtext;
    flap := not flap;
    MemHandle := GlobalAlloc( GHND or GMEM_SHARE, StrLen(rtfstring)+1 );
    If MemHandle <> 0 Then Begin
      try
        StrCopy( GlobalLock( MemHandle ), rtfstring );
        GlobalUnlock( MemHandle );
        With Clipboard Do Begin
          Open;
          try
            AsText := '1244444';
            SetAsHandle( CF_RTF, MemHandle );
          finally
            Close;
          end;
        End;
      Finally
        GlobalFree( MemHandle );
      End;
    End
    Else
      MessageDlg('Global Alloc failed!',
                 mtError, [mbOK], 0 );
end;
2 голосов
/ 12 февраля 2012

Вы должны определить свой собственный буфер обмена.Это может выглядеть примерно так:

type
  TMyCustomClipboard = class
  private
    FStream: TMemoryStream;
    function GetAsText: string;
    procedure SetAsText(const Value: string);
    ...
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    property AsText: string read GetAsText write SetAsText;
    procedure AsAnyThing: AnyType read GetAsAnyThing write AsAnyThing;
    ...
  end;

Тогда вы можете использовать FStream в качестве пользовательского контейнера буфера обмена.Вы можете хранить (копировать) любые данные внутри этого потока и использовать (вставлять), когда вам это нужно.Вам просто нужно написать несколько методов Get / Set для ваших типов данных.

1 голос
/ 12 февраля 2012

Вы не можете.У вас может быть внутренний буфер памяти, в который вы перемещаете данные и извлекаете их, вы можете называть его «копировать» и «вставлять», если хотите, но не помещайте его в пользовательский интерфейс таким образом, или вы просто запутаетеваши пользователи.Существует только один системный буфер обмена, и вы не можете помещать в него данные, не затрагивая другие программы.Если ваша следующая мысль - сохранить буфер обмена, перезаписать ваши материалы, а затем восстановить исходное содержимое, не беспокойтесь.

1 голос
/ 12 февраля 2012

TClipboard - системный буфер обмена, инкапсулирующий класс, поэтому его нельзя использовать для создания другой копии буфера обмена. Вы должны реализовать свой собственный класс, представляющий универсальный буфер с сеттерами и геттерами.

...