Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 在ADO异步回调期间无法将对象添加到GlobalInterfaceTable_Delphi_Com_Ado_Delphi 5 - Fatal编程技术网

Delphi 在ADO异步回调期间无法将对象添加到GlobalInterfaceTable

Delphi 在ADO异步回调期间无法将对象添加到GlobalInterfaceTable,delphi,com,ado,delphi-5,Delphi,Com,Ado,Delphi 5,我正在使用以下测试代码将对象添加到GlobalInterfaceTable: function TForm1.AddSomethingToGit(): DWORD; var unk: IUnknown; cookie: DWORD; git: IGlobalInterfaceTable; begin unk := TCounter.Create; if FGit = nil then begin git := CoGlobalIn

我正在使用以下测试代码将对象添加到GlobalInterfaceTable:

function TForm1.AddSomethingToGit(): DWORD;
var
    unk: IUnknown;
    cookie: DWORD;
    git: IGlobalInterfaceTable;
begin
    unk := TCounter.Create;

    if FGit = nil then
    begin
        git := CoGlobalInterfaceTable.Create;
        Fgit := git; //yes, i didn't use InterlockedCompareExchange. Can you imagine trying to explain that syntax to people?
    end;

    OleCheck(Fgit.RegisterInterfaceInGlobal(unk, IUnknown, {out}cookie));

    Result := cookie;
end;
我从按钮处理程序调用测试代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
   AddSomethingToGit();
end;
一切都很好。它所在的对象位于全局接口表中,等待提取。我知道它仍然在那里,因为TInterfacedObject中的析构函数尚未运行,例如断点从未命中:

注意:如果我现在关闭测试应用程序,那么我将在我的对象上看到GlobalInterfaceTable调用
Release
,将其释放。但那是在关机期间,现在我还在记忆中

但如果我从ADO回调调用相同的测试函数:

conn := CreateTrustedSqlServerConnection(serverName, defaultDatabaseName);
dataSet := TADODataSet.Create(nil);
dataSet.Connection := conn;
dataSet.OnFetchComplete := FetchComplete;
dataSet.CursorLocation := clUseClient;
dataSet.CommandText := 'WAITFOR DELAY ''00:00:03''; SELECT GETDATE() AS foo';
dataSet.CommandType := cmdText;
dataSet.ExecuteOptions := [eoAsyncFetch];
dataSet.Open();
procedure TForm1.FetchComplete(DataSet: TCustomADODataSet;
    const Error: Error; var EventStatus: TEventStatus);
begin
   AddSomethingToGit();
end;
使用回调:

conn := CreateTrustedSqlServerConnection(serverName, defaultDatabaseName);
dataSet := TADODataSet.Create(nil);
dataSet.Connection := conn;
dataSet.OnFetchComplete := FetchComplete;
dataSet.CursorLocation := clUseClient;
dataSet.CommandText := 'WAITFOR DELAY ''00:00:03''; SELECT GETDATE() AS foo';
dataSet.CommandType := cmdText;
dataSet.ExecuteOptions := [eoAsyncFetch];
dataSet.Open();
procedure TForm1.FetchComplete(DataSet: TCustomADODataSet;
    const Error: Error; var EventStatus: TEventStatus);
begin
   AddSomethingToGit();
end;
我放在全局接口表中的对象在回调返回时立即被销毁,命中
TInterfacedObject
中的断点

实际上,在ADO异步回调期间,我不会向GIT添加虚拟测试对象。但当这不起作用时,我们会将失败的代码精简到最底层

tl;dr:我试图将对象添加到全局接口表中,但一旦将其放入其中,它就会被销毁

花言巧语 我认为在将对象放入GIT之前,可能必须手动调用
AddRef
,但是GIT register方法调用
AddRef
本身

如何构造
IGlobalInterfaceTable

class function CoGlobalInterfaceTable.Create: IGlobalInterfaceTable;
begin
    // There is a single instance of the global interface table per process, so all calls to this function in a process return the same instance.
    OleCheck(CoCreateInstance(CLSID_StdGlobalInterfaceTable, nil, CLSCTX_INPROC_SERVER, IGlobalInterfaceTable, Result));
