Jump to content
maXcomX

Why does CoInitializeEx returns 1 or $80010106 ?

Recommended Posts

Every time I start up an app on OnFormCreate

 

CoInitializeEx(Nil,
                       COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

returns with 1 (S_FALSE) // The COM library is already initialized on this thread

 

Does creating a form automatically initialize COM?

 

and

 

OnFormCreate 

 

CoInitializeEx(nil,
                       COINIT_MULTITHREADED);

returns with -2147417850 ($80010106 or 0x80010106) // can't find the description of this return value

 

On Delphi 10.4,  Win 11 latest update.

 

What could be a possible issue?

 

 

 

 

Edited by maXcomX

Share this post


Link to post
12 minutes ago, maXcomX said:

Every time I start up an app on initialization

 

CoInitializeEx(Nil,
                       COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

returns with 1.

That is S_FALSE.

Quote

CoInitializeEx(nil,
                       COINIT_MULTITHREADED);

returns with -2147417850

That is RPC_E_CHANGED_MODE.

Quote

What could be a possible issue?

Per the documentation: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex

Quote

Return value

This function can return the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following values.

Return code Description
S_OK
The COM library was initialized successfully on this thread.
S_FALSE
The COM library is already initialized on this thread.
RPC_E_CHANGED_MODE
A previous call to CoInitializeEx specified the concurrency model for this thread as multithread apartment (MTA). This could also indicate that a change from neutral-threaded apartment to single-threaded apartment has occurred.

Remarks

CoInitializeEx must be called at least once, and is usually called only once, for each thread that uses the COM library. Multiple calls to CoInitializeEx by the same thread are allowed as long as they pass the same concurrency flag, but subsequent valid calls return S_FALSE. To close the COM library gracefully on a thread, each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize.

 

You need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions.

 

Otherwise, the COM function will return CO_E_NOTINITIALIZED.

 

After the concurrency model for a thread is set, it cannot be changed. A call to CoInitialize on an apartment that was previously initialized as multithreaded will fail and return RPC_E_CHANGED_MODE.

This means that you are calling CoInitializeEx() on a thread that has already called CoInitialize/Ex() successfully, where S_FALSE means you are trying to set the same concurrency model that has already been assigned to the thread, whereas RPC_E_CHANGED_MODE means you are specifying a concurrency model that is not compatible with the thread's current concurrency model.

 

For instance, are you calling CoInitializeEx() in the main UI thread?  The RTL's System.Win.ComObj unit initializes COM in the main UI thread during program startup, using the global CoInitFlags variable to decide whether to use CoInitialize() or CoInitializeEx().

Edited by Remy Lebeau

Share this post


Link to post

Oops, I edited the question for some more clarification.

Sorry Remy.

 

Edited by maXcomX

Share this post


Link to post

I did read the info from ms docs you mentioned, thank you.

 

But I can't understand what this have to do with starting a clean Delphi app calling OnCreateForm give this results while there is nothing initiated before.

 

For example:

 

Creating a new vcl app with just one form and declaring CoInitializeEx on OnFormCreate and CoUninitialize() on FormCloseQuery.

When debugging CoInitializeEx produces the results I mentioned at the start of this thread.

 

Edited by maXcomX
Clarifying

Share this post


Link to post

look at: VCL ->  System.Win.ComObj.pas, line 2600 (RAD 11.3)... and "finalization" section

Quote

    InitProc := @InitComObj;   ... -->  call "procedure InitComObj;"

procedure InitComObj;
begin
  if InitComObjCalled then Exit;
  if SaveInitProc <> nil then
    TProcedure(SaveInitProc);
  if (CoInitFlags <> -1) and Assigned(System.Win.ComObj.CoInitializeEx) then
  begin
    NeedToUninitialize := Succeeded(System.Win.ComObj.CoInitializeEx(nil, CoInitFlags));
    IsMultiThread := IsMultiThread or
      ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
      (CoInitFlags = COINIT_MULTITHREADED);  // this flag has value zero
  end
  else
    NeedToUninitialize := Succeeded(CoInitialize(nil));
  InitComObjCalled := True;
end;

bds_eeO41mbwIE.gif

Edited by programmerdelphi2k
  • Like 1

Share this post


Link to post

now try this "without any" other code in your form sample (just "empty project":

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(InitProc) then
    ShowMessage('InitProc assigned')
  else
    ShowMessage('InitProc NOT assigned');
end;

 

Share this post


Link to post

Thank you, but this is not an expected behaviour of this function, does it?

For example if you run this code in C++ all goes well.

Delphi issue?

 

 

Share this post


Link to post

Also, there is a big issue when trying to use WASAPI when CoInitializeEx fails. Methods like  ActivateAudioInterfaceAsync will fail too with unpredictable results.

Share this post


Link to post
4 minutes ago, programmerdelphi2k said:

I think that Remy it's more xperient in "deep MSWin"... and maybe can help better than me sorry if I cannot

Any way: Thanks a lot for your efforts to solve this issue!

Share this post


Link to post

I dont know almost nothing about COM usage, but if you check if "initproc" before any usage... it was assigned, dont help you?

if Assigned(InitProc) then

Share this post


Link to post
1 hour ago, Remy Lebeau said:

That is S_FALSE.

That is RPC_E_CHANGED_MODE.

Per the documentation: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex

This means that you are calling CoInitializeEx() on a thread that has already called CoInitialize/Ex() successfully, where S_FALSE means you are trying to set the same concurrency model that has already been assigned to the thread, whereas RPC_E_CHANGED_MODE means you are specifying a concurrency model that is not compatible with the thread's current concurrency model.

 

For instance, are you calling CoInitializeEx() in the main UI thread?  The RTL's System.Win.ComObj unit initializes COM in the main UI thread during program startup, using the global CoInitFlags variable to decide whether to use CoInitialize() or CoInitializeEx().

Thank you Remy, but I've been through all those things. That's why I ended here 😉

Share this post


Link to post
28 minutes ago, programmerdelphi2k said:

I dont know almost nothing about COM usage, but if you check if "initproc" before any usage... it was assigned, dont help you?


if Assigned(InitProc) then

It does what it's expected to do, but it will not explain why the function is resulting the results I mentioned. 

Share this post


Link to post

Per https://stackoverflow.com/a/20019023/65863:

Quote

In the RTL/VCL source, COM is initialized in the following ways:

  1. By a call to OleInitialize made from Forms.TApplication.Create. So this call will be made for all VCL forms applications, but not, for example, for service applications.
  2. By a call to CoInitialize or CoInitializeEx in ComObj.InitComObj. This is registered as an InitProc in the initialization section of the ComObj unit. In turn, the call to Application.Initialize in your project .dpr file's code will invoke ComObj.InitComObj.
  3. In many and various other locations around the RTL/VCL. Including, but not limited to, Datasnap, ComServ, Soap, System.Win.Sensors, Winapi.DirectShow9. Some of these areas of code are more recent than Delphi 7.

Now, of these various COM initializations, the ones that count are 1 and 2. In any standard VCL forms application, both of these will run at startup in the main thread. Item 1 runs first and so gets to initialize COM first. That's the initialization that counts. Item 2 runs after and returns S_FALSE meaning that COM was already initialized.

And also: https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-oleinitialize

Quote

OleInitialize calls CoInitializeEx internally to initialize the COM library on the current apartment. Because OLE operations are not thread-safe, OleInitialize specifies the concurrency model as single-thread apartment.

So, there you go.  If your project uses the ComObj unit, it will call CoInitialize/Ex() when Vcl.Forms.TApplication.Initialize() is called, which won't matter since the constructor of Vcl.Forms.TApplication will have already called OleInitialize() beforehand, which calls CoInitializeEx():

constructor TApplication.Create(AOwner: TComponent);
var
...
begin
  inherited Create(AOwner);
  if not IsLibrary then
    FNeedToUninitialize := Succeeded(OleInitialize(nil));
  ...
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
15 hours ago, maXcomX said:

Also, there is a big issue when trying to use WASAPI when CoInitializeEx fails. Methods like  ActivateAudioInterfaceAsync will fail too with unpredictable results.

Can you be more specific?

Share this post


Link to post

Yes I can.

I'm working on a new example that replaces the mmio methods, because they are or will be deprecated (like mmioOpen) And that make all other mmio related methods quite useless.

So far I managed to translate a CPP example to it's Delphi equivalent. However Microsoft made a lot of samples based on a DOSBox interface.

 

To be more specific, the DOSbox sample runs well on MS Visual Studio (returning the adequate result of CoInitializeEx() , but not within Delphi 10.4.

 

Here Delphi source including the CPP source:

 

 

 

 

 

 

LoopBackCapture_2.zip

 

The Delphi sample uses MFPack

Edited by maXcomX

Share this post


Link to post

I must have overlooked something.

The place to call ConitializeEx() should not be called in OnFormCreate, as Remy commented by posting this link.

The MS sample is a DOSbox sample, I forgot that when creating a form in Delphi ConitializeEx() has already being called.

Thank you Remy.

 

 

 

Share this post


Link to post

I must have overlooked something. 

When initializing an UI application, any earlier Conitialize() as default, should be set to ConitializeEx() when necessarily.

So, if Assigned(InitProc) is true, CoUnitialize() should be called to change the thread model and set to ConitializeEx() to make callbacks working.

Also the parameter COINIT_MULTITHREADED that only should be used by DOSbox and other windowless apps, should be COINIT_APARTMENTTHREADED for UI apps.

Am I correct?

 

 

 

Edited by maXcomX
Clarifying

Share this post


Link to post
1 hour ago, maXcomX said:

When initializing an UI application, any earlier Conitialize() as default, should be set to ConitializeEx() when necessarily.

Unfortunately, as I already pointed out earlier, the VCL's TApplication constructor calls OleInitialize() before the ComObj unit has a chance to call CoInitialize/Ex(), and OleInitialize() internally calls CoInitialize(nil), so if you require CoInitializeEx() with any mode other than COINIT_APARTMENTTHREADED then you will have to do your COM work in a separate worker thread instead of in the UI thread.

1 hour ago, maXcomX said:

So, if Assigned(InitProc) is true, CoUnitialize() should be called

The ComObj unit is not the only unit that uses InitProc.  InitProc is a chained list of initialization procedures, so you can't use Assigned(InitProc) alone as an indicator of whether you should call CoInitializeEx() or not.  If you need CoInitializeEx() called, then just call it yourself unconditionally, and just be sure to pay attention to whether it fails or not.  Returning S_FALSE is NOT a failure condition!

1 hour ago, maXcomX said:

to change the thread model and set to ConitializeEx() to make callbacks working.

A thread cannot change its COM threading mode once it has been set.  That is what the RPC_E_CHANGED_MODE error code tells you.  That IS a failure condition!  If you need to use a different COM threading model than the calling thread has already been set to, you will have to move your COM work to another thread that is able to be set to the desired COM model.

1 hour ago, maXcomX said:

Also the parameter COINIT_MULTITHREADED that only should be used by DOSbox and other windowless apps, should be COINIT_APARTMENTTHREADED for UI apps.

Am I correct?

Typically, but not necessarily always.  It really depends on each app's particular requirements.

  • Like 1

Share this post


Link to post

Ok, thank you again for this useful info.

I stumbled into this while writing a basic audio loopback application, where I experienced a strange behavior of function ActivateAudioInterfaceAsync.

My first thought was it had something todo with ConitializeEx().  But reading the documentation again, the interface should be implemented on an agile object (aggregating a free-threaded marshaler.

I think I need to implement the TAggregatedObject, but not sure if that is correct and how to implement this.

I tried this:

  TActivateAudioInterfaceCompletionHandler = class(TInterfacedObject, IActivateAudioInterfaceCompletionHandler)
  public
    function ActivateCompleted(activateOperation: IActivateAudioInterfaceAsyncOperation): HRESULT; stdcall;
  end;

  TLoopbackCapture = class(TAggregatedObject, IActivateAudioInterfaceCompletionHandler)

  // etc

  // constructor
constructor TLoopbackCapture.Create(oObj: IUnknown);
var
  hr: HResult;

begin
  inherited Create(IActivateAudioInterfaceCompletionHandler(oObj));
  // etc

// From the mainform creating tLoopBackCapture is called

 var
   OuterObj: TActivateAudioInterfaceCompletionHandler;
   oLoopbackCapture: TLoopbackCapture;


procedure TfrmMain.FormCreate(Sender: TObject);
begin
  OuterObj := TActivateAudioInterfaceCompletionHandler.Create();
  oLoopbackCapture := TLoopbackCapture.Create(IActivateAudioInterfaceCompletionHandler(OuterObj));
end;

But this approach doesn't work either.

When calling ActivateAudioInterfaceAsync, 

function TAggregatedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := IInterface(FController).QueryInterface(IID, Obj);  << FController = nil
end;

No Interface ($80004002), IID = {'94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90'}, well actually that's the identifier of the IAgile interface..

Followed by EAccessError.

 

So, I think implementing TAggregatedObject is not the way, but implementing IAgile should do it?

 

It's a real headbanger.. 😞

 

 

Edited by maXcomX
Typo

Share this post


Link to post
5 hours ago, maXcomX said:

But reading the documentation again, the interface should be implemented on an agile object (aggregating a free-threaded marshaler.

Aggregating the FTM means your COM object would need to call the CoCreateFreeThreadedMarshaler() function and save the IUnknown interface it gives you, and then have your object's QueryInterface() method return that interface when it is asked for IID_IMarshal.

 

However, nothing in the ActivateAudioInterfaceAsync() documentation mentions aggregating the FTM at all.  All it says is that your completionHandler object needs to implement the IAgileObject interface, nothing more.  IAgileObject is an indicator that lets the system know that the object is free-threaded and thus can be called across apartment boundaries without marshaling.

5 hours ago, maXcomX said:

I think I need to implement the TAggregatedObject, but not sure if that is correct and how to implement this.

You DO NOT need to implement TAggregatedObject in this situation.

5 hours ago, maXcomX said:

No Interface ($80004002), IID = {'94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90'}, well actually that's the identifier of the IAgile interface..

TAggregatedObject and IAgileObject are completely different and related things.

5 hours ago, maXcomX said:

Followed by EAccessError.

Right, because you did not hook up the TAggregatedObject.Controller to point at another COM object.  As you can see in the code snippet you posted, TAggregatedObject.QueryInterface() simply delegates to another COM object.

5 hours ago, maXcomX said:

So, I think implementing TAggregatedObject is not the way

Correct.

5 hours ago, maXcomX said:

but implementing IAgile should do it?

Yes. You would need something like this:

TActivateAudioInterfaceCompletionHandler = class(TInterfacedObject, IActivateAudioInterfaceCompletionHandler, IAgileObject)
public
  function ActivateCompleted(activateOperation: IActivateAudioInterfaceAsyncOperation): HRESULT; stdcall;
end;

function TActivateAudioInterfaceCompletionHandler.ActivateCompleted(activateOperation: IActivateAudioInterfaceAsyncOperation): HRESULT; stdcall;
begin
  // do something...
end;

...

ActivateAudioInterfaceAsync(
  ...,
  TActivateAudioInterfaceCompletionHandler.Create as IActivateAudioInterfaceCompletionHandler,
  ...
);

 

 

Share this post


Link to post

Thanks, Remy.

 

IAgileObject works as expected.

 

On 3/29/2023 at 2:40 AM, Remy Lebeau said:

However, nothing in the ActivateAudioInterfaceAsync() documentation mentions aggregating the FTM at all.  All it says is that your completionHandler object needs to implement the IAgileObject interface, nothing more.  IAgileObject is an indicator that lets the system know that the object is free-threaded and thus can be called across apartment boundaries without marshaling.

This text confused me:

E_ILLEGAL_METHOD_CALL
On versions of Windows previous to Windows 10, this error may result if the function is called from an incorrect COM apartment, or if the passed IActivateAudioInterfaceCompletionHandler is not implemented on an agile object (aggregating a free-threaded marshaler).

 

So on first thought, I would need an aggregated interface instead of an IAgileObject interface.

And because IAgileObject is not defined in any Delphi version, TAggregatedObject bubbled up..

 

Being confused has nothing to do with Confucius I would say...

 

For the audience I translated IAgileObject to Delphi (which should be declared in ObjIdl, part of Delphi ActiveX?)

  // Interface IAgileObject
  // ======================
  // The IAgileObject interface is a marker interface that indicates that an object
  // is free threaded and can be called from any apartment.
  // Unlike what happens when aggregating the Free Threaded Marshaler (FTM),
  // implementing the IAgileObject interface doesn't affect what happens when marshaling a call.
  // Instead, the IAgileObject interface is recognized by the Global Interface Table (GIT).
  // When an object that implements the IAgileObject interface is placed in the GIT and
  // localized to another apartment, the object is called directly in the new apartment, rather than marshaling.
  {$HPPEMIT 'DECLARE_DINTERFACE_TYPE(IAgileObject);'}
  {$EXTERNALSYM IAgileObject}
  IAgileObject = interface(IUnknown)
  ['{94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90}']
    // public
  end;
  IID_IAgileObject = IAgileObject;
  {$EXTERNALSYM IID_IAgileObject}

Again: Thank you for your help and explanations on this subject.

 

Edited by maXcomX
Typo

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×