我正在使用以下测试代码将对象添加到 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 中的析构函数尚未运行,例如断点从未命中:
注意:如果我现在关闭测试应用程序,那么我将看到
Release
对我的对象的 GlobalInterfaceTable 调用,将其释放。但那是在关机期间,现在我还在记忆中。
但是,如果我从 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 异步回调期间向 GIT添加一个虚拟测试对象,而是添加一个实际的 ADO 接口。但是当这不起作用时,我们将失败的代码精简到最基本的部分。
tl;博士:我尝试将一个对象添加到全局接口表,但一旦我把它放在那里,它就会被破坏。
奖金喋喋不休
我想也许我必须AddRef
在将对象放入 GIT 之前手动调用,但是 GIT注册方法会调用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;
我TCounter.Destroy
的被击中了。