【发布时间】:2015-11-08 03:23:45
【问题描述】:
我正在尝试在 XE6 中使用断开连接的 ADO Recordset。这个想法是您正常打开记录集,然后将记录集的 ActiveConnection 设置为您的语言相当于 null/Nothing/nil:
rs.Set_ActiveConnection(null);
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。这意味着我需要欺骗编译器传递一个位于地址 0x00000000 的 OleVariant(即 nil)。这样 ADO 将在堆栈上看到值 0x00000000,并且知道我的意思是 null:
rs.Set_ActiveConnection(POleVariant(nil)^); //access violation before call
我确定 Bo..Imp...Co..Embarcadero 有这样的称呼;我就是想不通。
Delphi 5 汇编
Dephi 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]
阅读奖励
【问题讨论】:
-
@MartynA
varNull是一个声明为1的常量。它在变体记录中用于指示变体记录包含的内容。所以将ActiveConnection设置为1是没有意义的。但我尝试了它的乐趣:它失败了,同样的例外。 -
我没能正确记住的是“RecordSet.Set_ActiveConnection(IDispatch(Nil));” (至少这在 XE8 中编译,fwiw)
-
@MartynA:至少在 XE8 上有效。 (通过works,我的意思是不会引发异常。)
-
@KenWhite:确实。 D7era 中曾经有一个公文包模式的 ADO 演示。 Fwiw,D7 AdoInt.Pas 有“过程 Set_ActiveConnection(const pvar: IDispatch); safecall; AND 过程 _Set_ActiveConnection(pvar: OleVariant); safecall;”在 RecordSet15 界面中。
-
@MartynA:XE8 AdoInt 单元具有相同的两个声明,并且新导入的 ADO 类型库也会在 MSADO_TLB.pas 中生成相同的两个声明。使用
IDispatch(nil)调用任何一个似乎都是等效的 - 两者都不会引发异常。
标签: delphi ado delphi-xe6