我已经实现了IThumbnailProvider,它被编译到dll中,然后使用regsvr32
注册。
在代码中,我使用了STL容器,如std::vector
:
std::vector<double> someRGBAccumulatorForDownsamplingToThumbnail = std::vector<double>(1234567);
因为STL容器在很大程度上是围绕RAII构建的,所以没有对失败的内存分配进行null检查。相反,上面的代码将在内存不足的情况下抛出一个异常。发生这种情况时,我是否应该捕获此异常以返回HRESULT
(上下文:GetThumbnail的实现(?
try {
// ...
} catch (bad_alloc& ex) {
return E_OUTOFMEMORY;
}
或者WINAPI可以安全地处理我允许异常";冒泡";?
我之所以这么问,是因为我读到WINAPI是基于C的,而且C没有例外。
IThumbnailProvider
是一个COM接口。组件对象模型是一个与语言无关的协议,它描述了客户端和接口实现者之间的二进制契约。它建立了一个具有明确规则的边界(应用程序二进制接口,ABI(1。
由于协议与语言无关,因此允许跨越ABI的内容仅限于最小公分母。它最终比C函数所称的支持略低。任何特定于语言的构造(如C++异常(都不能跨越ABI。
在C++中实现COM接口时,必须确保C++异常永远不会跨越ABI。你能做的最起码的事情就是将所有接口方法标记为noexcept
:
HRESULT MyThumbnailProvider::GetThumbnail(UINT, HBITMAP*, WTS_ALPHATYPE*) noexcept {
// ...
}
虽然这满足了COM协定的所有要求,但通常不希望出现未捕获的异常导致COM对象所在的整个进程中断。
更详细的解决方案是捕获所有异常并将其转换为HRESULT
错误代码(请参阅COM中的错误处理(,类似于有问题的代码:
HRESULT MyThumbnailProvider::GetThumbnail(UINT, HBITMAP*, WTS_ALPHATYPE*) noexcept {
try {
// ...
} catch(...) {
return E_FAIL;
}
}
同样,这是完全有效的,尽管任何COM开发人员都害怕看到0x80004005
错误代码,这在语义上等同于"出了问题">。在尝试诊断问题时几乎没有用处。
更有用的实现将尝试将某些众所周知的C++异常类型映射到标准HRESULT
值(例如std::bad_alloc
->E_OUTOFMEMORY
或std::system_error
到调用HRESULT_FROM_WIN32
的结果(。虽然可以在每个接口方法实现上手动实现catch
级联,但已经有库为您实现了。Windows实现库(WIL(为此提供了异常保护,将详细信息排除在代码之外。
以下是使用WIL的可能接口方法实现:
HRESULT MyThumbnailProvider::GetThumbnail(UINT, HBITMAP*, WTS_ALPHATYPE*) noexcept {
try {
// ...
}
CATCH_RETURN();
}
顺便说一句,我在后两个实现中保留了noexcept
说明符,仅作为防御措施;它们不是严格要求的,但要保持接口的有效性,以防将来实现发生变化,从而允许C++异常逃逸。
1我不知道有官方文件阐明了这些规则。我们必须假设编译器就是规范。顺便说一句,微软的C和C++编译器并不同意,Direct2D团队发现了这一点