I am trying to use a disconnected ADO Recordset in XE6. The idea is that you open the recordset normally, then you set the recordset's ActiveConnection to your language's equivalent of null
/Nothing
/nil
:
rs.Set_ActiveConnection(
null);
The following example from Delphi 5 works fine:
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);
The issue is that I cannot make it work in Delphi XE6. In Delphi 5 i would successfully call:
rs.Set_ActiveConnection(nil);
and everything worked splendidly. It worked because _Recordset
interface was declared as:
procedure Set_ActiveConnection(const pvar: IDispatch); safecall;
So it was valid to pass nil
; and it worked.
In XE6 the delcaration changed to:
procedure Set_ActiveConnection(pvar: OleVariant); safecall;
To which you cannot pass nil
. The question then becomes, what is the OleVariant
equivalent of nil
?
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil); //E2010 Incompatible types: 'OleVariant' and 'Pointer'
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Null);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(EmptyParam);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Unassigned);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(nil)); //E2089 Invalid typecast
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(Null));
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
It's clear to me that Codebarcadero got the declaration wrong. It really is supposed to be an IDispatch
. This means i need to trick the compiler into passing an OleVariant located at address 0x00000000
(i.e. nil). That way ADO will see the value 0x00000000
on the stack, and know i mean null
:
rs.Set_ActiveConnection(POleVariant(nil)^); //access violation before call
I'm sure Bo..Imp...Co..Embarcadero has the intended way to call this; i just cannot figure it out.
Dephi 5 does the correct thing; it pushes $00 (i.e. nil
) onto the stack:
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
Whereas Delphi XE6 is going through heroic efforts to do something i don't know what:
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]
In D7 (don't have D5 to hand), AdoInt.Pas contains two flavours of Set_ActiveConnection, e.g.
Recordset15 = interface(_ADO)
['{0000050E-0000-0010-8000-00AA006D2EA4}']
procedure Set_ActiveConnection(const pvar: IDispatch); safecall;
procedure _Set_ActiveConnection(pvar: OleVariant); safecall;
and in Delphi XE6:
Recordset15 = interface(_ADO)
['{0000050E-0000-0010-8000-00AA006D2EA4}']
//...
procedure _Set_ActiveConnection(const pvar: IDispatch); safecall;
procedure Set_ActiveConnection(pvar: OleVariant); safecall;
So try the other version in XE6. Personally, I'd have tried
Set_ActiveConnection(IDispatch(Nil))
first, but you say in comments that _Set_ActiveConnection works for you.
The reason I'd have tried Set_ActiveConnection(IDispatch(Nil)) first, for an interface which requires an OleVariant to be passed, is this: Ever since interfaces were added into Delphi (in D3?), iirc in the version after variant-based OLE automation was added (D2), the compiler has known how to generate code to convert in both directions between an OleVariant and an IDispatch interface. So the "problem" is how to pass an IDispatch interface as an OleVariant argument. That bit, to my simple-minded way of looking at it, is easy, just write IDispatch() where the argument is supposed to be an OleVariant, and leave the compiler to sort out the code to generate. And if the value we want to pass as the IDisaptch interface is actually Nil, we just need to write
SomeInterfaceMemberExpectingAnOleVariant(IDispatch(Nil))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With