1

之前调用 后VirtualStringGrid -> CopyToClipBoard,我可以将网格粘贴为记事本中的选项卡式文本,或者在粘贴到 Excel 或 Outlook 时作为完全格式化的网格(标题、颜色和边框)。

但是,自从我使用 VirtualTreeView V6.2 从 Embarcadero XE8 迁移到 RAD Seattle 后,我遇到了问题CopyToClipboard:如果目标应用程序是某种文本编辑器,我只能粘贴为文本。粘贴到任何可以接受 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 ClipboardFormats都设置为true。

4

1 回答 1

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.

于 2015-10-26T18:10:28.060 回答