何时调用 Windows 控制台应用程序需要 CoInitialize
When is calling CoInitialize required for a Windows 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("%sn"), szDisplayName);
}
psfParent->Release();
CoTaskMemFree(pidlSystem);
Sleep(5000);
return 0;
}
但是,如果我将CSIDL_SYSTEM
替换为CSIDL_MYDOCUMENTS
,则GetDisplayNameOf
方法调用将失败,并显示:
onecorecomcombaseobjactobjact.cxx(812)combase.dll!74EA3270: (caller: 74EA201B) ReturnHr(1) tid(d4c) 800401F0 CoInitialize has not been called.
onecoreuapshellwindows.storageregfldr.cpp(1260)windows.storage.dll!76FE4FA3: (caller: 76E9F7EE) ReturnHr(1) tid(d4c) 80040111 ClassFactory cannot supply requested class
在调用SHGetFolderLocation
之前添加CoInitialize(NULL);
可以解决此问题。
为什么在一种情况下需要调用 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可以在不加载COM/OLE的情况下运行。为此,它提供了自己的mini-COM实现。Shell 扩展可以将自己标记为不需要真正的 COM,并且在 shell32 中实现的东西会调用一个特殊的CoCreateInstance
,尝试直接从 shell32 加载内容。这是为了避免加载 ole32.dll因为在具有 4 MiB RAM(Windows 95 最低要求)的英特尔 386 机器上加载是一个非常大的文件。
处理文件系统的IShellFolder
实现是在 shell32 中实现的,不需要 COM,因此能够处理类似c:Windowssystem32
的路径。
但是,CSIDL_MYDOCUMENTS
不是一个普通的文件夹,它是一个命名空间扩展,其部分实现在 mydocs.dll 中。正如您所发现的,其中的某些部分确实需要 COM。
当然,所有这些都是一个实现细节,您永远不应该假设如果不初始化 COM,任何这些都可以工作。
SHGetFolderLocation
可以将执行委托给需要 COM 初始化的扩展。尽管文档没有明确说明,但您可以找到有关同一模块(shell32.dll的一部分ShellExecute
的注释。
因为 ShellExecute 可以将执行委托给 Shell 扩展(数据 源、上下文菜单处理程序、谓词实现)是 使用组件对象模型 (COM) 激活,COM 应为 在调用 ShellExecute 之前初始化。一些命令行管理程序扩展 需要 COM 单线程单元 (STA) 类型。在这种情况下, COM 应初始化,如下所示:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
当然,在某些情况下,ShellExecute不会使用以下之一。 这些类型的命令行管理程序扩展和这些实例不需要 要初始化的 COM。尽管如此,最好的做法是 始终在使用此功能之前启动 COM。
可以使用以下帮助程序类自动初始化当前线程上的 COM 库。
class COMRuntime
{
public:
COMRuntime() {
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
}
~COMRuntime() {
::CoUninitialize();
}
};
然后只需声明该类的一个实例:
int main()
{
COMRuntime com;
// the rest of your code
}
- 试图在visual studio上用C++创建一个桌面应用程序
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- c++r值引用应用于函数指针
- 在C应用程序中运行C++(带有STL)函数
- 使用VerQueryValue检索应用程序的文件描述
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- CoInitialize()在单独的线程上崩溃而不返回
- 使用调试/崩溃报告将应用程序部署到客户端
- C++控制台应用程序阻止退出
- 码头化的C++应用程序是否向后兼容早期的内核版本
- 将应用程序从32位移植到64位时出现问题
- 有没有任务栏API可以立即应用注册表更改
- 如何改变c++应用程序的视觉效果
- WM_CTLCOLORSTATIC从未在WIN32应用程序中触发
- PC中的程序和PHONE中的本机描述应用程序之间的数据连接
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 示例外壳应用程序显示的 V8 "segmentation fault (core dumped)"错误
- phytec phyBOARD iMX-6在从闪存而不是SD卡运行qt5 opengles应用程序时表现不佳(FPS减半
- TMap::Emplace() 在应用现有密钥时会覆盖吗?
- 何时调用 Windows 控制台应用程序需要 CoInitialize