为什么我可以调用StringFromCLSID,即使没有调用CoInitializeEx之前

Why I can call StringFromCLSID even without the calling of CoInitializeEx before?

本文关键字:调用 CoInitializeEx 之前 我可以 StringFromCLSID 为什么      更新时间:2023-10-16

我正在通过c++学习COM。从MSDN:

应用程序需要在创建CoInitializeEx之前使用它其他COM库调用,内存分配函数除外。

内存分配函数我认为是CoTaskMemAllocCoTaskMemFree

但是我看到,我的"Hello World"在没有CoInitializeExCoUninitialize函数调用的情况下工作得很好。在我的代码中,我使用StringFromCLSID函数,该函数在combaseapi.h头中声明。所以,在我看来是一个COM函数。我的代码:

/* 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;
}

如果我删除CoInitializeExCoUninitialize函数的调用,那么我的代码仍然工作。我预料它不会成功……

为什么StringFromCLSID在没有调用CoInitializeEx的情况下也能工作?

谢谢。

StringFromCLSID基本上是将GUID值(字节)打印成字符串,然后使用连字符和大括号对其进行格式化。不涉及其他任何内容,因此,要使此调用成功,实际上不需要初始化COM。

您必须执行CoInitialize/CoInitializeEx以确保安全,但不这样做您不一定会立即遇到问题。

为什么StringFromCLSID工作,甚至没有调用CoInitializeEx之前?

关键信息在文档中有详细说明。

CoInitializeEx功能:

在调用任何库函数之前,您需要在线程上初始化COM库,除了…内存分配函数。否则,COM函数将返回coe_notinitialized。

StringFromCLSID功能:

StringFromCLSID调用StringFromGUID2函数将全局唯一标识符(GUID)转换为可打印字符的字符串。

调用者负责通过调用CoTaskMemFree函数释放分配给字符串的内存。

StringFromCLSID()返回一个动态分配的字符串。我们可以从上面突出显示的句子中推断,内存是使用CoTaskMemAlloc()分配的——这在文档中被明确地描述为不需要CoInitializeEx()

StringFromGUID2()GUID数据格式化到调用者指定的内存块中。格式化字符串不需要COM功能。wsprintfW()StringCbPrintfW()或其他等效函数就足够了。因此,StringFromGUID2()不需要CoInitializeEx(),即使没有明确的文档说明。我认为微软不使用他们众多可用的字符串格式化函数之一来实现StringFromGUID2()是相当短视的。所以我认为可以肯定地说,CoInitializeEx()不是这方面的要求(除非微软另有说明)。

GUID结构体只包含一些数字和字节。声明和使用 GUID不依赖于COM库。你可以在你的代码中自由地使用GUID,而不需要接触COM——除非你想生成一个新的GUID,在这种情况下,CoInitializeEx()CoCreateGUID()的要求是模糊的,因为CoCreateGUID()在COM库中,但被明确地记录为简单地调用UuidCreate(),而不是在RPC库中。

所以这就是为什么你可以先调用StringFromCLSID()而不调用CoInitializeEx()GUID本身不需要COM初始化。字符串被分配了一个不需要COM初始化的内存函数。字符串的格式化方式使得很可能不需要COM初始化。