如何在 XE6 中断开 ADO 记录集?

How to disconnect an ADO Recordset in XE6?

我正在尝试在 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。那么问题就变成了,nilOleVariant 是什么?

尝试#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);

导致异常:

Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another

尝试#3

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

导致异常:

Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another

尝试#4

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

导致异常:

Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another

尝试#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));

导致异常:

Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another

试试 #7

我很清楚 Codebarcadero 的声明有误。它确实应该是 IDispatch。这意味着我需要诱使编译器传递位于地址 0x00000000(即 nil)的 OleVariant。这样 ADO 就会在堆栈上看到值 0x00000000,并且知道我的意思是 null:

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

我确定 Bo..Imp...Co..Embarcadero 有预期的调用方式;我就是想不通。

Delphi5集

Dephi 5 做了正确的事情;它将 $00(即 nil)压入堆栈:

<strong>rs.Set_ActiveConnection(无);</strong> 推 $0 ;推零 mov eax,[ebp-$08] ;获取rs的地址 推 eax ;推 "this" mov eax,[eax] ;获取IRecordset的VMT call dword ptr [eax+$28] ;调用VMT的偏移量$28

而 Delphi XE6 正在通过英勇的努力去做一些我不知道的事情:

<strong>rs.Set_ActiveConnection(无);</strong> lea eax,[ebp-$000000d8] 调用 Null lea edx,[ebp-$000000d8] lea eax,[ebp-$000000c8] 致电@OleVarFromVar push dword ptr [ebp-$000000bc] 推 dword ptr [ebp-$000000c0] 推 dword ptr [ebp-$000000c4] 推 dword ptr [ebp-$000000c8] mov eax,[ebp-$04] 推 eax 移动 eax,[eax] 调用 dword ptr [eax+$2c]

红利阅读

在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 适合您。

对于需要传递 OleVariant 的接口,我首先尝试 Set_ActiveConnection(IDispatch(Nil)) 的原因是:自从接口被添加到 Delphi (在 D3 中?),iirc 在添加了基于变量的 OLE 自动化之后的版本中 (D2),编译器已经知道如何生成代码以在 OleVariant 和 IDispatch 接口之间进行双向转换。所以 "problem" 是如何将 IDispatch 接口作为 OleVariant 参数传递的。以我头脑简单的方式来看,这很简单,只需在参数应该是 OleVariant 的地方编写 IDispatch(),然后让编译器整理要生成的代码。而如果我们要作为IDisaptch接口传递的value实际上是Nil,我们只需要写

SomeInterfaceMemberExpectingAnOleVariant(IDispatch(Nil))