Ранее после звонка 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: платформа dev — это Win8 и Win10, а для форматов буфера обмена VirtualStringTree задано значение true.
Ты звонишь GlobalFree()
после каждого звонка 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()
Метод, он использует реализацию, отличную от кода выше. Он извлекает данные дерева в IDataObject
COM-объект (TVTDataObject
) представляет форматы буфера обмена, перечисленные в VirtualTreeView ClipboardFormats
свойство и любые дополнительные форматы, предоставляемые VirtualTreeView OnGetUserClipboardFormats
событие. Это включает в себя текст, HTML, RTF и CSV. Затем звонит OleSetClipboard()
поместить этот COM-объект в буфер обмена. Если какое-либо приложение использует GetClipboardData()
вместо OleGetClipboard()
, Windows автоматически извлекает данные по мере необходимости. Так что, возможно, реализация TVTDataObject
был сломан в v6.2. Вы должны связаться JAM Software (текущие сопровождающие VirtualTreeView) и подать отчет об ошибке об этом.
Других решений пока нет …