2007. március 10., szombat

Clipboard access routines which use only API functions


Problem/Question/Abstract:

Clipboard access routines which use only API functions

Answer:

This unit provides clipboard access routines that do not rely on the VCL Clipbrd unit. That unit drags in Dialogs and Forms and a major part of the VCL as a consequence, not appropriate for simple console or non-form programs. This unit uses only API routines, the only VCL units used are Classes (for exceptions and streams) and SysUtils.

{Clipboard access routines using only API functions
Author: Dr. Peter Below
Version 1.0 created 5 Juli 2000
Current revision 1.0
Last modified: 5 Juli 2000}

unit APIClipboard;

interface

uses
  Windows, Classes;

procedure StringToClipboard(const S: string);
function ClipboardAsString: string;
procedure CopyDataToClipboard(fmt: DWORD; const data; datasize: Integer;
  emptyClipboardFirst: Boolean = true);
procedure CopyDataFromClipboard(fmt: DWORD; S: TStream);
function ClipboardHasFormat(fmt: DWORD): Boolean;

implementation

uses
  Sysutils;

type
  {This is an internal exception class used by the unit=APIClipboard }
  EclipboardError = class(Exception)
  public
    constructor Create(const msg: string);
  end;

resourcestring
  eSystemOutOfMemory = 'could not allocate memory for clipboard data.';
  eLockfailed = 'could not lock global memory handle.';
  eSetDataFailed = 'could not copy data block to clipboard.';
  eCannotOpenClipboard = 'could not open the clipboard.';
  eErrorTemplate = 'APIClipboard: %s'#13#10 + 'System error code: %d'#13#10
    + 'System error message: %s';

  {EClipboardError.Create - Creates a new EclipboardError object

  Param msg is the string to embed into the error message
  Precondition: none
  Postcondition: none

  Description:
  Composes an error message that contains the passed message and the API error code
  and matching error message. The CreateFmt constructor inherited from the basic Exception
  class is used to do the work.

  Created 5.7.2000 by P. Below}

constructor EClipboardError.Create(const msg: string);
begin
  CreateFmt(eErrorTemplate, [msg, GetLastError, SysErrorMessage(GetLastError)]);
end;

{DataToClipboard - Copies a block of memory to the clipboard in a given format

Param fmt is the clipboard format to use
Param data is an untyped const parameter that addresses the data to copy
Param datasize is the size of the data, in bytes

Precondition:
The clipboard is already open. If not an EClipboardError will result. This precondition cannot be asserted, unfortunately.

Postcondition:
Any previously exisiting data of this format will have been replaced by the new data, unless datasize was 0 or we run into an exception. In this case the clipboard will be unchanged.

Description:
Uses API methods to allocate and lock a global memory block of approproate size,
copies the data to it and submits the block to the clipboard. Any error on the way will raise an EClipboardError exception.

Created 5.7.2000 by P. Below}

procedure DataToClipboard(fmt: DWORD; const data; datasize: Integer);
var
  hMem: THandle;
  pMem: Pointer;
begin
  if datasize <= 0 then
    Exit;
  hMem := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, datasize);
  if hmem = 0 then
    raise EclipboardError.Create(eSystemOutOfMemory);
  pMem := GlobalLock(hMem);
  if pMem = nil then
  begin
    GlobalFree(hMem);
    raise EclipboardError.Create(eLockFailed);
  end;
  Move(data, pMem^, datasize);
  GlobalUnlock(hMem);
  if SetClipboardData(fmt, hMem) = 0 then
    raise EClipboarderror(eSetDataFailed);
  {Note: API docs are unclear as to whether the memory block has to be freed in case of
  failure. Since failure is unlikely here lets blithly ignore this issue for now.}
end;

{DataFromClipboard - Copies data from the clipboard into a stream

Param fmt is the clipboard format to look for
Param S is the stream to copy to
precondition:  S <> nil
postcondition: If data was copied the streams position will have moved

Description:
Tries to get a memory block for the requested clipboard format. Nothing further is done if this
fails (because the format is not available or the clipboard is not open, we treat neither as error
here), otherwise the memory handle is locked and the data copied into the stream. Note that
we cannot determine the actual size of the data originally copied to the clipboard, only the
allocated size of the memory block! Since GlobalAlloc works with a granularity of 32 bytes the
block may be larger than required for the data and thus the stream may contain some spurious
bytes at the end. There is no guarantee that these bytes will be 0. If the memory handle
obtained from the clipboard cannot be locked we raise an (see class=EClipboardError) exception.

Created 5.7.2000 by P. Below}

