Jump to content
Sonjli

COM: OleCheck() in polling

Recommended Posts

Hello,

I hope not to be out of topic...

I have a COM client (developed as NTService) that every 5 minutes need to reconnect to server (don't ask me why... the COM server kick out clients for "security" every 5 minutes).

In every "connect" I do this: OleCheck(CoInitializeEx(nil, COINIT_MULTITHREADED))

Can this be a problem? I see that sometimes, after 30/40 reconnections, the COM server seems "locked". No other clients can connect correctly to the server until I kill my NTService.

It is not fault of the "security" system, because I still tested that.

Any ideas?

Ask me for infos if you need.

 

Thanks in advance,

Eddy

Share this post


Link to post

I think you need to post some more code and some more details.

  • Does the OleCheck ever raise an exception? If so how do you handle this exception?
  • Are you sure that you shouldn't be using COINIT_APARTMENTTHREADED  instead?
  • Are you sure there's a CoUninitialize for every CoInitializeEx?
  • How does the server disconnect the client?
  • Have you tried debugging the server to determine what it's doing when it "hangs"?
  • Sad 1

Share this post


Link to post
14 minutes ago, Lars Fosdal said:

CoInit/CoUnInit should run only once per thread.

https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex

Quote

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.

Anyway, that's on the client and it shouldn't affect the server.

  • Thanks 1

Share this post


Link to post

Is it possible that if I never call the CoUninitialize the server goes out of order or lock after a lot of calls?

I call CoInitilize every 5 minutes but I forgot calling CoUninitialize... f**k.

Share this post


Link to post
1 hour ago, Anders Melander said:

I think you need to post some more code and some more details.

  • Does the OleCheck ever raise an exception? If so how do you handle this exception?
    • Never, I log this
  • Are you sure that you shouldn't be using COINIT_APARTMENTTHREADED  instead?
    • Every NTService create a lot of threads, so I think COINIT_MULTITHREADED is the right choice... I think...
  • Are you sure there's a CoUninitialize for every CoInitializeEx?
    • No. I am very angry. I didn't see this
  • How does the server disconnect the client?
    • Mistery. It's a third party server. Poor documentation
  • Have you tried debugging the server to determine what it's doing when it "hangs"?
    • The logs of the server resides in a remote machine. The machine is a CNC. Very hard to get inside...

 

Share this post


Link to post

Okay. Let's assume that the problem lies with the client.

 

Apartment threading just means that ingoing COM calls are executed on the main thread. This is like using TThread.Synchronize to ensure that code that isn't thread safe is executed in the context of the main thread. If your code is thread safe or if you are sure that you're not using callbacks (e.g. COM events) then COINIT_MULTITHREADED  is probably fine.

 

The missing CoUninitialize will affect the client but assuming that you clear all references to server interfaces on disconnect then I can't see how that would affect the server.

So your code logic should go something like this:

CoInitializeEx(...);
try
  Server := ConnectToServer;
  try
  
    Server.DoStuffWithServer;
    
   finally
     Server := nil;
   end;
finally
  CoUninitialize;
end;

 

Share this post


Link to post
3 hours ago, Sonjli said:

In every "connect" I do this: OleCheck(CoInitializeEx(nil, COINIT_MULTITHREADED))

CoInitializeEx should be called only once. Better place is at program startup.

 

Share this post


Link to post
8 minutes ago, FPiette said:

CoInitializeEx should be called only once. Better place is at program startup.

It's a service application so the COM stuff is presumably done in a thread. The CoInitializeEx/CoUninitialize should be done in the thread.

Apart from it being inefficient there's nothing wrong with looping and wrapping the server interaction with local calls to CoInitializeEx/CoUninitialize inside the loop. Outside the loop would be much more efficient of course but it might be a good idea to start again with a fresh, clean COM environment after the server has disconnected the client.

 

 

Share this post


Link to post

I checked our sources. We normally never use CoInitializeEx, explicitly, just CoInitialize, but I guess that depends on the requirements of the OLE control.

Share this post


Link to post
6 minutes ago, Lars Fosdal said:

I checked our sources. We normally never use CoInitializeEx, explicitly, just CoInitialize, but I guess that depends on the requirements of the OLE control.

https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize

 

Quote

CoInitialize ... Initializes the COM library on the current thread and identifies the concurrency model as single-thread apartment (STA).

New applications should call CoInitializeEx instead of CoInitialize.

 

And the documentation for CoInitializeEx states:

Quote

Because OLE technologies are not thread-safe, the OleInitialize function calls CoInitializeEx with the COINIT_APARTMENTTHREADED flag. As a result, an apartment that is initialized for multithreaded object concurrency cannot use the features enabled by OleInitialize.

 

Share this post


Link to post

I wonder what kind of effect that has on f.x. FireDAC?

 

We use the SQLNCLI11 and MSOLEDBSQL drivers in heavily multithreaded services. 

We also use OLE objects in DLLs created a decade and a half ago against our ERP system.

Share this post


Link to post
6 minutes ago, Lars Fosdal said:

I wonder what kind of effect that has on f.x. FireDAC?

CoInitialize etc. only affects the calling thread. If you access FireDAC from a thread, and you have called CoInitialize on that thread, then any inbound COM will be executed on the main thread (i.e. single threaded apartment a.k.a. apartment threaded).

I'm guessing you are only doing outbound COM so the threading model shouldn't matter.

Share this post


Link to post
19 hours ago, Anders Melander said:

Apartment threading just means that ingoing COM calls are executed on the main thread. This is like using TThread.Synchronize to ensure that code that isn't thread safe is executed in the context of the main thread. If your code is thread safe or if you are sure that you're not using callbacks (e.g. COM events) then COINIT_MULTITHREADED  is probably fine. 

I DO use COM events... so, should I change COINIT_MULTITHREADED in COINIT_APARTMENTTHREADED?

Can this make server unstable?

Remark: the server is no more reachable from other clients until I close my "buggy" ntservice. When this issue happens, I stop my ntservice and the other clients connects correctly.

I doubt it is a server issue, I am barely sure I am doing something wrong.

Share this post


Link to post
59 minutes ago, Sonjli said:

I DO use COM events... so, should I change COINIT_MULTITHREADED in COINIT_APARTMENTTHREADED?

It depends. If you are sure that your event handlers are thread safe then COINIT_MULTITHREADED is fine. Otherwise change it to COINIT_APARTMENTTHREADED.

 

1 hour ago, Sonjli said:

Can this make server unstable?

Well if you code isn't thread safe then anything can happen but I think it's more likely that the missing CoUninitialize is the cause.

  • Thanks 1

Share this post


Link to post

I strongly doubt that the eventual problem is due to a "logic" error linked to multithreading, because normally this would generate exceptions with memory access errors recorded in the Windows system log. Many years ago I also made the mistake of calling CoInitialezeEx without CoUninitialize and there were several problems especially when closing the application. Since then I ALWAYS use the CoInitializeEx (nil, COINIT_MULTITHREADED) at the beginning of the threading EXECUTE method (not in the while block, of course) and the CoUninitialize at the end of EXECUTE (when the thread terminates. Obviously, ONLY within the EXECUTE method (or in the methods subsequently called by it) will it be possible to access the OLE / ACTIVEX resources for that thread.

Share this post


Link to post

Why does a call to CoInitializeEx(nil,
                                                    COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

always returns 1 instead of 0 (S_OK)?

Share this post


Link to post
22 minutes ago, maXcomX said:

Why does a call to CoInitializeEx() always returns 1 instead of 0 (S_OK)?

1 is S_FALSE.  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.

This means that you are calling CoInitializeEx() on a thread that has already called CoInitialize/Ex() successfully, and you are specifying the same concurrency model that has already been assigned to the thread.

 

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

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

×