VS 2010 C++ CLR Library project, errors on adding comutil.h library
> Error 20 error LNK2001: unresolved
> external symbol "extern "C" long
> __stdcall VariantCopy(struct tagVARIANT *,struct tagVARIANT const
> *)" (?VariantCopy@@$$J18YGJPAUtagVARIANT@@PBU1@@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
> Error 18 error LNK2001: unresolved
> external symbol "extern "C" void
> __stdcall VariantInit(struct tagVARIANT *)"
> (?VariantInit@@$$J14YGXPAUtagVARIANT@@@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
> Error 13 error LNK2001: unresolved
> external symbol "void __stdcall
> _com_issue_error(long)" (?_com_issue_error@@$$FYGXJ@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
> Error 10 error LNK2028: unresolved
> token (0A000376) "void __stdcall
> _com_issue_error(long)" (?_com_issue_error@@$$FYGXJ@Z)
> referenced in function "void __cdecl
> _com_util::CheckError(long)" (?CheckError@_com_util@@$$FYAXJ@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
> Error 4 error LNK2028: unresolved
> token (0A0003BC) "extern "C" void
> __stdcall VariantInit(struct tagVARIANT *)"
> (?VariantInit@@$$J14YGXPAUtagVARIANT@@@Z)
> referenced in function "public:
> __thiscall _variant_t::_variant_t(class _variant_t const &)" (??0_variant_t@@$$FQAE@ABV0@@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
> Error 2 error LNK2028: unresolved
> token (0A0003D1) "extern "C" long
> __stdcall VariantCopy(struct tagVARIANT *,struct tagVARIANT const
> *)" (?VariantCopy@@$$J18YGJPAUtagVARIANT@@PBU1@@Z)
> referenced in function "public:
> __thiscall _variant_t::_variant_t(class _variant_t const &)" (??0_variant_t@@$$FQAE@ABV0@@Z) D:ProjectsALServicencFlowncOPClient.NETStdafx.obj ncOPClient.NET
needed for
private:
std::string from_variant(VARIANT& vt)
{
_bstr_t bs(vt);
return std::string(static_cast<const char*>(bs));
}
/*
void to_variant(const std::string& str, VARIANT& vt)
{
_bstr_t bs(str.c_str());
reinterpret_cast<_variant_t&>(vt) = bs;
}*/
how can I fix it ?
Форум программистов Vingrad
Поиск: |
|
Опции темы |
Alca |
|
||
Эксперт Профиль Репутация: 25
|
Чего надо заинклюдить?
——————— C++ cross-platform library |
||
|
|||
smoke_man |
|
||
Опытный Профиль
Репутация: 9
|
#include <comdef.h> ——————— 0x2b|~0x2b |
||
|
|||
Alca |
|
||||||
Эксперт Профиль Репутация: 25
|
есть такой Добавлено @ 14:28
Если убрать этот код, то все компилиться…… Добавлено @ 14:29 ПРоект сейчас ANSI. Это сообщение отредактировал(а) Alca — 23.10.2009, 14:30 ——————— C++ cross-platform library |
||||||
|
|||||||
smoke_man |
|
||
Опытный Профиль
Репутация: 9
|
А если попробывать прилинковать comsupp.lib ——————— 0x2b|~0x2b |
||
|
|||
mes |
|
||
любитель Профиль Репутация: 6
|
это ошибка линкера о том что нет тела функции, а значит, грубо говоря, надо не заинклудить, а подключить нужную библиотeку.. ——————— http://opendots.net |
||
|
|||
Alca |
|
||
Эксперт Профиль Репутация: 25
|
нет у меня (D:_TMP_ProgfilesEmbarcadero) такой либы… Добавлено через 2 минуты и 52 секунды ——————— C++ cross-platform library |
||
|
|||
smoke_man |
|
||
Опытный Профиль
Репутация: 9
|
Пробуй. Присоединённый файл ( Кол-во скачиваний: 28 ) ——————— 0x2b|~0x2b |
||
|
|||
smoke_man |
|
||
Опытный Профиль
Репутация: 9
|
Тока это от Visual Studio. Надо будет в формат билдера перевести. Это сообщение отредактировал(а) smoke_man — 23.10.2009, 14:49 ——————— 0x2b|~0x2b |
||
|
|||
Alca |
|
||
Эксперт Профиль Репутация: 25
|
Как? ——————— C++ cross-platform library |
||
|
|||
smoke_man |
|
||
Опытный Профиль
Репутация: 9
|
coff2omf.exe используй Добавлено через 21 секунду ——————— 0x2b|~0x2b |
||
|
|||
Alca |
|
||||
Эксперт Профиль Репутация: 25
|
Добавлено через 6 минут и 30 секунд Добавлено через 8 минут и 42 секунды
Добавлено через 14 минут и 38 секунд ——————— C++ cross-platform library |
||||
|
|||||
Alca |
|
||
Эксперт Профиль Репутация: 25
|
smoke_man, спасибо за помощь! Добавлено через 2 минуты и 29 секунд ——————— C++ cross-platform library |
||
|
|||
Ceceron |
|
||
Новичок Профиль Репутация: нет
|
Alca а подскажи какой ты выход нашол, а то у меня таже ошибка, поделись исходничком. |
||
|
|||
Alca |
|
||
Эксперт Профиль Репутация: 25
|
http://forum.vingrad.ru/forum/topic-257483.html ——————— C++ cross-platform library |
||
|
|||
|
Правила форума «С++ Builder» | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rrader. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) |
0 Пользователей: |
« Предыдущая тема | C++ Builder | Следующая тема » |
|
|
|
Правила раздела Visual C++ / MFC / WTL (далее Раздела)
Проблема линковки
, использование _bstr_t в Studio.net
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
Господа, прошу помощи в следующей проблеме. #include <comutil.h> ошибки: test error LNK2019: unresolved external symbol «void __stdcall _com_issue_error(long)» (?_com_issue_error@@YGXJ@Z) referenced in function «public: __thiscall _bstr_t::_bstr_t(char const *)» (??0_bstr_t@@QAE@PBD@Z) все, что нашел в и-нете положительного результата не принесло. =( Заранее спасибо |
Dr_ShpitsVogel |
|
а вроде нужно подключать #include <comdef.h> |
smart_lts |
|
Спасибо ! Действительно помогло ! |
sploid |
|
Lib: comsupp.lib |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Visual C++ / MFC / WTL
- Следующая тема
[ Script execution time: 0,0222 ] [ 16 queries used ] [ Generated: 9.02.23, 09:07 GMT ]
- Remove From My Forums
-
Question
-
Hi,
I have created a WCE ATL DLL using EVC 4.0 targeting standard SDK-500.
In this DLL I am creating few smart pointers as shown below._COM_SMARTPTR_TYPEDEF(IMarkers, __uuidof(IMarkers));
_COM_SMARTPTR_TYPEDEF(IMarker, __uuidof(IMarker));This is creating problem for me during Linking, I am getting few
linking issues
error LNK2001: unresolved external symbol «void __cdecl
_com_issue_error(long)» (?_com_issue_error@@YAXJ@Z)I knew its something related with C++ style of exception handling,
which was not supported in Win CE 3.0.
There are lot of query is there related to same issue, but most of them
are talking about Win CE 3.0, they are proposing to implement a dummy
function to handle this issue.
Do we have any alternative work arround for Win CE 5.0.
As per my knowledge Win CE 5.0 support C++ style of exception.Right now I defined an empty function and get rid from this linking issue. Do we have any reasonable work around?
Thanks
Rakesh
Com issue error lib
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.
Полезные ссылки:
FAQ Раздела
Обновления для FAQ Раздела
Поиск по Разделу
MSDN Library Online
Господа, прошу помощи в следующей проблеме.
Не могу использовать _bstr_t в проекте из-за ошибок линковки.
код :
#include
.
void CtestDlg::OnBnClickedButton1()
<
BSTR qq;
qq = (_bstr_t)»qq»;
>
test error LNK2019: unresolved external symbol «void __stdcall _com_issue_error(long)» (?_com_issue_error@@YGXJ@Z) referenced in function «public: __thiscall _bstr_t::_bstr_t(char const *)» (??0_bstr_t@@QAE@PBD@Z)
test error LNK2019: unresolved external symbol «wchar_t * __stdcall _com_util::ConvertStringToBSTR(char const *)» (?ConvertStringToBSTR@_com_util@@YGPA_WPBD@Z) referenced in function «public: __thiscall _bstr_t::Data_t::Data_t(char const *)» (??0Data_t@_bstr_t@@QAE@PBD@Z)
Источник
Обнаружены ошибки в библиотеках C++Builder
Введение
Мы регулярно проверяем открытые проекты, а так же все другое, что можно проверить. Например, мы проверяли библиотеки, входящие в состав Visual C++ 2012. Результатом стала заметка: «Обнаружены ошибки в библиотеках Visual C++ 2012».
В дистрибутив Visual C++ входят исходные коды библиотек. Ситуация с C++Builder хуже. Есть только заголовочные файлы. Поэтому удалось проверить только некоторые inline-функции. Тем не менее, удалось найти кое-что интересное. Рассмотрим соответствующие участки кода.
Работа с предупреждениями
Диагностическое сообщение, выданное PVS-Studio:
V665 Possibly, the usage of ‘#pragma warning(default: X)’ is incorrect in this context. The ‘#pragma warning(push/pop)’ should be used instead. Check lines: 16, 18. iaguid.h 18
Нехорошо устанавливать режим вывода предупреждения в состояние по умолчанию. Правильным подходом, будет сохранить, а затем восстановить предыдущее состояние. Для это следует использовать «#pragma warning(push[ ,n ])» и «#pragma warning(pop)».
Неудачный макрос
Диагностическое сообщение, выданное PVS-Studio:
V640 The code’s operational logic does not correspond with its formatting. The second statement will always be executed. It is possible that curly brackets are missing. utilcls.h 1781
Макрос SET_VTYPE_AND_VARREF написан плохо. Его содержимое не обрамлено фигурными скобками < >. В результате условие «if (src)» будет относиться только к первой строчке макроса.
Неопределенное поведение
Диагностическое сообщение, выданное PVS-Studio:
V610 Instantiate linear_congruential : Undefined behavior. Check the shift operator ‘
Источник
Com issue error lib
Эксперт Профиль Репутация: 25
|
||||||
|
Опытный
Профиль
Группа: Участник
Сообщений: 447
Регистрация: 25.1.2007
Где: Рязань
Репутация: 9
Всего: 17
smoke_man |
|
||
|
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
Alca |
|
||
Код |
// Look for instance pInstanceName _bstr_t bstrInstance; _bstr_t bstrInputInstance = pInstanceName; for( int k=0; k NumInstances; k++ ) < bstrInstance = (wchar_t *)((PBYTE)pPerfInst + pPerfInst->NameOffset); if (!stricmp((LPCTSTR)bstrInstance, (LPCTSTR)bstrInputInstance)) < pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfInst + pPerfInst->ByteLength); break; > |
// Get the next instance.
pPerfInst = NextInstance( pPerfInst );
>
Если убрать этот код, то все компилиться.
Добавлено @ 14:29
VC++2008 компилит без проблем.
ПРоект сейчас ANSI.
Это сообщение отредактировал(а) Alca — 23.10.2009, 14:30
Опытный
Профиль
Группа: Участник
Сообщений: 447
Регистрация: 25.1.2007
Где: Рязань
Репутация: 9
Всего: 17
smoke_man |
|
||
|
любитель
Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006
Репутация: 6
Всего: 250
mes |
|
||
Цитата(Alca @ 23.10.2009, 13:18 |
Чего надо заинклюдить? |
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
Alca |
|
||
Цитата |
А если попробывать прилинковать comsupp.lib |
нет у меня (D:_TMP_ProgfilesEmbarcadero) такой либы.
Добавлено через 2 минуты и 52 секунды
Есть у кого comsupp.lib?
Опытный
Профиль
Группа: Участник
Сообщений: 447
Регистрация: 25.1.2007
Где: Рязань
Репутация: 9
Всего: 17
Присоединённый файл ( Кол-во скачиваний: 28 ) comsupp.lib 410,00 Kb
smoke_man |
|
||
|
Опытный
Профиль
Группа: Участник
Сообщений: 447
Регистрация: 25.1.2007
Где: Рязань
Репутация: 9
Всего: 17
Тока это от Visual Studio. Надо будет в формат билдера перевести.
Это сообщение отредактировал(а) smoke_man — 23.10.2009, 14:49
smoke_man |
|
||
|
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
Alca |
|
||
Цитата |
Надо будет в формат билдера перевести. |
Опытный
Профиль
Группа: Участник
Сообщений: 447
Регистрация: 25.1.2007
Где: Рязань
Репутация: 9
Всего: 17
Добавлено через 21 секунду
Входит в состав билдера.
smoke_man |
|
||
|
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
Alca |
|
||
Код |
C:>»C:\coff2omf» «C:\comsupp.lib» «C:\comsupp2.lib» COFF to OMF Converter Version 1.2.0 Copyright (c) 1999-2009 Embarcadero Technolo gies, Inc. All rights reserved. ERROR: COFF error: C:\comsupp.lib (coffread.cpp, 1675) : reading obj symbol table |
C:>pause
Для продолжения нажмите любую клавишу . . .
Добавлено через 6 минут и 30 секунд
Скомпилил с CComBSTR (вместо _bstr_t). Сейчас проверю.
Цитата |
The following compiling messages will come out when a _bstr_t object is defined in a cpp file with Borland C++ builder 5: Error: Unresolved external ‘ __stdcall _com_util::ConvertStringToBSTR(const char *) referenced from test.obj. Error: Unresolved external ‘ __stdcall _com_issue_error(long) referenced from test.obj. You could use CComBSTR instead of _bstr_t with Borland C++ builder. |
Добавлено через 14 минут и 38 секунд
Все работает как надо. Давно я так не трах. ся.
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
smoke_man, спасибо за помощь!
Alca |
|
||
|
Профиль
Группа: Участник
Сообщений: 4
Регистрация: 31.12.2009
Репутация: нет
Всего: нет
Ceceron |
|
||
|
Эксперт
Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006
Репутация: 25
Всего: 50
Alca |
|
||
1. Публиковать ссылки на вскрытые компоненты
2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
- Литературу по С++ Builder обсуждаем здесь
- Действия модераторов можно обсудить здесь
- С просьбами о написании курсовой, реферата и т.п. обращаться сюда
- Настоятельно рекомендуем заглянуть в DRKB (Delphi Russian Knowledge Base) — крупнейший в рунете сборник материалов по Дельфи
- FAQ раздела лежит здесь!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rrader.
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) |
0 Пользователей: |
« Предыдущая тема | C++ Builder | Следующая тема » |
[ Время генерации скрипта: 0.1422 ] [ Использовано запросов: 21 ] [ GZIP включён ]
Источник
Adblock
detector
Fitting COM into C++ System Error Handling
If you are programming on Windows, chances are high you are using COM. Be it for XML (MSXML), shell interaction, directshow, enumerating system devices or active directory objects, and a myriad of other functionality offered via COM interfaces.
One thing you have to deal with when using COM are return error codes of almost all interface methods, which they do in form of returning a HRESULT (typedef’d as long). An example of such would be:
virtual HRESULT IXMLDOMParseError::get_line(long* lineNumber);
Many times, those codes represent traditional Windows system error codes, and you can get to the windows error code via the HRESULT_CODE macro. That’s why in my opinion it makes sense to be able to turn them into system errors and simplify error handling.
Native C++ Compiler COM Support
I find it frightening when I see examples all over the internet, including MSDN, with C-style COM programming. All the object lifetime and error handling is done by hand, which clutters up the real task and understanding of what a COM library offers.
If you are like me, you really appreciate the Native C++ compiler COM support providing classes like _variant_t, _bstr_t, _com_ptr_t and _com_error. It’s a simple, thin and lightweight C++ wrapper around COM interfaces, which you don’t want to miss out. The C++ wrappers not only manage the lifetime of memory or objects but also simplify error handling, because you can wrap error codes in an error class and throw that around.
Unless you have to use the ‘raw’ COM interface methods, you get those wrappers for free when importing type libraries via VC’s #import directive.
Microsoft even updated _com_ptr_t with move semantics not so long ago, meaning it’s still mainstream.
#import’ing a type library usually creates wrapper methods around the raw functions, which perform the boiler plate of error checking
The wrapper checks for a FAILED operation and simply throws a _com_error, usually via a call to _com_issue_error_ex. E.g. the implementation of IXMLDOMParseError::get_line looks like this:
long IXMLDOMParseError::Getline ( ) { long _result = 0; HRESULT _hr = get_line(&_result); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); return _result; }
Simplify
_com_error is just one of many within the whole ecosystem of C++ error classes; dealing with an ever-growing list of error classes can make try/catch blocks a pain. If you have already your own application-wide error class you probably convert library errors into your own.
I prefer using standard components because the standard usually makes programming simpler. It turns out that std::system_error, available since C++11, is really useful for dealing with system-like errors, because it was designed with extensibility in mind.
COM even offers the option to set your own translation handler via _set_com_error_handler, so we can directly throw system errors and translating is encapsulated at one single point.
System Error
Polymorphic, Extensible Error Closure
The real power of std::system_error stems from the fact that it only provides certain logic to deal with error codes, but is otherwise agnostic to the kind of system error. It’s nothing more than a std exception class additionally storing an error code object. This error code object in turn holds an error number and a pointer to an error category.
I won’t go into further detail as Christopher Kohlhoff describes at length the rationale behind std::system_error and how to extend it with your own types on his blog. The bottom line is that the error category allows us to easily extend the system error handling.
Implementation
Outline
So what do we need?
-
An enum type classifying a COM error
-
An error category class capable of translating HRESULT codes into readable messages, and connecting COM error types to system error’s polymorphism.
-
Translation functions from COM error to system error; this would be also the point to capture any additional description provided by the COM layer, which isn’t available anymore at a later point.
-
Automatic translation
Code — Declarations
Step 1 — Classification
// Enables classifying HRESULT error codes; // @note We don't bother listing all possible values; // this also allows for compatibility with // ‘Construction rules for enum class values (P0138R0)’ enum class com_error_enum {}; namespace std { // Make com_error_enum known to std library template<> struct is_error_code_enum<com_error_enum> : true_type {}; } // std library overload for constructing a std::error_code from a classified com error. // @note This function is found by ADL from within std::error_code(com_error_enum) std::error_code make_error_code(com_error_enum e) noexcept; // Access one and only com category const error_category& com_category() noexcept;
Step 2 — Error Category
// Categorize a com error. // Translates a HRESULT to a message string and std::error_condition class com_error_category: public std::error_category { public: using error_category::error_category; const char* name() const noexcept override { return "com"; } // @note If _UNICODE is defined the error description gets // converted to an ANSI string using the CP_ACP codepage. std::string message(int hr) const override; // Make error_condition for error code (generic if possible) // @return system's default error condition if error value can be mapped to a Windows error, error condition with com category otherwise std::error_condition default_error_condition(int hr) const noexcept override; };
Step 3 — Translation (manual)
// Factory function creating a std::system_error from a HRESULT error code // @param msg Description to prepend to the error code message; must not be nullptr. std::system_error com_to_system_error(HRESULT code, const char* msg = ""); // Factory function creating a std::system_error from a HRESULT error code // @param msg Description to prepend to the error code message. std::system_error com_to_system_error(HRESULT code, const std::string& msg); // Factory function creating a std::system_error from a HRESULT error code // and an optional error message // @param msg Optional description to prepend to the error code message; // must not be nullptr; // gets converted to an ANSI string using the CP_ACP codepage. std::system_error com_to_system_error(HRESULT hr, const wchar_t* msg); // Factory function creating a std::system_error from a HRESULT error code // and an optional error message // @param msg Optional description to prepend to the error code message; // gets converted to an ANSI string using the CP_ACP codepage. std::system_error com_to_system_error(HRESULT hr, const std::wstring& msg); // Factory function creating a std::system_error from a _com_error. // If an error description is available, removes trailing newline or dot (end of last sentence) and prepends it to the error code message. // @note The error description gets converted to an ANSI string using the CP_ACP codepage. std::system_error com_to_system_error(const _com_error& e);
Step 3a — Desktop Family Apps
// Factory function creating a std::system_error from a HRESULT error code and optionally from an error information. // If an error description is available, removes trailing newline or dot (end of last sentence) and prepends it to the error code message. // @note The error description gets converted to an ANSI string using the CP_ACP codepage. std::system_error com_to_system_error(HRESULT hr, IErrorInfo* help);
Step 3b — Non-Desktop Apps
There’s no IErrorInfo interface available, see Implementation
Step 4 — Translation (automatic)
Step 4a — Desktop Family Apps
// Translate COM error and throw std::system_error. // Suitable as alternative COM error handler, settable with _set_com_error_handler // @note The error description gets converted to an ANSI string using the CP_ACP codepage. [[noreturn]] void __stdcall throw_translated_com_error(HRESULT hr, IErrorInfo* help = nullptr);
Step 4b — Non-Desktop Apps
// Translate COM error and throw std::system_error. // Suitable as alternative COM error handler, settable with _set_com_error_handler // @note The error description gets converted to an ANSI string using the CP_ACP codepage. [[noreturn]] void __stdcall throw_translated_com_error(HRESULT hr, const wchar_t* msg);
Implementation
It’s almost this simple. Extracting the error code message (step 2) and error description (step 3) is a bit tricky, but not hard either.
Things to be aware of:
-
Take care about unicode-to-ANSI string conversion:
The presented approach utilizes _com_util::ConvertBSTRToString, which in turn utilizes WideCharToMultiByte with the system codepage CP_ACP -
Mitigate reference-counting _bstr_t at the point when getting the description for the error message
-
Trim error description at end-of-sentence (that’s because the description is prepended to the error code message).
-
The IErrorInfo interface isn’t available in non-desktop environments. It gets replaced by a unicode C-String.
There’s a preprocessor macro _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP that we can use to tell the difference.
I think I thought about every detail for a correct, concise and convenient usage. Let’s take a look at some code again. Note that using namespace std; is assumed for readability.
Code — Implementation
// wide-to-ANSI string conversion helper namespace detail { inline unique_ptr<char[]> to_narrow(BSTR msg) { return unique_ptr<char[]>{ _com_util::ConvertBSTRToString(msg) }; } inline std::unique_ptr<char[]> to_narrow(const wchar_t* msg) { static_assert(std::is_same_v<wchar_t*, BSTR>); // const_cast is fine: // BSTR is a wchar_t*; // ConvertBSTRToString internally uses _wcslen and WideCharToMultiByte; return to_narrow(const_cast<wchar_t*>(msg)); }
Step 1 — Classification
inline error_code make_error_code(com_error_enum e) noexcept { return { int(e), com_category() }; } inline const error_category& com_category() noexcept { // immortalized object would be perfect, but isn’t subject of this post static com_error_category ecat; return ecat; }
Step 2 — Error Category
inline string com_error_category::message(int hr) const { // leverage _com_error::ErrorMessage #ifdef _UNICODE auto narrow = detail::to_narrow(_com_error{ hr }.ErrorMessage()); return narrow.get(); #else return _com_error{ hr }.ErrorMessage(); #endif } inline error_condition com_error_category::default_error_condition(int hr) const noexcept { if (HRESULT_CODE(hr) || hr == 0) // system error condition return system_category().default_error_condition(HRESULT_CODE(hr)); else // special error condition return { hr, com_category() }; }
Step 3 — Translation (manual)
inline system_error com_to_system_error(HRESULT hr, const string& msg) { // simply forward to com_to_system_error taking a C string return com_to_system_error(hr, msg.c_str()); } inline system_error com_to_system_error(HRESULT hr, const char* msg) { // construct from error_code and message return { { com_error_enum(hr) }, msg }; } inline system_error com_to_system_error(HRESULT hr, const wchar_t* msg) { return com_to_system_error(hr, *msg ? detail::to_narrow(msg).get() : ""); } inline system_error com_to_system_error(HRESULT hr, const wstring& msg) { return com_to_system_error(hr, msg.c_str()); }
Step 3a — Desktop Family Apps
inline system_error com_to_system_error(const _com_error& e) { // note: by forwarding to com_to_system_error(HRESULT, IErrorInfo*) // we benefit from hoisting _com_error::Description because _bstr_t wraps // up both unicode/ascii strings in a ref counter allocated on the heap return com_to_system_error(e.Error(), IErrorInfoPtr{ e.ErrorInfo(), false }); } inline system_error com_to_system_error(HRESULT hr, IErrorInfo* help) { using com_cstr = unique_ptr<OLECHAR[], decltype(SysFreeString)*>; auto getDescription = [](IErrorInfo* help) -> com_cstr { BSTR description = nullptr; if (help) help->GetDescription(&description); return { description, &SysFreeString }; }; com_cstr&& description = getDescription(e); // remove trailing newline or dot (end of last sentence) if (unsigned int length = description ? SysStringLen(description) : 0) { unsigned int n = length; // place sentinel, other than [rn.] OLECHAR ch0 = exchange(description[0], L''); for (;;) { switch (description[n - 1]) { case L'r': case L'n': case L'.': continue; } break; } // note: null-terminating is less ideal than just finding the new EOS, // but system_error copies its description string argument, // hence we don't gain anything from range-construction if (n < length && n) description[n] = L''; // reestablish 1st character if (n) description[0] = ch0; return com_to_system_error(e.Error(), description.get()); } // no description available return com_to_system_error(e.Error()); }
Step 3b — Non-Desktop Apps
std::system_error com_to_system_error(const _com_error& e) { // no description available; // wait, actually there is, but we don't know whether // _com_error has been constructed with a custom message or whether // _com_error::ErrorMessage() retrieves a formatted error code message. // Because the description message gets prepended when making system_error // we might end up with twice the same message. // That's why we cross-check with a newly constructed _com_error. bool hasCustomMessage; { _com_error e2{ e.Error() }; const wchar_t* msg = e.ErrorMessage(), msg2 = e2.ErrorMessage(); hasCustomMessage = msg && (!msg2 || std::wcscmp(msg, msg2)); } return com_to_system_error(e.Error(), hasCustomMessage ? detail::to_narrow(msg) : ""); }
Step 4 — Translation (automatic)
Step 4a — Desktop Family Apps
inline void __stdcall throw_translated_com_error(HRESULT hr, IErrorInfo* help) { throw com_to_system_error(hr, help); }
Step 4b — Non-Desktop Apps
inline void __stdcall throw_translated_com_error(HRESULT hr, const wchar_t* msg) { throw com_to_system_error(hr, msg ? msg : L””); }
Happy coding!
Klaus Triendl