Wednesday, June 20, 2007

Delphi Tips And Tricks: CoInitialize/CoUninitialize Part II

In Part I of CoInitialize/Uninitialize of Delphi Tips and Tricks I talked about encountering problems with CoUninitialize and how the Delphi compiler generates code to IUnknown._Release after a function block. In this part I want to discuss another common problem having to do with making multiple calls to CoInitialize and CoUninitialize.

Here are the rules to calling CoIninitialize and CoUninitialize:


  • Each thread must initialize the COM library by calling CoInitialize
  • Each call to CoInitialize with a return value of S_OK and S_FALSE must be paired with a call to CoUninitialize
  • Each thread can only be initialized with one threading model (remember GUI code is appartment threaded)
  • The first thread that calls CoInitialize must be the last thread to call CoUninitialize.
  • Do not call CoInitialize in DllMain


If you do not follow these five basic rules your application will not function properly. You can find these the documentation with these five basic rules on MSDN here. There is more information but the simple list above is much easier to read.

Below is a simple example that demonstrates correct usage and handling of the return value from CoInitialize and CoInitializeEx in a single threaded application. If there is any interested I will expand this to a multithreaded application?


program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils, ActiveX, Windows;

procedure CoInitStatus(Success: HResult);
begin
case Success of
S_OK, S_FALSE: WriteLn('success');
RPC_E_CHANGED_MODE, E_INVALIDARG,
E_OUTOFMEMORY, E_UNEXPECTED: WriteLn('failed');
end;
end;

procedure CoUninit(Success: HResult);
begin
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;

var
Success1, Success2, Success3: HResult;
begin
Success1 := CoInitialize(nil);
CoInitStatus(Success1);

Success2 := CoInitialize(nil);
CoInitStatus(Success2);

Success3 := CoInitializeEx(nil, COINIT_MULTITHREADED);
CoInitStatus(Success3);

CoUninit(Success3);
CoUninit(Success2);
CoUninit(Success1);
end.


The output from running this program should be:

success
success
failed

3 comments:

Anonymous said...

Could you please look at QC37344 and see if it can be opened?

http://qc.codegear.com/wc/qcmain.aspx?d=37344

If CoInitializeEx fails with RPC_E_CHANGED_MODE and you call CoUnInitialize without checking the result, you'll have an unbalanced call to CoUninitialize, which will probably shut down COM too early.

Chris Bensen said...

If the call to CoInitializeEx in the constructor of TCustomComboBoxEx is the first call to initialize the COM library then you might be able to work around the problem by calling to CoInitialize and CoUninitialize before the call to the constructor. But I'll check into it.

Steffen Friismose said...

Well, I have recently created a project that relies on COM in a multi threaded environment. Basically just using the TXMLDocument with the MSXML parser.
So, naturally, I would be interested in seeing how you propose writing the code for a thread.

Post a Comment