何时调用Windows控制台应用程序所需的CoInitialize
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了何时调用Windows控制台应用程序所需的CoInitialize相关的知识,希望对你有一定的参考价值。
下面的代码源自https://docs.microsoft.com/en-us/windows/desktop/shell/folder-info#determining-an-objects-parent-folder,在2017年Visual Studio编译和运行时按预期工作:
#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
"), 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.storage
egfldr.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。为此,它提供了自己的mini-COM实现。 Shell扩展可能标记为不需要真正的COM,而在shell32中实现的东西会调用一个特殊的CoCreateInstance
,它试图直接从shell32加载东西。这是为了避免加载ole32.dll,因为它是一个非常大的文件加载到具有4 MiB RAM的Intel 386机器上(Windows 95最低要求)。
处理文件系统的IShellFolder
实现是在shell32中实现的,不需要COM,因此能够处理像c:Windowssystem32
这样的路径。
但是,CSIDL_MYDOCUMENTS
不是普通的文件夹,它是命名空间扩展,其实现的部分是在mydocs.dll中。当你发现时,它的一部分确实需要COM。
所有这些当然都是一个实现细节,你永远不应该假设任何这个都可以在没有初始化COM的情况下工作。
SHGetFolderLocation
可以将执行委托给需要COM初始化的扩展。虽然文档没有明确说明,但您可以找到有关ShellExecute
的注释,它是同一模块(shell32.dll)的一部分。
由于ShellExecute可以将执行委托给使用组件对象模型(COM)激活的Shell扩展(数据源,上下文菜单处理程序,动词实现),因此应在调用ShellExecute之前初始化COM。某些Shell扩展需要COM单线程单元(STA)类型。在这种情况下,COM应该初始化,如下所示:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
肯定存在ShellExecute不使用这些类型的Shell扩展之一的实例,并且这些实例根本不需要初始化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
}
以上是关于何时调用Windows控制台应用程序所需的CoInitialize的主要内容,如果未能解决你的问题,请参考以下文章
Push Kit 何时会包含 Android 12 所需的 android:exported 属性? [关闭]
卸载一个程序,在控制面板中删除时,提示“无法卸载,因为卸载所需的文件夹已经删除。”怎么办
我的通用 Windows 平台软件是不是能够获得启动关机所需的权限?