Как отключить набор записей 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))