1

Previously after calling VirtualStringGrid -> CopyToClipBoard, I could paste the grid as tabbed text in notepad or as a fully formatted grid (headers and colour and borders) when pasting into Excel or Outlook.

However, I'm experiencing issues with CopyToClipboard since I moved from Embarcadero XE8 to RAD Seattle with VirtualTreeView V6.2 : I only can paste as text if the target application is some kind of text editor. Pasting to any kind of 'rich' application that would accept RTF or html results in error.

I've tried to call the ContentToXXX methods (see code below) text is exported fine. Html is exported, but the resulting Data2Export string contains the whole code on an html page and cannot be pasted to Outlook, for instance. Any call to ContentToRTF results in a crash.

I googled for this kind of issue but I did not find anything quite relevant.

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();
}

Any idea of how to solve or workaround this issue?

Something wrong with the code?

PD: the dev platform are Win8 and Win10 and the VirtualStringTree ClipboardFormats are all set to true.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219

1 Answers1

1

You are calling GlobalFree() after each call to SetClipboardData(). YOU MUST NOT DO THAT unless SetClipboardData() fails. The documentation is very clear on that matter:

SetClipboardData function

If SetClipboardData succeeds, the system owns the object identified by the hMem parameter. The application may not write to or free the data once ownership has been transferred to the system, but it can lock and read from the data until the CloseClipboard function is called. (The memory must be unlocked before the Clipboard is closed.) If the hMem parameter identifies a memory object, the object must have been allocated using the function with the GMEM_MOVEABLE flag.

Also, you are saving your Text and RTF data blocks to the clipboard using the same CF_TEXT format. Your RTF data should be using the CF_RTF format instead.

Try this:

#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();
    }
}

If you look at source code for VirtualTreeView's CopyToClipboard() method, it uses a very different implementation than the code above. It retrieves the tree data into an IDataObject COM object (TVTDataObject) representing the clipboard formats listed in the VirtualTreeView's ClipboardFormats property, and any additional formats provided by the VirtualTreeView's OnGetUserClipboardFormats event. That includes Text, HTML, RTF, and CSV. It then calls OleSetClipboard() to put that COM object on the clipboard. If any application uses GetClipboardData() instead of OleGetClipboard(), Windows automatically extracts the data as needed. So maybe the implementation of TVTDataObject has been broken in v6.2. You should contact JAM Software (the current maintainers of VirtualTreeView) and file a bug report about that.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Hi Remy, I had missed the SetClipboardData trick. However the calling just `VST->ContentToRTF( TVSTTextSourceType::tstAll );` results in an access violation. I'll report the CopyToClipboard issue to Jam Software. Thanks for your help! – Michel WB - DivByZero Oct 27 '15 at 09:18
  • FYI here is the bug report and fix announced in version 6.2.1: [link](https://github.com/Virtual-TreeView/Virtual-TreeView/issues/588) – Michel WB - DivByZero Nov 02 '15 at 08:37