Robert Meek wrote:
Yes I asked the wrong question!  What I meant to ask was as I
obviously have to place things back on the clipboard that are in a specially
registered format, how do I do that not knowing what the format is?

But you *do* know the format. You had to know the format in order to get the data off the clipboard in the first place. When you store the data to a file, also store the name of the format.


I
haven't found anyway to use the numeric constants for these special
types...it seems you need to know the CF_ constant type?  And there is no
listing I can find to use for converting from one to the other!  Also, how
can I save what is on the clipboard to a stream so it can be loaded into a
blob along with it's format type so I can then load it back into the
clipboard later?  There's nothing in the Clipbrd unit that I can use for
that.

If I wanted to save the contents of the clipboard in all available formats to a single file, this is the code I would use:


function GetFormatName(const Format: UInt): WideString;
var
  Len: Integer;
begin
  if Format < $c000 then begin
    Result := SysUtils.Format('#%u', [Format]);
  end else begin
    Len := 400;
    SetLength(Result, Len);
    Len := GetClipboardFormatNameW(Format, PWideChar(Result), Len);
    Win32Check(Len <> 0);
    SetLength(Result, Len);
  end;
end;

procedure SaveClipboardToFile(const FileName: WideString);
var
OutHandle: THandle;
OutFile: TStream;
Format: DWord;
FormatName: WideString;
NameLen: LongWord;
Data: THandle;
DataBuffer: Pointer;
DataSize: LongWord;
begin
Win32Check(OpenClipboard(0));
try
OutHandle := CreateFileW(PWideChar(FileName), Generic_Write, 0, nil, Create_Always, File_Attribute_Archive or File_Flag_Sequential_Scan, 0);
Win32Check(OutHandle <> Invalid_Handle_Value);
try
OutFile := THandleStream.Create(Integer(OutHandle));
try
Format := EnumClipboardFormats(0);
while Format <> 0 do try
FormatName := GetFormatName(Format);
NameLen := Length(FormatName);
if NameLen = 0 then continue;
OutFile.Write(NameLen, SizeOf(NameLen));
if NameLen > 0 then OutFile.Write(FormatName[1], NameLen * SizeOf(FormatName[1]));


          Data := GetClipboardData(Format);
          Win32Check(Data <> 0);

          DataBuffer := GlobalLock(Data);
          try
            DataSize := GlobalSize(Data);
            OutFile.Write(DataSize, SizeOf(DataSize));
            OutFile.Write(DataBuffer^, DataSize);
          finally
            GlobalUnlock(Data);
          end;
        finally
          Format := EnumClipboardFormats(Format);
        end;
        Assert(Format = 0);
        if GetLastError <> Error_Success then RaiseLastOSError;
        // Use 0 to designate end of data
        OutFile.Write(Format, SizeOf(Format));
      finally
        OutFile.Free;
      end;
    finally
      CloseHandle(OutHandle);
    end;
  finally
    CloseClipboard;
  end;
end;

It's naive. It doesn't account for private clipboard formats, and it also doesn't make any effort to detect and skip synthesized clipboard formats.

The procedure saves the clipboard data in a list. First it writes the name of the format, and then it writes the data. Both the name and the data get prefixed by their lengths first so that they can be read back correctly later. Below is a procedure that should be able to read the data and put it back on the clipboard.

function GetFormatNumber(const Name: WideString): UInt;
begin
  if Name[1] = '#' then begin
    Result := StrToInt(Copy(Name, 2, MaxInt));
  end else begin
    Result := RegisterClipboardFormatW(PWideChar(Name));
  end;
end;

procedure LoadClipboardFromFile(const FileName: WideString);
var
InHandle: THandle;
InFile: TStream;
Format: DWord;
FormatName: WideString;
NameLen: LongWord;
Data: THandle;
DataBuffer: Pointer;
DataSize: LongWord;
begin
Win32Check(OpenClipboard(0));
try
Win32Check(EmptyClipboard);
InHandle := CreateFileW(PWideChar(FileName), Generic_Read, 0, nil, Open_Existing, File_Attribute_Archive or File_Flag_Sequential_Scan, 0);
Win32Check(InHandle <> Invalid_Handle_Value);
try
InFile := THandleStream.Create(Integer(InHandle));
try
InFile.ReadBuffer(NameLen, SizeOf(NameLen));
while NameLen <> 0 do begin
SetLength(FormatName, NameLen);
InFile.ReadBuffer(FormatName[1], NameLen * SizeOf(FormatName[1]));


          Format := GetFormatNumber(FormatName);

          InFile.ReadBuffer(DataSize, SizeOf(DataSize));
          Data := GlobalAlloc(GMem_Moveable or GMem_DDEShare, DataSize);
          Win32Check(Data <> 0);
          try
            DataBuffer := GlobalLock(Data);
            Win32Check(Assigned(DataBuffer));
            try
              InFile.ReadBuffer(DataBuffer^, DataSize);
              SetClipboardData(Format, Data);
            finally
              GlobalUnlock(Data);
            end;
          except
            GlobalFree(Data);
            raise;
          end;

          InFile.ReadBuffer(NameLen, SizeOf(NameLen));
        end;
      finally
        InFile.Free;
      end;
    finally
      CloseHandle(InHandle);
    end;
  finally
    CloseClipboard;
  end;
end;

I use a special format for clipboard format names because the documentat for GetClipboardFormatName suggests that the built-in formats don't have names.

--
Rob

_______________________________________________
Delphi mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi

Reply via email to