Проблема с TVirtualStringGrid CopyToClipboard Embarcadero Seattle C++

Раньше после вызова VirtualStringGrid -> CopyToClipBoard я мог вставить сетку как текст с вкладками в блокноте или как полностью отформатированную сетку (заголовки, цвет и границы) при вставке в Excel или Outlook.

Однако у меня возникли проблемы с CopyToClipboard с тех пор, как я перешел с Embarcadero XE8 на RAD Seattle с VirtualTreeView V6.2: я могу вставлять текст только в том случае, если целевое приложение является текстовым редактором. Вставка в любое «богатое» приложение, которое может принимать RTF или html, приводит к ошибке.

Я пытался вызвать методы ContentToXXX (см. код ниже), текст экспортируется нормально. Html экспортируется, но результирующая строка Data2Export содержит весь код на странице html и не может быть вставлена, например, в Outlook. Любой вызов ContentToRTF приводит к сбою.

Я гуглил для такого рода проблемы, но я не нашел ничего подходящего.

void __fastcall TForm::ExportGrid( void )
{
// old code that used to work fine
//  VST->CopyToClipboard();

  Virtualtrees::TVSTTextSourceType exportSrcType = tstAll;

  OpenClipboard( Handle );
  EmptyClipboard();

  std::string Data2Export = "";
  HGLOBAL hg;

  // tabbed text
  Data2Export = AnsiString( VST->ContentToText( exportSrcType, "\t" ) ).c_str();
  hg = GlobalAlloc( GMEM_MOVEABLE, Data2Export.size() + 1 );

  if ( !hg )
  {
    CloseClipboard();
    return;
  }

  memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.size() + 1 );
  GlobalUnlock( hg );
  SetClipboardData( CF_TEXT, hg );
  GlobalFree( hg );

  // html
  Data2Export = AnsiString( VST->ContentToHTML( exportSrcType ) ).c_str();
  hg = GlobalAlloc( GMEM_MOVEABLE, Data2Export.size() + 1 );

  if ( !hg )
  {
    CloseClipboard();
    return;
  }

  memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.size() + 1 );
  GlobalUnlock( hg );
  SetClipboardData( CF_HTML, hg );
  GlobalFree( hg );

  // RTF
  Data2Export = AnsiString( VST->ContentToRTF( exportSrcType ).c_str() ).c_str();
  hg      = GlobalAlloc( GMEM_MOVEABLE, Data2Export.size() + 1 );

  if ( !hg )
  {
    CloseClipboard();
    return;
  }

  memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.size() + 1 );
  GlobalUnlock( hg );
  SetClipboardData( CF_TEXT, hg );
  GlobalFree( hg );

  CloseClipboard();
}

Любая идея о том, как решить или обойти эту проблему?

Что-то не так с кодом?

PD: платформа разработки — Win8 и Win10, а для всех форматов буфера обмена VirtualStringTree установлено значение true.


person Michel WB - DivByZero    schedule 26.10.2015    source источник


Ответы (1)


Вы звоните GlobalFree() после каждого звонка SetClipboardData(). ВЫ НЕ ДОЛЖНЫ ДЕЛАТЬ ЭТО, если SetClipboardData() не терпит неудачу. Документация очень ясна по этому поводу:

функция SetClipboardData

Если SetClipboardData завершается успешно, система владеет объектом, идентифицированным параметром hMem. Приложение не может записывать или освобождать данные после передачи прав собственности в систему, но оно может блокировать данные и считывать их до тех пор, пока не будет вызвана функция CloseClipboard. (Память должна быть разблокирована перед закрытием буфера обмена.) Если параметр hMem идентифицирует объект памяти, этот объект должен быть выделен с помощью функции с флагом GMEM_MOVEABLE.

Кроме того, вы сохраняете блоки данных Text и RTF в буфер обмена, используя тот же формат CF_TEXT. Вместо этого ваши данные RTF должны использовать формат CF_RTF.

Попробуй это:

#include <richedit.h>

void __fastcall TForm::ExportGrid( void )
{
    // old code that used to work fine
    //  VST->CopyToClipboard();

    Virtualtrees::TVSTTextSourceType exportSrcType = tstAll;

    if ( !OpenClipboard( Handle ) ) return;
    try
    {
        EmptyClipboard();

        AnsiString Data2Export;
        HGLOBAL hg;

        // tabbed text
        Data2Export = VST->ContentToText( exportSrcType, "\t" );
        hg = GlobalAlloc( GMEM_MOVEABLE, Data2Export.size() + 1 );
        if ( hg )
        {
            memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.Length() + 1 );
            GlobalUnlock( hg );
            if ( !SetClipboardData( CF_TEXT, hg ) ) // or maybe CF_CSV instead...
                GlobalFree( hg );
        }

        // html
        Data2Export = VST->ContentToHTML( exportSrcType );
        hg = GlobalAlloc( GMEM_MOVEABLE, Data2Export.Length() + 1 );
        if ( hg )
        {
            memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.Length() + 1 );
            GlobalUnlock( hg );

            if ( !SetClipboardData( CF_HTML, hg ) )
                GlobalFree( hg );
        }

        // RTF
        Data2Export = VST->ContentToRTF( exportSrcType );
        hg = GlobalAlloc( GMEM_MOVEABLE, Data2Export.Length() + 1 );
        if ( hg )
        {
            memcpy( GlobalLock( hg ), Data2Export.c_str(), Data2Export.Length() + 1 );
            GlobalUnlock( hg );

            if ( !SetClipboardData( CF_VRTF, hg ) )
                GlobalFree( hg );

        }
    }
    __finally
    {
        CloseClipboard();
    }
}

Если вы посмотрите на исходный код метода VirtualTreeView CopyToClipboard(), он использует совсем другую реализацию, чем приведенный выше код. Он извлекает данные дерева в COM-объект IDataObject (TVTDataObject), представляющий форматы буфера обмена, перечисленные в свойстве ClipboardFormats VirtualTreeView, и любые дополнительные форматы, предоставляемые событием OnGetUserClipboardFormats VirtualTreeView. Это включает в себя текст, HTML, RTF и CSV. Затем он вызывает OleSetClipboard(), чтобы поместить этот COM-объект в буфер обмена. Если какое-либо приложение использует GetClipboardData() вместо OleGetClipboard(), Windows автоматически извлекает данные по мере необходимости. Так что, возможно, реализация TVTDataObject была нарушена в версии 6.2. Вам следует связаться с JAM Software (текущие специалисты по сопровождению VirtualTreeView) и отправить отчет об ошибке.

person Remy Lebeau    schedule 26.10.2015
comment
Привет, Реми, я пропустил трюк SetClipboardData. Однако вызов только VST->ContentToRTF( TVSTTextSourceType::tstAll ); приводит к нарушению прав доступа. Я сообщу о проблеме с CopyToClipboard в Jam Software. Спасибо за вашу помощь! - person Michel WB - DivByZero; 27.10.2015
comment
К вашему сведению, вот отчет об ошибке и исправление, объявленное в версии 6.2.1: ссылка - person Michel WB - DivByZero; 02.11.2015