【问题标题】:Why I can call StringFromCLSID even without the calling of CoInitializeEx before?为什么即使之前没有调用 CoInitializeEx 也可以调用 StringFromCLSID?
【发布时间】:2015-08-10 17:07:34
【问题描述】:

我正在通过 C++ 学习 COM。来自MSDN

应用程序必须在进行任何操作之前使用 CoInitializeEx 其他 COM 库调用内存分配函数除外

内存分配函数在我看来是CoTaskMemAllocCoTaskMemFree

但是我明白了,无论是否调用 CoInitializeExCoUninitialize 函数,我的“Hello World”都可以正常工作。在我的代码中,我使用在 combaseapi.h 标头中声明的 StringFromCLSID 函数。所以,在我看来它是一个 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 也能工作?

谢谢。

【问题讨论】:

  • 这听起来可能有点迂腐,但我认为这很重要:始终遵守规范为您提供最佳服务。该规则说,如果没有首先调用CoInitializeEx,则允许Microsoft 使StringFromCLSID 使您的程序崩溃;它没有发生在你身上的事实是无关紧要的。在编程语言圈中,这被称为“未定义行为”。相反,微软花费了数千小时添加多余的代码,并放弃了性能增强,因为必须保护不遵守规范的程序。
  • @Bush:您链接到的文档适用于CoInitializeEx()Windows Mobile 版本。 CoInitializeEx()桌面 版本记录在 here 中,其措辞与移动版本不同。

标签: c++ winapi visual-c++ com


【解决方案1】:

StringFromCLSID 基本上是将 GUID 值(字节)打印输出到字符串中,然后用连字符和大括号很好地格式化它。不涉及其他任何内容,因此此调用成功不需要 COM 初始化。

为了安全起见,您必须执行CoInitialize/CoInitializeEx,但不执行此操作不一定会立即遇到问题。

【讨论】:

  • MSDN 建议使用CoInitializeEx 而不是CoInitialize“新应用程序应该调用CoInitializeEx 而不是CoInitialize。”
  • 这是正确的,但是 CoInitialize 调用完全等于带有 COINIT_APARTMENTTHREADED 参数的 CoInitializeEx 调用。
【解决方案2】:

为什么即使之前没有调用 CoInitializeEx,StringFromCLSID 也能工作?

文档中正确说明了关键信息。

CoInitializeEx function:

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

StringFromCLSID function:

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

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

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

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

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

这就是为什么您可以在不先调用CoInitializeEx() 的情况下调用StringFromCLSID()GUID 本身不需要 COM 初始化。该字符串被分配了一个不需要 COM 初始化的内存函数。并且字符串的格式很可能不需要 COM 初始化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多