i have an ADO Recordset
object that is created in a background thread:
var
conn: TADOConnection;
rs: _Recordset;
begin
conn := CreateDatabaseConnection();
rs := conn.Execute(CommandText, cmdText, []);
conn.Free;
//Give the recordset to the UI thread
//Don't forget to add a reference before we stuff it into a 32-bit variable
rs._AddRef();
PostMessage(hwndUIThreadWindow, WM_HeresTheRecordsetYouAskedFor, WPARAM(rs), 0);
end;
And then the Recordset
is handed to my "main" thread:
procedure ExecuteComplete(var msg: TMessage); message WM_HeresTheRecordsetYouAskedFor;
var
rs: _Recordset;
begin
rs := _Recordset(msg.wParam);
//don't forget to remove the manually added reference
rs._Release();
ShowMessage(rs.Fields['TheTimeIs'].Value);
end;
i also could have done:
var
global_Recordset: _Recordset;
var
conn: TADOConnection;
begin
conn := CreateDatabaseConnection();
global_Recordset := conn.Execute(CommandText, cmdText, []);
conn.Free;
end;
Either way, a thread that didn't create the COM object is now using it. From the main thread:
global_Recordset .Fields['TheTimeIs'].Value;
COM forbids accessing COM objects from apartments (in this case: threads) that did not create the object.
What is the correct way to marshal in in-process COM object interfaces across apartment boundaries?
The correct way to pass a COM object across apartments is to marshal the interface pointer, which can be done in one of two different ways:
Have the worker thread call the
CoMarshalInterThreadInterfaceInStream()
function and pass the resultingIStream
pointer to the UI thread, which then calls theCoGetInterfaceAndReleaseStream()
function.Use the
IGlobalInterfaceTable
interface. Have the worker thread create the interface and call itsRegisterInterfaceInGlobal()
method and pass the resulting cookie to the UI thread, which then creates the interface and calls itsGetInterfaceFromGlobal()
andRevokeInterfaceFromGlobal()
methods.