end;
使用(不是我的)Delphi翻译的界面:

  IGlobalInterfaceTable = interface(IUnknown)
     ['{00000146-0000-0000-C000-000000000046}']
     function RegisterInterfaceInGlobal(pUnk: IUnknown; const riid: TIID; out dwCookie: DWORD): HRESULT; stdcall;
     function RevokeInterfaceFromGlobal(dwCookie: DWORD): HRESULT; stdcall;
     function GetInterfaceFromGlobal(dwCookie: DWORD; const riid: TIID; out ppv): HRESULT; stdcall;
   end;
为了完整性:

const
  CLSID_StdGlobalInterfaceTable : TGUID = '{00000323-0000-0000-C000-000000000046}';
更新一 我拼命想避免添加我自己的对象,因为担心有人会认为我的对象搞糟了。这就是我最初使用Delphi的内置
TInterfacedObject
进行演示的原因。为了确认被销毁的确实是“我的”对象,我将问题中的引用从
TInterfacedObject
更改为
TCounter

TCounter = class(TInterfacedObject, IUnknown)
private
   FFingerprint: string;
public
   constructor Create;
   destructor Destroy; override;
end;

{ TCounter }

constructor TCounter.Create;
begin
   inherited Create;
   FFingerprint := 'Rob Kennedy';
end;

destructor TCounter.Destroy;
begin
   if FFingerprint = 'Rob Kennedy' then
      Beep;
   inherited;
end;

我的
t计数器.Destroy被击中。

我解决了问题;这可能是一个(未记录的)基本限制,使其基本上无法使用:

必须先检索添加到GlobalInterfaceTable的任何对象,然后才能拆下添加的单元

从单独的线程运行以下操作:

procedure TAddToGitThread.Execute;
var
   unk: IUnknown;
   cookie: DWORD;
   git: IGlobalInterfaceTable;
begin
    CoInitialize(nil);
    try
        unk := TCounter.Create;

        git := CoGlobalInterfaceTable.Create;
        OleCheck(git.RegisterInterfaceInGlobal(unk, IUnknown, {out}cookie));
        unk := nil;
    finally
        CoUninitialize; <--objects added from this apartment Released
    end;
end;
返回

0x80070057参数不正确

您想使用添加到GIT的对象吗?你应该在没有机会的时候抓住它


我讨厌我没有做错的bug,但它仍然失败。

我知道它很旧,但你可以简单地在另一个线程中安排一个匿名方法(该线程将在应用程序的出口关闭),在该线程中创建对象并将其存储到GIT中。这样,这个特殊线程的单元将由您管理,对象将住在该单元中


然而,从我的测试来看,在git中,您也可以从一个单元注册在其他单元中创建的对象。但部分文件的表述有所不同。我还在做这件事。

我相信你需要保留对GIT的引用。我会用这样的东西:。顺便说一句,我对你的另一个问题的评论仍然有效,传递数据(比如PODO对象)——更简单,特别是当涉及线程时……很好地说明了问题。不过,它缺少一点:表明在断点处被销毁的对象与您创建并放入接口表的对象相同。需要考虑的是:线程模型。当您直接调用函数时,ADO使用的线程是否与任何线程模型兼容?@whosrdaddy您不必持有对GIT接口的引用。但更改了问题代码以保留对它的引用:只是为了避免混淆。@RobKennedy将
TInterfacedObject
更改为测试对象
TCounter
。更新问题。另外,
IGlobalInterfaceTable
的目的是在同一进程中在线程和线程模型之间移动对象;唯一的工作就是允许公寓之间的东西。而且没有任何线程不安全的代码:
CoCreateInstance
InterlocatedIncrement/Decrement
看起来像:“调用[RegisterInterfaceInGlobal]的单元必须保持活动状态,直到相应的调用RevokeInterfaceFromGlobal。”很抱歉,您被它烧坏了,尽管如此。@RobKennedy我发誓在发现这个限制后我读了三遍那个文档,然后你就找到了。“他妈的在哪里?哦,在add方法上。”你应该用任何相关的代码来支持你的答案。