Delphi 在ADO异步回调期间无法将对象添加到GlobalInterfaceTable
我正在使用以下测试代码将对象添加到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
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方法上。”你应该用任何相关的代码来支持你的答案。