А я во вспомогательном
канале вывода в Excel зашел с другого конца: при создании структуры ADO.Recordset я указываю опциональный признак того, надо ли в соотв. колонке избавиться от "незначащих" значений, и после вывода просто делают замену средствами самого Excel таких значений на то, что возвращает COMVariant::createNoValue()
X++:
// очистка ячеек с "пустыми" значениями (ноль для чисел либо 01.01.1900 для дат), чтоб не надо было докручивать шаблон для их сокрытия
// _cell - ячейка, на которой в outputReportBody() вызывается метод CopyFromRecordset()
protected void clearEmptyCells(COM _cell)
{
COMVariant cvSrcValue;
COMVariant cvDstValue;
str strAddr;
container conFieldInfo;
boolean bClearEmpty;
Counter cnRows;
Counter n;
COM oRng; // область, в которой будет производиться замена
;
cnRows = rstAxa.RecordCount(); // количество выведенных строк данных
cvDstValue = COMVariant::createNoValue(); // создаем пустое значение
for (n = 1; n <= arrFields.lastIndex(); n++)
{
conFieldInfo = arrFields.value( n );
if (conlen(conFieldInfo) >= #ConPosFieldInfoClearEmpty) // по умолчанию этого поля в контейнеере быть не должно
{
bClearEmpty = conpeek( conFieldInfo, #ConPosFieldInfoClearEmpty );
if (!bClearEmpty)
continue;
cvSrcValue = this.getEmptyVariantValue( conpeek( conFieldInfo, #ConPosFieldInfoType ) );
if (!cvSrcValue) // если вернулся null, значит, тип не поддерживается
continue;
// формируем адрес диапазона ячеек *относительно* _cell
strAddr = ComExceldocument_RU::numToNameCell( n, 1 );
if (cnRows > 1)
strAddr += @':' + ComExceldocument_RU::numToNameCell( n, cnRows );
oRng = _cell.range( strAddr ); // получаем столбец внутри диапазона _cell
oRng.Replace( cvSrcValue, cvDstValue, #xlWhole, #xlByColumns );
}
}
}
// возвращает COMVariant, представляющий "пустое" значение для указанного типа ADO, либо null, если указанный тип не поддерживается
protected COMVariant getEmptyVariantValue( Integer _adoType )
{
COMVariant ret;
;
switch( _adoType )
{
case #adDate :
case #adDBDate :
case #adDBTimeStamp :
ret = COMVariant::createFromDateAndTime( datenull(), 0 );
break;
case #adSingle :
case #adDouble :
case #adCurrency :
case #adDecimal :
case #adNumeric :
ret = COMVariant::createFromReal( 0.0 );
break;
case #adTinyInt :
case #adSmallInt :
case #adInteger :
case #adBigInt :
case #adUnsignedTinyInt :
case #adUnsignedSmallInt :
case #adUnsignedInt :
case #adUnsignedBigInt :
ret = COMVariant::createFromInt( 0 );
break;
default :
ret = null;
break;
}
return ret;
}
Хотя, конечно, просто не устанавливать значение ячейки в Recordset, пожалуй, как-то "прямее"

PS. По ходу реализации наткнулся на одни "грабли": оказалось, что COMVariant::createFromDate( datenull() ) возвращает COMVariant не со значением 01.01.1900 00:00, как можно было бы ожидать, а со значением 01.01.1900 <текущее_время>!