2015-11-30 28 views
0

Я работаю над приложением, которое недавно было обновлено с Delphi 2007 до XE7. Существует один конкретный сценарий, когда преобразование TMemoryStream в PChar не выполняется. Вот код:StrAlloc не работает после перехода на Delphi XE7

procedure TCReport.CopyToClipboard; 
var 
    CTextStream: TMemoryStream; 
    PValue: PChar; 
begin 
    CTextStream := TMemoryStream.Create; 
    //Assume that this code is saving a report column to CTextStream 
    //Verified that the value in CTextStream is correct 
    Self.SaveToTextStream(CTextStream); 

    //The value stored in PValue below is corrupt 
    PValue := StrAlloc(CTextStream.Size + 1); 
    CTextStream.Read(PValue^, CTextStream.Size + 1); 
    PValue[CTextStream.Size] := #0; 

    { Copy text stream to clipboard } 
    Clipboard.Clear; 
    Clipboard.SetTextBuf(PValue); 

    CTextStream.Free; 

    StrDispose(PValue); 
end; 

Добавление кода для SaveToTextStream:

procedure TCReport.SaveToTextStream(CTextStream: TStream); 
var 
    CBinaryMemoryStream: TMemoryStream; 
    CWriter: TWriter; 
begin 

    CBinaryMemoryStream := TMemoryStream.Create; 
    CWriter := TWriter.Create(CBinaryMemoryStream, 24); 

    try 
    CWriter.Ancestor := nil; 
    CWriter.WriteRootComponent(Self); 
    CWriter.Free; 

    CBinaryMemoryStream.Position := 0; 

    { Convert Binary 'WriteComponent' stream to text} 

    ObjectBinaryToText(CBinaryMemoryStream, CTextStream); 
    CTextStream.Position := 0; 
    finally 
    CBinaryMemoryStream.Free; 
    end; 
end; 

Я заметил, что StrLen (PChar) также выходит, что половина размера TMemoryStream. Но в Delphi 2007 он вышел так же, как и размер TMemoryStream.

Я знаю, что приведенный выше код предполагает, что размер символа равен 1 байту, и это может быть проблемой. Но я попробовал несколько подходов, и ничего не работает.

Не могли бы вы предложить лучший способ сделать это преобразование?

+1

В какой кодировке хранится данные 'SaveToTextStream'? Правильный способ исправления кода зависит от используемой кодировки. Три реалистичные возможности: 1) та же кодировка, что и «UnicodeString» (UTF-16LE) 2) UTF-8 3) та же кодировка, что и «AnsiString» (которые зависят от активной кодовой страницы Windows). – hvd

+0

Почему это было отклонено? – jetty

ответ

0

По ВЕ, что hvd и David Heffernan писал выше, один из возможных способов является изменение CTextStream на CopyToClipboard к TStringStream следующим образом:

procedure TCReport.CopyToClipboard; 
var 
    CTextStream: TStringStream; 
begin 
    CTextStream := TStringStream.Create; 
    try 
     //Assume no error with Self.SaveToTextStream 
     Self.SaveToTextStream(CTextStream); 

     { Copy text stream to clipboard } 
     Clipboard.AsText := CTextStream.DataString; 
    finally 
     CTextStream.Free; 
    end; 
end; 

Но вы должны убедиться что SaveToTextStream функция обеспечивает CTextStream с точным кодирование текстовых данных.

+0

Это работает! SaveToTextStream принимает TStream в качестве входного параметра. Только то, что мне было нужно. Благодарю. – jetty

+0

@jetty Приветствую вас. – theodorusap

+0

Это, кажется, довольно плохой совет для меня. Как написано, код заставляет использовать ANSI. Конечно, этого следует избегать. @jetty, вы действительно обнимаете переход на Unicode? Как реализуется «SaveToTextStream»? Текстовые представления 'TCReport' содержат текстовое представление? –

8

Еще раз, это проблема Delphi 2009 и более поздних версий с использованием текста Unicode. В Delphi 2007 и ранее:

  • Char является псевдонимом AnsiChar.
  • PChar является псевдонимом PAnsiChar.
  • string является псевдонимом AnsiString.

В Delphi 2009 и более поздних версий:

  • Char является псевдонимом WideChar.
  • PChar является псевдонимом PWideChar.
  • string является псевдонимом UnicodeString.

Ваш код написан при условии, что PChar является PAnsiChar. Отсюда ваши проблемы. В любом случае вам необходимо прекратить использование StrAlloc. Вы делаете жизнь тяжело для себя, вручную выделяя кучу памяти здесь. Пусть компилятор выполнит эту работу.

Вы должны получить текст в строковой переменной, а затем просто сделать:

Clipboard.AsText := MyStrVariable; 

Точно, как лучше всего, чтобы получить строку, зависит от объектов, которые TCReport предложения. Я ожидаю, что он даст строку непосредственно в этом случае вы будете писать что-то вроде этого:

procedure TCReport.CopyToClipboard; 
begin 
    Clipboard.AsText := Self.ReportAsText; 
end; 

Я предполагаю, как к чему вашему функциональности ваших TCReport предложений, но я уверен, что вы знаете.

+0

+1. Эта. Люди, которые утверждают, что переносят в unicode delphi, не зная, что они делают, сделают хэш всего, что они делают. Ручное выделение (с помощью StrAlloc или GetMem) является запахом кода, если это не необходимо. Конец кода, который был глупым даже в эпоху Delphi 7, является признаком едва-компетентного разработчика. –

+0

Привет @DavidHeffernan, не могли бы вы взглянуть на отредактированный код? Я упомянул код для SaveToTextStream. – jetty

+0

Какая кодировка использует 'ObjectBinaryToText'? –

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