2015-07-23 1 views
2

У меня есть метод, который считывает данные в ячейках строки TStringGrid и копирует их в буфер обмена. И у меня есть соответствующий метод для вставки данных из буфера обмена в пустую строку в TStringGrid.Копирование/вставка с помощью TMemoryStream и TClipboard в D2009 +

Эти методы были написаны для D7, но были повреждены после миграции на XE2.

procedure TfrmBaseRamEditor.CopyLine(Sender: TObject; StrGridTemp: TStringGrid; 
            Row, Column: Integer); 
var 
    Stream: TMemoryStream; 
    MemHandle: THandle; 
    MemBlock: Pointer; 
    i, Len: Integer; 
    RowStr: String; 
begin 
    Stream := nil; 
    try 
    Stream := TMemoryStream.Create; 

    // The intermediate format to write to the stream. 
    // Separate each item by horizontal tab character. 
    RowStr := ''; 
    for i := 0 to (StrGridTemp.ColCount - 1) do 
     RowStr := RowStr + StrGridTemp.Cells[i, Row] + #9; 
    // Write all elements in a string. 
    Len := Length(RowStr); 
    Stream.Write(Len, SizeOf(Len)); 
    Stream.Write(PChar(RowStr)^, Length(RowStr)); 
    // Request Memory for the clipboard. 
    MemHandle := GlobalAlloc(GMEM_DDESHARE, Stream.SIZE); 
    MemBlock := GlobalLock(MemHandle); 
    try 
     // Copy the contents of the stream into memory. 
     Stream.Seek(0, soFromBeginning); 
     Stream.Read(MemBlock^, Stream.SIZE); 
    finally 
     GlobalUnlock(MemHandle); 
    end; 
    // Pass the memory to the clipboard in the correct format. 
    Clipboard.Open; 
    Clipboard.SetAsHandle(TClipboardFormat, MemHandle); 
    Clipboard.Close; 
    finally 
    Stream.Free; 
    end; 
end; 

procedure TfrmBaseRamEditor.PasteLine(Sender: TObject; StrGridTemp: TStringGrid; 
             Row, Column: Integer); 
var 
    Stream: TMemoryStream; 
    MemHandle: THandle; 
    MemBlock: Pointer; 
    ASize, Len, i: Integer; 
    TempStr: String; 
begin 
    Clipboard.Open; 
    try 
    // If something is in the clipboard in the correct format. 
    if Clipboard.HasFormat(TClipboardFormat) then 
    begin 
     MemHandle := Clipboard.GetAsHandle(TClipboardFormat); 
     if MemHandle <> 0 then 
     begin 
     // Detect size (number of bytes). 
     ASize := GlobalSize(MemHandle); 
     Stream := nil; 
     try 
      Stream := TMemoryStream.Create; 
      // Lock the contents of the clipboard. 
      MemBlock := GlobalLock(MemHandle); 
      try 
      // Copy the data into the stream. 
      Stream.Write(MemBlock^, ASize); 
      finally 
      GlobalUnlock(MemHandle); 
      end; 
      Stream.Seek(0, soFromBeginning); 
      Stream.Read(Len, SizeOf(Len)); 
      SetLength(TempStr, Len); 
      Stream.Read(PChar(TempStr)^, Stream.SIZE); 
      for i := 0 to StrGridTemp.RowCount do 
      StrGridTemp.Cells[i, Row] := NextStr(TempStr, #9); 
     finally 
      Stream.Free; 
     end; 
     end; 
    end; 
    finally 
    Clipboard.Close; 
    end; 
end; 

Проблема проявляется, когда я копирую строку с некоторыми значениями, а затем вставляю ее в пустую строку. Первая ячейка вставлена ​​правильно, но вторая ячейка содержит символы мусора (и ничто не вставлено в третьем столбце вперед). Я знаю, почему ничего не вставлено в третьем столбце вперед, потому что символ «горизонтальной вкладки», который разделяет столбцы, повреждается вместе с содержимым ячейки.

Я просмотрел «Delphi and Unicode» Марко Канту, но не смог выяснить, где все происходит неправильно.

ответ

3

Char является псевдонимом для WideChar. Таким образом, в CopyLine

Stream.Write(PChar(RowStr)^, Length(RowStr)); 

только пишет половину строки. Он должен быть

Stream.Write(PChar(RowStr)^, Length(RowStr)*SizeOf(Char)); 

В PasteLine я нахожу эту строку нечетное:

Stream.Read(PChar(TempStr)^, Stream.SIZE); 

Поскольку вы уже потребляли часть строки, которую вы пытаетесь читать после конца. Я бы написал так:

Stream.Read(PChar(TempStr)^, Len*SizeOf(Char)); 

Обратите внимание, что если вы используете один и тот же пользовательский идентификатор формата буфера обмена в качестве ANSI программы, то вы будете иметь кодирования несовпадений при копировании из одного и вставить в другой. Возможно, вам будет полезно зарегистрироваться в другом формате буфера обмена для нового формата Unicode.


Некоторые другие комментарии:

Stream := nil; 
try 
    Stream := TMemoryStream.Create; 
    ... 
finally 
    Stream.Free; 
end; 

должны быть записаны в виде:

Stream := TMemoryStream.Create; 
try 
    ... 
finally 
    Stream.Free; 
end; 

Если конструктор вызывает исключение, то try блок не будет введен.

Вам не нужно выписывать длину строки. Вы можете полагаться на размер потока при чтении, чтобы знать, как долго строка.

В CopyLine, буфер обмена Open и Close звонки должны быть защищены блоком try/finally.

+0

Благодарим вас за подробный ответ! Это поставило проблему, и теперь я понимаю код намного лучше. Фантастический – DBedrenko

+0

Я понимаю, что блок finally вокруг 'Clipboard.Open()' должен иметь 'Clipboard.Close()', но что должно быть в блоке 'finally' строки' Clipboard.Close() ', опубликованной в оригинале код? – DBedrenko

+0

Должен ли я обернуть все 3 строки буфера обмена в одном блоке, чья 'finally' является' Clipboard.Free() '? – DBedrenko

Смежные вопросы