c++ - Windows 控制台应用程序何时需要调用 CoInitialize

标签 c++ winapi com console-application

下面的代码,源自https://learn.microsoft.com/en-us/windows/desktop/shell/folder-info#determining-an-objects-parent-folder ,通过 Visual Studios 2017 编译和运行时按预期工作:

#include "stdafx.h"
#include <shlobj.h>
#include <shlwapi.h>
#include <objbase.h>

#pragma comment(lib, "shlwapi")

int main()
{
    IShellFolder *psfParent = NULL;
    LPITEMIDLIST pidlSystem = NULL;
    LPCITEMIDLIST pidlRelative = NULL;
    STRRET strDispName;
    TCHAR szDisplayName[MAX_PATH];
    HRESULT hr;

    hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, NULL, &pidlSystem);

    hr = SHBindToParent(pidlSystem, IID_IShellFolder, (void **)&psfParent, &pidlRelative);

    if (SUCCEEDED(hr))
    {
        hr = psfParent->GetDisplayNameOf(pidlRelative, SHGDN_NORMAL, &strDispName);
        hr = StrRetToBuf(&strDispName, pidlSystem, szDisplayName, sizeof(szDisplayName));

        _tprintf(_T("%s\n"), szDisplayName);
    }

    psfParent->Release();
    CoTaskMemFree(pidlSystem);

    Sleep(5000);

    return 0;
}

如果我替换CSIDL_SYSTEMCSIDL_MYDOCUMENTS不过,GetDisplayNameOf方法调用失败并显示:

onecore\com\combase\objact\objact.cxx(812)\combase.dll!74EA3270: (caller: 74EA201B) ReturnHr(1) tid(d4c) 800401F0 CoInitialize has not been called.
onecoreuap\shell\windows.storage\regfldr.cpp(1260)\windows.storage.dll!76FE4FA3: (caller: 76E9F7EE) ReturnHr(1) tid(d4c) 80040111 ClassFactory cannot supply requested class

添加CoInitialize(NULL);在调用 SHGetFolderLocation 之前修复了问题。

为什么在一种情况下需要调用 CoInitialize 而在另一种情况下则不需要?

此外,似乎应该始终调用 CoInitialize,但有趣的是示例代码没有调用它。我很好奇为什么会这样。我无法按原样编译示例代码 - <iostream.h>找不到,这就是为什么我替换了 cout通过调用 _tprintf 打印代码...也许这表明了问题所在? C++ 运行时是否为你调用 CoInitialize,也许 VS 正在尝试为我构建一个 C 应用程序或其他东西(就像在 Linux 上,使用 gcc 和 g++ 编译有不同的含义)。

最佳答案

通常,您应该在创建从 IUnknown 继承的 shell COM 对象之前初始化 COM/OLE,使用拖放等。这也适用于可能在内部使用 COM 的函数,理论上可以是 shell32 和 shlwapi 中的大部分 SH* 函数。

为什么它可以与 CSIDL_SYSTEM 一起使用?

Windows 95 shell could run without loading COM/OLE 。为此,它提供了自己的迷你 COM 实现。 Shell 扩展可以将自己标记为不需要真正的 COM,并且在 shell32 内实现的内容将调用一个特殊的 CoCreateInstance 来尝试直接从 shell32 加载内容。这是为了避免加载 ole32.dll,因为它是一个非常大的文件,需要在具有 4 MiB RAM 的 Intel 386 计算机上加载(Windows 95 最低要求)。

处理文件系统的IShellFolder实现是在shell32中实现的,不需要COM,因此能够处理像c:\Windows\system32这样的路径。

CSIDL_MYDOCUMENTS 然而,它不是一个普通的文件夹,它是一个命名空间扩展,其部分实现位于 mydocs.dll 中。正如您所发现的,其中的某些部分确实需要 COM。

所有这些当然都是一个实现细节,您永远不应该假设其中任何一个都可以在不初始化 COM 的情况下工作。

关于c++ - Windows 控制台应用程序何时需要调用 CoInitialize,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55753034/

相关文章:

vb.net - 在 .NET 中获取使用 CreateObject 启动的进程的进程 ID

delphi - Delphi 中使用 VCL 的 Shell 命名空间扩展

com - 在 F# 中嵌入互操作类型

c++ - 查找 Microsoft Edge 版本(通过 WinApi)

c++ - 构造类成员 std::vector<std::unique_ptr<AClass>> 的聪明方法

C++ 编译器或链接器优化

windows - 如何防止使用 GetTempFileName 创建的文件在重启时被自动删除

c++ - 使用 C++ 从 USB 人机接口(interface)设备转换事件

c++ - 通过另一个宏创建的函数创建所有列表的宏

c - LeaveCriticalSection() 是否将缓存的变量刷新到内存中?