i figured out the problem; it's likely an (undocumented) fundamental limitation of IGlobalInterfaceTable
that makes it essentially unusable:
Any object added to the GlobalInterfaceTable must be retrieved before the adding apartment is torn down.
Running the following from a separate thread:
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;
As soon as the apartment associated with the separate thread is uninitialized: any objects still in the GlobalInterfaceTable are flushed.
This makes it impossible the post messages containing GIT cookie values between threads.
Even artificially inflating the reference count, to prevent object destruction won't help you:
procedure TAddToGitThread.Execute;
var
unk: IUnknown;
cookie: DWORD;
git: IGlobalInterfaceTable;
begin
CoInitialize(nil);
try
unk := TCounter.Create;
unk._AddRef; //inflate reference count to prevent destruction on apartment teardown
git := CoGlobalInterfaceTable.Create;
OleCheck(git.RegisterInterfaceInGlobal(unk, IUnknown, {out}cookie));
unk := nil;
finally
CoUninitialize; <--object added from this apartment is Released, but not freed
end;
end;
Even though the object was not destroyed (because we inflated the reference count), it will no longer be in the global interface table. Asking for it will just fail:
hr := _git.GetInterfaceFromGlobal(_cookie, IUnknown, {out}unk);
returns
0x80070057 The parameter is incorrect
You wanted to use that object added to the GIT? You had should have grabbed it while you didn't have the chance!
i hate bugs where i do nothing wrong, but it still fails.