I'm building a multi-threaded windows service application in Delphi XE2 which uses ADO database components to connect to SQL Server. I've used CoInitialize(nil);
plenty times before inside threads, but in this case, I have a function which I'm unsure about.
This function is called TryConnect
which attempts to connect to a database with a given connection string. It returns true or false on the connection success. The problem is that this function will be used both inside and outside the main service thread, and it will be creating its own temporary TADOConnection
component, which requires CoInitialize
...
My question is do I need to call CoInitialize
inside this function also? If I do, and since the service's execute procedure uses CoInitialize
also, will they interfere if I call this function from within the service? The TryConnect
function is inside of an object which is created from the main service thread (but will eventually be moved to its own thread). I need to know if calling CoInitialize()
twice from the same thread (and CoUninitialize
) will interfere - and how to handle this scenario properly.
Here's the code below...
//This is the service app's execute procedure
procedure TJDRMSvr.ServiceExecute(Sender: TService);
begin
try
CoInitialize(nil);
Startup;
try
while not Terminated do begin
DoSomeWork;
ServiceThread.ProcessRequests(False);
end;
finally
Cleanup;
CoUninitialize;
end;
except
on e: exception do begin
PostLog('EXCEPTION in Execute: '+e.Message);
end;
end;
end;
//TryConnect might be called from same service thread and another thread
function TDBPool.TryConnect(const AConnStr: String): Bool;
var
DB: TADOConnection; //Do I need CoInitialize in this function?
begin
Result:= False;
DB:= TADOConnection.Create(nil);
try
DB.LoginPrompt:= False;
DB.ConnectionString:= AConnStr;
try
DB.Connected:= True;
Result:= True;
except
on e: exception do begin
end;
end;
DB.Connected:= False;
finally
DB.Free;
end;
end;
So to clarify what it's really doing, I might have an occasion of this:
CoInitialize(nil);
try
CoInitialize(nil);
try
//Do some ADO work
finally
CoUninitialize;
end;
finally
CoUninitialize;
end;
CoInitialize
has to be called in every single thread that uses COM, regardless of what thread it is, or whether it has a parent thread or child threads. If the thread uses COM, it must call CoInitialize
.
The correct answer here is "it depends". Since you know the service thread has called CoInitialize
, if TryConnect
is called from the service thread it won't need to be called again. If the other threads that could call it have also called CoInitialize
, it won't need to be called, as the function will run under the calling thread.
The MSDN documentation specifically addresses this question (emphasis added):
Typically, the COM library is initialized on a thread only once. Subsequent calls to CoInitialize or CoInitializeEx on the same thread will succeed, as long as they do not attempt to change the concurrency model, but will return S_FALSE. To close the COM library gracefully, each successful call to CoInitialize or CoInitializeEx, including those that return S_FALSE, must be balanced by a corresponding call to CoUninitialize. However, the first thread in the application that calls CoInitialize with 0 (or CoInitializeEx with COINIT_APARTMENTTHREADED) must be the last thread to call CoUninitialize. Otherwise, subsequent calls to CoInitialize on the STA will fail and the application will not work.
So the answer is: If you're not sure, call CoInitialize
. Do it in a try..finally
block, and call CoUnitialize
in the finally
, or initialize in a constructor and uninitialize in the destructor.