4

I am learning COM through C++. From MSDN:

Applications are required to use CoInitializeEx before they make any other COM library calls except for memory allocation functions.

The memory allocation functions is CoTaskMemAlloc and CoTaskMemFree in my opinion.

But I see, my "Hello World" works fine with and without the CoInitializeEx and CoUninitialize functions calling. In my code I use the StringFromCLSID function which is declared in the combaseapi.h header. So, it is a COM function in my opinion. My code:

/* entry_point.cpp */
#include "Tools.h"
#include <objbase.h>

int main(){
  HRESULT hr = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  if (FAILED(hr)){
    trace("Can't initialize COM for using in the current thread.");
    keep_window_opened();
    return 1;
  }
  // {D434CF7D-2CDD-457A-A4EF-5822D629CE83}
  static const CLSID clsid =
  { 0xd434cf7d, 0x2cdd, 0x457a, { 
    0xa4, 0xef, 0x58, 0x22, 0xd6, 0x29, 0xce, 0x83 } };

  const size_t SIZE = 39;
  wchar_t* wch = nullptr;
  hr = ::StringFromCLSID(clsid, &wch);
  if (FAILED(hr)){
    trace("Can't convert CLSID to wchar_t array.");
  }
  else{
    trace("CLSID converted to wchar_t array.");
    char mch[SIZE];
    size_t count = 0;
    int result = ::wcstombs_s(&count, mch, wch, SIZE);
    if (result){
      trace("Can't convert wchar_t array to char array.");
    }
    else{
      trace(mch);
    }
    ::CoTaskMemFree(wch);
  }
  ::CoUninitialize();

  keep_window_opened();
  return 0;
}

If I remove the calls of CoInitializeEx and CoUninitialize functions, then my code works still. I expected it will not work...

Why StringFromCLSID work even without the calling of CoInitializeEx before?

Thank you.

Andrey Bushman
  • 11,712
  • 17
  • 87
  • 182
  • This might sound a bit pedantic but I think it's important: you are best served by always obeying specifications. That rule says Microsoft is allowed to make `StringFromCLSID` crash your program if `CoInitializeEx` has not been called first; the fact that it hasn't happened to you is *immaterial*. In programming language circles this is called "undefined behavior". What happens instead is that Microsoft spends thousands of hours adding otherwise superfluous code, and giving up on performance enhancements, because of having to protect programs that don't obey specifications. – Euro Micelli May 28 '15 at 13:48
  • @Bush: The documentation you linked to is for the **Windows Mobile** version of `CoInitializeEx()`. The **desktop** version of `CoInitializeEx()` is documented [here](https://msdn.microsoft.com/en-us/library/windows/desktop/ms695279.aspx) instead, and has different verbage than the mobile version. – Remy Lebeau May 28 '15 at 22:51

2 Answers2

4

StringFromCLSID is basically a printout of GUID value (bytes) into string, then formatting it nicely with hyphens and braces. There is nothing else involved and hence COM initialization is not really needed for this call to succeed.

You have to do CoInitialize/CoInitializeEx to be safe, but not doing it you don't necessarily hit a problem right away.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • 1
    MSDN recommends to use `CoInitializeEx` instead of `CoInitialize`: *"New applications should call `CoInitializeEx` instead of `CoInitialize`."* – Andrey Bushman May 28 '15 at 09:29
  • 1
    This is corrrect, however `CoInitialize` call is simply equal to `CoInitializeEx` call with `COINIT_APARTMENTTHREADED` argument. – Roman R. May 28 '15 at 09:36
3

Why StringFromCLSID work even without the calling of CoInitializeEx before?

The key information is stated right in the documentation.

CoInitializeEx function:

You need to initialize the COM library on a thread before you call any of the library functions except ... the memory allocation functions. Otherwise, the COM function will return CO_E_NOTINITIALIZED.

StringFromCLSID function:

StringFromCLSID calls the StringFromGUID2 function to convert a globally unique identifier (GUID) into a string of printable characters.

The caller is responsible for freeing the memory allocated for the string by calling the CoTaskMemFree function.

StringFromCLSID() returns a dynamically allocated string. We can infer from the highlighted sentence above that the memory is allocated using CoTaskMemAlloc() - which is explicitly documentated as not requiring CoInitializeEx().

StringFromGUID2() formats GUID data into a caller-specified memory block. Formatting a string does not require COM functionality. The wsprintfW(), StringCbPrintfW(), or other equivalent function would suffice. So CoInitializeEx() should not be required for StringFromGUID2(), even though this is not explicitly documented. I think it would be pretty short-sighted for Microsoft to not use one of their many available string formatting functions to implement StringFromGUID2(). So I think it should be safe to say that CoInitializeEx() is not a requirement for this (unless Microsoft says otherwise).

The GUID structure simply contains a few numbers and bytes. Declaring and using a GUID is not dependent on the COM library. You can freely use a GUID in your code all you want without touching COM at all - unless you want to generate a new GUID, in which case the CoInitializeEx() requirement for CoCreateGUID() is blurry as CoCreateGUID() is in the COM library but is explicitly documented as simply calling UuidCreate(), which is in the RPC library instead.

So that is why you can call StringFromCLSID() without calling CoInitializeEx() first. A GUID on its own does not require COM initialization. The string is being allocated with a memory function that does not require COM initialization. And the string is being formatted in a manner that most likely does not require COM initialization.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770