Как вставить данные буфера обмена пользовательского формата в TMemo?

Этот вопрос относится к этому вместе с его принятый ответ размещен здесь в stackoverflow.

Я не чувствую себя комфортно в программировании Windows API.

Изучение способа EasyGPS от Topografix обрабатывает манипуляции с буфером обмена, я обнаружил, что он использует собственный формат буфера обмена с именем GPX, который на самом деле представляет собой обычный текст XML (GPX, если быть точным). Использование Clipboard.AsText исключено.

Я спотыкаюсь на этом этапе:

program ProbeClipboard;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  ClipBrd;

var
  CF_GPX: Cardinal;
  ClipboardData: THandle;

begin
  CF_GPX:=RegisterClipboardFormat('GPX');

  if ClipBoard.HasFormat(CF_GPX) then
  begin
    Writeln('GPX format available in clipboard');
    //
    OpenClipboard(0);

    ClipboardData := GetClipboardData(CF_GPX);

    if ClipboardData = 0 then
      raise Exception.Create('Clipboard data Error');

    /// How to use GlobalLock and GlobalUnLock
    /// so that I can paste the Clipboard data
    /// to a TMemo instance for example

    CloseClipboard;
  end;
end.

Пожалуйста, помогите мне исправить эту программу.


person menjaraz    schedule 01.01.2012    source источник
comment
FWIW, вам нужно попробовать, наконец, здесь, чтобы защитить OpenClipboard. Вам также нужна проверка ошибок при вызове OpenClipboard.   -  person David Heffernan    schedule 01.01.2012


Ответы (1)


Я бы написал так:

program ProbeClipboard;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  ClipBrd;

var
  CF_GPX: Cardinal;
  ClipboardData: Windows.HGLOBAL;
  Ptr: Pointer;
  Size: DWORD;

begin
  CF_GPX := RegisterClipboardFormat('GPX');

  Clipboard.Open;
  try
    if Clipboard.HasFormat(CF_GPX) then
    begin
      Writeln('GPX format available in clipboard');

      ClipboardData := Clipboard.GetAsHandle(CF_GPX);
      if ClipboardData=0 then
        RaiseLastOSError;

      Ptr := Windows.GlobalLock(ClipboardData);
      if Ptr=nil then
        RaiseLastOSError;

      try
        Size := Windows.GlobalSize(ClipboardData);

        //Ptr now points to a memory block of Size bytes 
        //containing the clipboard data
      finally
        Windows.GlobalUnlock(ClipboardData);
      end;
    end;
  finally
    Clipboard.Close;
  end;
end.

Обратите внимание, что я переместил команду буфера обмена Open, которая блокирует буфер обмена, чтобы он находился за пределами теста для формата CF_GPX. Это делается для того, чтобы избежать состояния гонки, которое существует в вашем коде. В вашем коде буфер обмена может быть изменен между вызовом HasFormat и вызовом OpenClipboard.

Я также использовал исключительно класс Clipboard. В этом классе есть все, что вам нужно, и вам не нужно использовать необработанный API буфера обмена Win32.

Я даже включил проверку ошибок!

person David Heffernan    schedule 01.01.2012
comment
Спасибо, что указали мне, что стандартный синглтон Clipboard делает свою работу, я был слишком сосредоточен на Windows API, несмотря на мой плохой уровень владения языком. Подойдет ли PChar вместо Pointer, поскольку предполагаемый пользовательский формат буфера обмена GPX на самом деле представляет собой обычный текст? - person menjaraz; 02.01.2012
comment
Как именно вытащить строковые данные, зависит от того, в какой кодировке они находятся. Знаете ли вы? АНСИ? UTF8? UTF16? Тем не менее, я бы хотел использовать процедуру SetString, чтобы убедиться, что я не переполнил буфер. - person David Heffernan; 02.01.2012
comment
Используйте SetString(utf8str, PAnsiChar(Ptr), Size-1) - person David Heffernan; 02.01.2012
comment
Пожалуйста, не могли бы вы сделать мне одолжение? Было бы неплохо, если бы вы могли указать мне, где на сайтах stackoverflow/stackexchange я могу найти обширный материал о процессе tidying up, который вы представили мне ранее. Мне нужна ссылка, чтобы убедить кого-то на SO, кто не считает это нужным. - person menjaraz; 02.01.2012
comment
@menjaraz Прибираешься? Поднял это из того, что делают другие, и из комментариев модераторов на мете. Но это довольно очевидно. Например, если вы указываете на ошибку в ответе, а затем ответ редактируется, чтобы исправить ошибку, удалить комментарий будет вежливо. - person David Heffernan; 03.01.2012