【问题标题】:How to disconnect an ADO Recordset in XE6?如何在 XE6 中断开 ADO 记录集?
【发布时间】: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。这意味着我需要欺骗编译器传递一个位于地址 0x00000000OleVariant(即 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


【解决方案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)) 

首先,但您在 cmets 中说 _Set_ActiveConnection 适合您。

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

SomeInterfaceMemberExpectingAnOleVariant(IDispatch(Nil))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2012-05-27
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多