4

From reading several answers on this site, I learned that CoInitialize(Ex) should be called by the creator of a thread. Then, any code running in that thread can use COM. If that code happens to call CoInitialize(Ex) by itself, that would be harmless, because it would have no effect. It should not call CoUninitialize – that too should be done by the creator of the thread – but it won't if it inspects the (saved) result of CoInitialize(Ex) which would be S_FALSE. If the creator would not take responsibility for performing the initialization, the thread is at the "mercy" of that code to pick an appropriate threading model, which from then on won't be changeable.

What are the implications of all this for writing and using libraries?

When all code is your own, and you have a small team, it is manageable to organize the COM (un)initialization calls well. However, with libraries, the user shouldn't need to know how they do what they do, e.g. that COM is involved. I'd hate to have in documentation what can be dealt with in code. Also, no assumptions should be made about what thread the library's code will run in, unless it concerns VCL code.

Most open source libraries I've inspected call CoInitialize(Ex) in the initialization section and CoUninitialize in the finalization section, often even without checking if initialization succeeded. Some call InitProc instead, yet some first check IsLibrary.

What is it that they really should be doing? What if I'd write a unit myself that I'd want anyone to be able to use without much consideration? Wrap everything that touches COM in a thread and have that thread perform its own COM (un)initialization?

How bad is it really to be naive about it, like those open source units? When using them in a VCL app, their COM (un)initialization would always run on the main thread, which already has that performed first by Forms.TApplication.Create. Does that make the calls in the units innocent but useless? What if any of the units is listed in the .dpr before Forms? What about non-VCL apps, or DLL's? Should I not use such units before having them corrected? Should I somehow guard against what they might try to do, by preventively initializing COM always?

This is a rather complex question, but it all boils down to: how to (make it easy to) avoid trouble with regards to COM (un)initialization?

Community
  • 1
  • 1
Thijs van Dien
  • 6,516
  • 1
  • 29
  • 48
  • 1
    Perhaps http://stackoverflow.com/questions/30020493/whats-the-appropriate-place-to-call-coinitialize-couninitialize-across-dlls ? – Jerry Dodge Jan 06 '17 at 03:32
  • You could try reading the [documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx), particularly the first paragraph in Remarks which explains repeated (nested) calls. – Ken White Jan 06 '17 at 03:35
  • I'm not familiar with delphi, but one hard recommendation is to not call CoUninitialize on calls to CoInitialize(Ex) that failed (note S_FALSE is success). How CoInitialize(Ex) was used defines thread's apartment, so another hard reco is to make absolutely sure your coclasses are marked with the proper ThreadingModel so COM will handle thread marshalling for you whatever the apartment your object reside in is. For example, don't mark it as 'both' if it's not. Other than that the doc is correct. If you want to make sure your library handles all cases, make Com init an option (registry, etc.). – Simon Mourier Jan 06 '17 at 07:14
  • 1
    I think its just a terrible idea to "initialize COM" on a random call into your library. A thread, to be used as a valid COM thread must be pumping messages properly or otherwise be doing things appropriately to the kind of apartment - and only the creator of the thread can know that. – Chris Becke Jan 06 '17 at 09:36

1 Answers1

7

It's simple enough. Document that any consumers of your library must initialize COM. It's perfectly respectable and commonplace to do that. There's really nothing to worry about in placing such a requirement on the consumers of your library. If they fail to do what is required of them, that is their problem. As you point out, the creator of the thread has to take charge of initializing COM so you have no viable alternative.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I'd strongly prefer to make it foolproof. That means if documentation were the only option to deal with COM, I shouldn't use COM in my libraries, which is very limiting. What about creating my own thread and run my code in there? If it doesn't involve anything GUI-related (mostly it's MSXML), what are your objections? Also you only address the writer's end; how do you suggest to approach 3rd party code that doesn't adhere to your principle? When to patch those calls out, when to leave them...? – Thijs van Dien Jan 06 '17 at 18:41
  • You can't make a library fool proof. If somebody doesn't follow the rules it won't work. What you should aim for is that the rules can be followed. – David Heffernan Jan 06 '17 at 18:43
  • I aim for as few rules as possible. – Thijs van Dien Jan 06 '17 at 18:50
  • Well, do it your way if you want. It's up to you. – David Heffernan Jan 06 '17 at 18:51
  • From that I infer that there's no reason why it wouldn't work, as in: no major gotchas to using COM in another thread, as long as it's nothing cooperative like GUI stuff, such as MSXML, WMI, things like that? I've seen the rabbit hole be very deep in some cases (need for a message pump, deadlocks when not careful) but suppose mine is rather simple. – Thijs van Dien Jan 06 '17 at 18:58
  • Well, just loads of complexity for no gain. – David Heffernan Jan 06 '17 at 19:08