procedure DataFromClipboard(fmt: DWORD; S: TStream);
var
  hMem: THandle;
  pMem: Pointer;
  datasize: DWORD;
begin
  Assert(Assigned(S));
  hMem := GetClipboardData(fmt);
  if hMem <> 0 then
  begin
    datasize := GlobalSize(hMem);
    if datasize > 0 then
    begin
      pMem := GlobalLock(hMem);
      if pMem = nil then
        raise EclipboardError.Create(eLockFailed);
      try
        S.WriteBuffer(pMem^, datasize);
      finally
        GlobalUnlock(hMem);
      end;
    end;
  end;
end;

{CopyDataToClipboard - Copies a block of memory to the clipboard in a given format

Param fmt is the clipboard format to use
Param data is an untyped const parameter that addresses the data to copy
Param datasize is the size of the data, in bytes
Param emptyClipboardFirst determines if the clipboard should be emptied, true by default

Precondition:
The clipboard must not be open already

Postcondition:
If emptyClipboardFirst is true all prior data will be cleared from the clipboard, even if
datasize is <= 0. The clipboard is closed again.

Description:
Tries to open the clipboard, empties it if required and then tries to copy the passed data to
the clipboard. This operation is a NOP if datasize <= 0. If the clipboard cannot be opened a (see
class=EClipboardError) is raised.

Created 5.7.2000 by P. Below}

procedure CopyDataToClipboard(fmt: DWORD; const data; datasize: Integer;
  emptyClipboardFirst: Boolean = true);
begin
  if OpenClipboard(0) then
  try
    if emptyClipboardFirst then
      EmptyClipboard;
    DataToClipboard(fmt, data, datasize);
  finally
    CloseClipboard;
  end
  else
    raise EclipboardError.Create(eCannotOpenClipboard);
end;

{StringToClipboard - Copies a string to clipboard in CF_TEXT clipboard format

Param S is the string to copy, it may be empty.

Precondition:
The clipboard must not be open already.

Postcondition:
Any prior clipboard content will be cleared, but only if S was not empty. The clipboard is closed again.

Description:
Hands the brunt of the work off to (See routine=CopyDataToClipboard), but only if S was
not empty. Otherwise nothing is done at all.

Created 5.7.2000 by P. Below}

procedure StringToClipboard(const S: string);
begin
  if Length(S) > 0 then
    CopyDataToClipboard(CF_TEXT, S[1], Length(S) + 1);
end;

{CopyDataFromClipboard - Copies data from the clipboard into a stream

Param fmt is the clipboard format to look for
Param S is the stream to copy to

Precondition:
S <> nil
The clipboard must not be open already.

Postcondition:
If data was copied the streams position will have moved. The clipboard is closed again.

Description:
Tries to open the clipboard, and then tries to copy the data to the passed stream. This
operation is a NOP if the clipboard does not contain data in the requested format. If the
clipboard cannot be opened a (see class=EClipboardError) is raised.

Created 5.7.2000 by P. Below}

procedure CopyDataFromClipboard(fmt: DWORD; S: TStream);
begin
  Assert(Assigned(S));
  if OpenClipboard(0) then
  try
    DataFromClipboard(fmt, S);
  finally
    CloseClipboard;
  end
  else
    raise EclipboardError.Create(eCannotOpenClipboard);
end;

{ClipboardAsString - Returns any text contained on the clipboard. Returns the clipboards
content if it contained something in CF_TEXT format, or an empty string.

Precondition: The clipboard must not be already open
Postcondition: The clipboard is closed.

Description:
If the clipboard contains data in CF_TEXT format it is copied to a temp memory stream,
zero-terminated for good measure and copied into the result string.

Created 5.7.2000 by P. Below}

function ClipboardAsString: string;
const
  nullchar: Char = #0;
var
  ms: TMemoryStream;
begin
  if not IsClipboardFormatAvailable(CF_TEXT) then
    Result := EmptyStr
  else
  begin
    ms := TMemoryStream.Create;
    try
      CopyDataFromClipboard(CF_TEXT, ms);
      ms.Seek(0, soFromEnd);
      ms.WriteBuffer(nullChar, Sizeof(nullchar));
      Result := Pchar(ms.Memory);
    finally
      ms.Free;
    end;
  end;
end;

{ClipboardHasFormat - Checks if the clipboard contains data in the specified format

Param fmt is the format to check for. Returns true if the clipboard contains data in this format, false otherwise

Precondition:  none
Postcondition: none

Description:
This is a simple wrapper around an API function.

Created 5.7.2000 by P. Below}

function ClipboardHasFormat(fmt: DWORD): Boolean;
begin
  Result := IsClipboardFormatAvailable(fmt);
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése