【发布时间】:2013-07-02 15:09:57
【问题描述】:
我正在使用以下测试代码向 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;
一旦回调返回,我放置在全局接口表中的对象就被销毁,到达TInterfacedObject 中的断点。
实际上,我不会在 ADO 异步回调 i would be adding an actual ADO interface 期间向 GIT 添加虚拟 test 对象。但是当这不起作用时,我们会将失败的代码精简到最简单的部分。
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 进行演示的原因。为了确认它确实是被销毁的 "my" 对象,我将问题中的引用从 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;
我的TCounter.Destroy 被击中了。
【问题讨论】:
-
相信您需要持有对 GIT 的引用。我会使用这样的东西:stackoverflow.com/a/1071393/800214。顺便说一句,我对您的其他问题的评论仍然有效,传递数据(如 PODO 对象) - 更简单,尤其是在涉及线程时......
-
很好地介绍了问题。但是,它缺少一件事:表明您目睹在断点处被销毁的对象与您创建并放入接口表中的对象相同。
-
需要考虑的事项:线程模型。当您直接调用您的函数时,ADO 使用的线程是否与似乎工作的任何线程模型兼容?
-
@whosrdaddy 您不必持有对 GIT 接口的引用。但是更改了问题代码以保留对它的引用:只是为了避免混淆。
-
@RobKennedy 将
TInterfacedObject更改为测试对象TCounter。更新的问题。此外,IGlobalInterfaceTable的目的是在同一进程中的线程和线程模型之间移动对象;唯一的工作就是允许公寓之间的东西。并且没有任何线程不安全代码:CoCreateInstance和InterlockedIncrement/Decrement