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);
It works in Delphi 5
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
?
Try #1
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil); //E2010 Incompatible types: 'OleVariant' and 'Pointer'
Try #2
//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
Try #3
//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
Try #4
//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
Try #5
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(nil)); //E2089 Invalid typecast
Try #6
//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
Try #7
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.
Delphi 5 assembly
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]