Показать сообщение отдельно
Старый 07.11.2008, 13:21   #24  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
? э-хе-хе...
Испытываю возобновление интереса к теме и вот в связи с чем. ADODB.Recordset имеет полезнейшие свойства Filter и Sort, которыми можно эффективно пользоваться, фильтруя исходный набор или изменяя порядок записей исходного Recordset'а и далее перебирая в цикле нужные записи в нужном порядке.

Резонно было бы предположить, хотя про это нигде не сказано в справке, что методом CopyFromRecordset должен выгружаться набор записей с учетом фильтрации и сортировки. Увы, при управлении Excel из Аксапты этого не происходит:

X++:
static void Job103(Args _args)
{
    #CCADO

    ComExcelDocument_RU doc = new ComExcelDocument_RU();
    COM                 rst = new COM('ADODB.Recordset');

    COM     xlApp;
    COM     wbook;
    COM     activeSheet;
    COM     range;
    COM     flds,fld;

    int     i;
    ;

    doc.NewFile('',false);
    wbook = doc.getComDocument();

    xlApp = wbook.Parent();
    activeSheet = xlApp.ActiveSheet();

    flds = rst.Fields();

    flds.Append('myField1', #adVarChar, 50);
    flds.Append('myField2', #adInteger);
    
    rst.Open();

    for (i=1;i<=1000;i++)
    {
        rst.AddNew();
            fld = flds.Item(0); fld.Value(int2str(i));
            fld = flds.Item(1); fld.Value(i*2);
        rst.Update();
    }

    rst.Filter("myField1 Like '*24*'");
    rst.Sort('myField1 DESC');

    rst.MoveFirst();

    range = activeSheet.Range('A1');
    range.CopyFromRecordset(rst, 300);

    range = activeSheet.Range('A1000');
    range.CopyFromRecordset(rst, 300);

    doc.visible(true);

    
    // чтобы показать, что набор действительно фильтруется и сортируется
    rst.MoveFirst();
    while (! rst.EOF())
    {
        info(strFmt('%1 --- %2', 
                    new CCADOField( flds.Item(0) ).value(),
                    new CCADOField( flds.Item(1) ).value()));
        rst.MoveNext();
    }
}
Поведение метода CopyFromRecordset в этом примере не соответствует ожиданиям. Он не учитывает не только сортировку и фильтрацию, но и декларированное в справке продвижение указателя текущей записи. Так, по здравой логике, первый оператор "range.CopyFromRecordset(rst, 300);" должен вывести в Excel первые 300 записей и установить указатель на 301-ю. Второй же оператор "range.CopyFromRecordset(rst, 300);" должен вывести следующие 300 записей: с 301-й по 600-ю и остановиться на 601-й.

Но этого не происходит. Вместо этого - и начиная с ячейки A1, и начиная с ячейки A1000 - одинаково выводятся первые 300 записей исходного набора. А хотелось бы, чтобы вывелись только 20 записей с ячейки A1, т.е. те записи, которые пример выводит в окно Infolog.

Возможно мои претензии необоснованы и CopyFromRecordset должен себя вести именно так, как ведет себя в этом примере? Я уже было засомневался, вернее, почти отказался от своих "претензий", но решился на еще одну проверку - в самом Excel на VBA:
Код:
 
Sub Job103_VBA()

    Dim i As Integer
    
    Dim rst As Object
    Dim flds As Object
    
    Dim xlApp As Excel.Application

    Set rst = CreateObject("ADODB.Recordset")
    'я сознательно создал rst через CreateObject, а не как New ADODB.Recordset, 
    'чтобы не искать ссылку на ADO через Tools\References    
    
    Set flds = rst.Fields
    
    flds.Append "myField1", 200, 50 '200 = adVarChar
    flds.Append "myField2", 3 '3 = adInteger
    
    rst.Open
    
    For i = 1 To 1000
        rst.AddNew
            rst.Fields(0).Value = i
            rst.Fields(1).Value = i * 2
        rst.Update
    Next i
    
    rst.Filter = "myField1 Like '*24*'"
    rst.Sort = "myField1 DESC"
    
    rst.MoveFirst
    
    'Set xlApp = New Excel.Application
    Set xlApp = Application
    
    xlApp.Workbooks.Add
      
    xlApp.Range("A1").CopyFromRecordset rst, 300
    xlApp.Range("A1000").CopyFromRecordset rst, 300
    
    xlApp.Visible = True
    
End Sub
Что скажете о результате? Он такой, как хотелось! Ура? Увы, нет. Закомментируйте строку "Set xlApp = Application" и раскомментируйте строку выше, т.е. должно стать вот так:
Код:
 
    Set xlApp = New Excel.Application
    'Set xlApp = Application
Таким образом, теперь мы из Excel создаем другой Excel и выводим рекордсет в этот другой. Результат такой же, как при выводе из Аксапты - не такой, как хочется.

Есть ли у кого какие-нибудь соображения? Может, кто-нибудь интересующийся продвинуто заглянет какими-нибудь крутыми вьюерами в тайны происходящих процессов COM-автоматизации? Может быть удастся победить ситуацию использованием каких-нибудь COMDispFunction?


P.S. Проделал даже такой финт. Создал шаблон xlt, в который зашил VBA-процедуру:
Код:
 
Sub myCopyFromRecordset(ByVal rng As Range, ByVal rst As ADODB.Recordset, ByVal maxRows As Integer)
    rng.CopyFromRecordset rst, maxRows
End Sub
Из Аксапты создал новый файл на базе этого шаблона и вызвал процедуру строкой:
X++:
    xlApp.Run('myCopyFromRecordset', range, rst, 300);
Результат - тоже не тот