Как отключить набор записей ADO в XE6?

Я пытаюсь использовать отключенный набор записей ADO в XE6. Идея состоит в том, что вы обычно открываете набор записей, а затем устанавливаете ActiveConnection набора записей на эквивалент вашего языка null/Nothing/nil:

rs.Set_ActiveConnection(ноль);

Следующий пример из Delphi 5 работает нормально:

var rs: _Recordset;

rs := CoRecordset.Create;
rs.CursorLocation := adUseClient; //the default for a Recordset is adUseServer (Connection.Execute's default is adUseClient)
rs.CursorType := adOpenForwardOnly; //the default
rs.Open(CommandText, Conn,
      adOpenForwardOnly, //CursorType
      adLockReadOnly, //LockType
      adCmdText);

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil);

Работает в Delphi 5

Проблема в том, что я не могу заставить его работать в Delphi XE6. В Delphi 5 я бы успешно позвонил:

rs.Set_ActiveConnection(nil);

и все работало великолепно. Это сработало, потому что _Recordset интерфейс был объявлен как:

procedure Set_ActiveConnection(const pvar: IDispatch); safecall;

Так что было действительно, чтобы пройти nil; и это сработало.

В XE6 делкарация изменилась на:

procedure Set_ActiveConnection(pvar: OleVariant); safecall;

К которому вы не можете перейти nil, Тогда возникает вопрос, что является OleVariant эквивалент nil?

Попробуйте №1

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil); //E2010 Incompatible types: 'OleVariant' and 'Pointer'

Попробуйте №2

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Null);

вызывает исключение:

Аргументы имеют неправильный тип, находятся за пределами допустимого диапазона или конфликтуют друг с другом

Попробуйте № 3

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(EmptyParam);

вызывает исключение:

Аргументы имеют неправильный тип, находятся за пределами допустимого диапазона или конфликтуют друг с другом

Попробуйте №4

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Unassigned);

вызывает исключение:

Аргументы имеют неправильный тип, находятся за пределами допустимого диапазона или конфликтуют друг с другом

Попробуйте № 5

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(nil)); //E2089 Invalid typecast

Попробуйте №6

//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(Null));

вызывает исключение:

Аргументы имеют неправильный тип, находятся за пределами допустимого диапазона или конфликтуют друг с другом

Попробуйте №7

Мне ясно, что Codebarcadero ошибся в декларации. Это действительно должно быть IDispatch, Это означает, что мне нужно обмануть компилятор, передав ему OleVariant, расположенный по адресу 0x00000000 (то есть ноль). Таким образом, ADO увидит ценность 0x00000000 в стеке, и знаю, что я имею в виду null:

rs.Set_ActiveConnection(POleVariant(nil)^); //access violation before call

Я уверен, что Bo..Imp... Co..Embarcadero имеет намеренный способ назвать это; Я просто не могу этого понять.

Сборка Delphi 5

Дефи 5 делает правильную вещь; это толкает $00 (т.е. nil) в стек:

rs.Set_ActiveConnection(nil); push $0 ;push nil mov eax,[ebp-$08] ;get address of rs push eax ;push "this" mov eax,[eax] ;get VMT of IRecordset call dword ptr [eax+$28] ;call offset $28 of VMT

Принимая во внимание, что Delphi XE6 героически пытается сделать что-то, что я не знаю, что:

rs.Set_ActiveConnection(nil); lea eax,[ebp-$000000d8] call Null lea edx,[ebp-$000000d8] lea eax,[ebp-$000000c8] call @OleVarFromVar push dword ptr [ebp-$000000bc] push dword ptr [ebp-$000000c0] push dword ptr [ebp-$000000c4] push dword ptr [ebp-$000000c8] mov eax,[ebp-$04] push eax mov eax,[eax] call dword ptr [eax+$2c]

Бонус Чтение

1 ответ

Решение

В D7 (без D5 на руках) AdoInt.Pas содержит два варианта Set_ActiveConnection, например

  Recordset15 = interface(_ADO)
    ['{0000050E-0000-0010-8000-00AA006D2EA4}']
    procedure Set_ActiveConnection(const pvar: IDispatch); safecall;
    procedure _Set_ActiveConnection(pvar: OleVariant); safecall;

и в Delphi XE6:

Recordset15 = interface(_ADO)
   ['{0000050E-0000-0010-8000-00AA006D2EA4}']
   //...
   procedure _Set_ActiveConnection(const pvar: IDispatch); safecall;
   procedure Set_ActiveConnection(pvar: OleVariant); safecall;

Так что попробуйте другую версию в XE6. Лично я бы попробовал

Set_ActiveConnection(IDispatch(Nil)) 

во-первых, но вы говорите в комментариях, что _Set_ActiveConnection работает для вас.

Причина, по которой я сначала попробовал бы Set_ActiveConnection(IDispatch(Nil)) для интерфейса, который требует передачи OleVariant, заключается в следующем: с тех пор как интерфейсы были добавлены в Delphi (в D3?), Iirc в версии после варианта- была добавлена ​​автоматизация на основе OLE (D2), компилятор знал, как генерировать код для преобразования в обоих направлениях между интерфейсом OleVariant и IDispatch. Таким образом, "проблема" заключается в том, как передать интерфейс IDispatch в качестве аргумента OleVariant. Этот бит, на мой простой взгляд, прост, просто напишите IDispatch(), где аргумент должен быть OleVariant, и оставьте компилятор, чтобы разобраться в генерируемом коде. И если значение, которое мы хотим передать как интерфейс IDisaptch, на самом деле равно Nil, нам просто нужно написать

SomeInterfaceMemberExpectingAnOleVariant(IDispatch(Nil))
Другие вопросы по тегам