From f95c99074e1c0ce8eb128b601ac793b7f9a31cd8 Mon Sep 17 00:00:00 2001 From: Mari Sano Date: Sat, 6 Feb 2021 23:08:34 +0900 Subject: [PATCH] =?UTF-8?q?COM=E3=82=A8=E3=83=A9=E3=83=BC=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E3=82=AF=E3=83=A9=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura/sakura.vcxproj | 3 + sakura/sakura.vcxproj.filters | 9 + sakura_core/basis/CErrorInfo.cpp | 90 +++++++ sakura_core/basis/CErrorInfo.h | 64 +++++ sakura_core/basis/TComImpl.hpp | 91 +++++++ sakura_core/basis/_com_raise_error.cpp | 35 +++ tests/unittests/test-cerrorinfo.cpp | 319 +++++++++++++++++++++++++ tests/unittests/tests1.vcxproj | 1 + tests/unittests/tests1.vcxproj.filters | 3 + 9 files changed, 615 insertions(+) create mode 100644 sakura_core/basis/CErrorInfo.cpp create mode 100644 sakura_core/basis/CErrorInfo.h create mode 100644 sakura_core/basis/TComImpl.hpp create mode 100644 sakura_core/basis/_com_raise_error.cpp create mode 100644 tests/unittests/test-cerrorinfo.cpp diff --git a/sakura/sakura.vcxproj b/sakura/sakura.vcxproj index 32d45c0238..9b9bdc325e 100644 --- a/sakura/sakura.vcxproj +++ b/sakura/sakura.vcxproj @@ -257,6 +257,8 @@ + + @@ -607,6 +609,7 @@ + diff --git a/sakura/sakura.vcxproj.filters b/sakura/sakura.vcxproj.filters index 6e22b49692..eaa38e855e 100644 --- a/sakura/sakura.vcxproj.filters +++ b/sakura/sakura.vcxproj.filters @@ -1094,6 +1094,12 @@ Cpp Source Files + + Cpp Source Files\basis + + + Cpp Source Files\basis + @@ -2273,6 +2279,9 @@ Cpp Source Files + + Cpp Source Files\basis + diff --git a/sakura_core/basis/CErrorInfo.cpp b/sakura_core/basis/CErrorInfo.cpp new file mode 100644 index 0000000000..17392f960f --- /dev/null +++ b/sakura_core/basis/CErrorInfo.cpp @@ -0,0 +1,90 @@ +/*! @file */ +/* + Copyright (C) 2018-2020 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include "StdAfx.h" +#include "basis/CErrorInfo.h" + +/*! + * コンストラクタ + */ +CErrorInfo::CErrorInfo( + const _bstr_t& source, //!< [in] ソース + const _bstr_t& description //!< [in] 説明 +) + : bstrSource_(source) + , bstrDescription_(description) +{ +} + +/*! + * GUIDを取得する + */ +IFACEMETHODIMP CErrorInfo::GetGUID(GUID *pGUID) +{ + if (!pGUID) return E_POINTER; + *pGUID = GUID_NULL; + return S_OK; +} + +/*! + * ソース情報を取得する + */ +IFACEMETHODIMP CErrorInfo::GetSource(BSTR *pBstrSource) +{ + if (!pBstrSource) return E_POINTER; + *pBstrSource = bstrSource_.copy(); + return S_OK; +} + +/*! + * 説明を取得する + */ +IFACEMETHODIMP CErrorInfo::GetDescription(BSTR *pBstrDescription) +{ + if (!pBstrDescription) return E_POINTER; + *pBstrDescription = bstrDescription_.copy(); + return S_OK; +} + +/*! + * ヘルプファイルのパスを取得する + */ +IFACEMETHODIMP CErrorInfo::GetHelpFile(BSTR *pBstrHelpFile) +{ + if (!pBstrHelpFile) return E_POINTER; + _bstr_t bstrHelpFile_; + *pBstrHelpFile = bstrHelpFile_.copy(); + return S_OK; +} + +/*! + * ヘルプコンテキストを取得する + */ +IFACEMETHODIMP CErrorInfo::GetHelpContext(DWORD *pdwHelpContext) +{ + if (!pdwHelpContext) return E_POINTER; + constexpr DWORD dwHelpContext_ = 0; + *pdwHelpContext = dwHelpContext_; + return S_OK; +} diff --git a/sakura_core/basis/CErrorInfo.h b/sakura_core/basis/CErrorInfo.h new file mode 100644 index 0000000000..a55c6bdd51 --- /dev/null +++ b/sakura_core/basis/CErrorInfo.h @@ -0,0 +1,64 @@ +/*! @file */ +/* + Copyright (C) 2018-2020 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#pragma once + +#include +#include + +#include +#include + +#include + +#include "basis/TComImpl.hpp" + +/*! + * @brief COMエラー情報クラス + * + * Windowsのエラー情報を提供するために使用するクラスです。 + * _com_raise_error関数にエラー情報を渡すことにより、日本語メッセージを含む例外を投げることができます。 + * + * @see https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-ierrorinfo + * @see https://docs.microsoft.com/en-us/cpp/cpp/com-raise-error + */ +class CErrorInfo : public TComImpl { + _bstr_t bstrSource_; + _bstr_t bstrDescription_; + +public: + CErrorInfo( + const _bstr_t& source, //!< [in] ソース + const _bstr_t& description //!< [in] 説明 + ); + + IFACEMETHODIMP GetGUID(GUID *pGUID) override; + IFACEMETHODIMP GetSource(BSTR *pBstrSource) override; + IFACEMETHODIMP GetDescription(BSTR *pBstrDescription) override; + IFACEMETHODIMP GetHelpFile(BSTR *pBstrHelpFile) override; + IFACEMETHODIMP GetHelpContext(DWORD *pdwHelpContext) override; +}; + +//! メッセージからエラー情報を生成する +#define MakeMsgError(msg) new CErrorInfo(_CRT_WIDE(__FILE__) L"(" _CRT_WIDE(_CRT_STRINGIZE(__LINE__)) L")", (msg)) diff --git a/sakura_core/basis/TComImpl.hpp b/sakura_core/basis/TComImpl.hpp new file mode 100644 index 0000000000..2cea71ef04 --- /dev/null +++ b/sakura_core/basis/TComImpl.hpp @@ -0,0 +1,91 @@ +/*! @file */ +/* + Copyright (C) 2018-2020 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +/*! + * TComImpl - COMオブジェクトの実装クラステンプレート + * 使用上の注意: + * 1. 生成はnewで行い、deleteはしないでください。 + * 自動変数で生成するとヒープエラーが出ます。 + * 2. 生成したらAddRef()し、不要になったらRelease()してください。 + */ +template, std::enable_if_t, std::nullptr_t> = nullptr> +class TComImpl : public TargetInterface +{ + LONG nRefCount_ = 0; + + using Me = TComImpl; + +public: + TComImpl() = default; + TComImpl(const Me&) = delete; + Me& operator = (const Me&) = delete; + TComImpl(Me&&) = delete; + Me& operator = (Me&&) = delete; + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject) override + { + if (ppvObject == nullptr) { + return E_POINTER; + } + + if (IsEqualIID(iid, __uuidof(TargetInterface)) || + IsEqualIID(iid, IID_IUnknown)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() override + { + return ::InterlockedIncrement(&nRefCount_); + } + + ULONG STDMETHODCALLTYPE Release() override + { + const LONG nRefCount = ::InterlockedDecrement(&nRefCount_); + + if (nRefCount == 0) { + const DeleteorType deletor; + deletor(this); + } + + return nRefCount; + } +}; diff --git a/sakura_core/basis/_com_raise_error.cpp b/sakura_core/basis/_com_raise_error.cpp new file mode 100644 index 0000000000..dc72698aac --- /dev/null +++ b/sakura_core/basis/_com_raise_error.cpp @@ -0,0 +1,35 @@ +/*! @file */ +/* + Copyright (C) 2018-2020 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include "StdAfx.h" +#include +#include + +/*! + MinGW向け_com_raise_error実装 + */ +void _com_raise_error(HRESULT hr, IErrorInfo* pErrorInfo) +{ + throw _com_error(hr, pErrorInfo, true); +} diff --git a/tests/unittests/test-cerrorinfo.cpp b/tests/unittests/test-cerrorinfo.cpp new file mode 100644 index 0000000000..221b941a0a --- /dev/null +++ b/tests/unittests/test-cerrorinfo.cpp @@ -0,0 +1,319 @@ +/*! @file */ +/* + Copyright (C) 2018-2020 Sakura Editor Organization + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include + +#ifndef NOMINMAX +#define NOMINMAX +#endif /* #ifndef NOMINMAX */ + +#include +#include + +#include + +#include "basis/CErrorInfo.h" + +/*! + * @brief テンプレートクラスの機能確認 + */ +TEST(TComImpl, AddRef_Release) +{ + // IErrorInfoを生成する + IErrorInfo* pErrorInfo(reinterpret_cast(MakeMsgError(L"test"))); + + // 初期値は0なので、AddRefすると1が返る + ASSERT_EQ(1, pErrorInfo->AddRef()); + + // 内部カウンタは1なので、Releaseすると0になる + ASSERT_EQ(0, pErrorInfo->Release()); +} + +/*! + * @brief テンプレートクラスの機能確認 + */ +TEST(TComImpl, QueryInterface_BadPointer) +{ + // IErrorInfoを生成する + IErrorInfo* pErrorInfo(reinterpret_cast(MakeMsgError(L"test"))); + + // 初期値は0なので、AddRefすると1が返る + ASSERT_EQ(1, pErrorInfo->AddRef()); + + // QueryInterfaceのOutポインタにnullptrを指定するとポインタエラーになる + ASSERT_EQ(E_POINTER, pErrorInfo->QueryInterface(GUID_NULL, nullptr)); + + // 内部カウンタは1なので、Releaseすると0になる + ASSERT_EQ(0, pErrorInfo->Release()); +} + +/*! + * @brief テンプレートクラスの機能確認 + */ +TEST(TComImpl, QueryInterface_IUnknown) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + IErrorInfo* pErrorInfo(reinterpret_cast(MakeMsgError(L"test"))); + + // 初期値は0なので、AddRefすると1が返る + ASSERT_EQ(1, pErrorInfo->AddRef()); + + // IUnknownを取得する + ComPtr pUnknown; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->QueryInterface(pUnknown.GetAddressOf()))); + + //pUnknownを解放する + pUnknown = nullptr; + + // 内部カウンタは1に戻っているはずなので、Releaseすると0になる + ASSERT_EQ(0, pErrorInfo->Release()); +} + +/*! + * @brief テンプレートクラスの機能確認 + */ +TEST(TComImpl, QueryInterface_Imlemented) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + IErrorInfo* pErrorInfo(reinterpret_cast(MakeMsgError(L"test"))); + + // 初期値は0なので、AddRefすると1が返る + ASSERT_EQ(1, pErrorInfo->AddRef()); + + // IErrorInfoを取得する + ComPtr pOther; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->QueryInterface(pOther.GetAddressOf()))); + + //pOtherを解放する + pOther = nullptr; + + // 内部カウンタは1に戻っているはずなので、Releaseすると0になる + ASSERT_EQ(0, pErrorInfo->Release()); +} + +/*! + * @brief テンプレートクラスの機能確認 + */ +TEST(TComImpl, QueryInterface_NotImlemented) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + IErrorInfo* pErrorInfo(reinterpret_cast(MakeMsgError(L"test"))); + + // 初期値は0なので、AddRefすると1が返る + ASSERT_EQ(1, pErrorInfo->AddRef()); + + // ICreateErrorInfo(実装されていないインターフェース)の取得を試みる + ComPtr pCreateErrorInfo; + ASSERT_EQ(E_NOINTERFACE, pErrorInfo->QueryInterface(pCreateErrorInfo.GetAddressOf())); + + // 内部カウンタは1なので、Releaseすると0になる + ASSERT_EQ(0, pErrorInfo->Release()); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetGUID_BadPointer) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // GUIDを取得する + ASSERT_EQ(E_POINTER, pErrorInfo->GetGUID(nullptr)); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetGUID) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // guidを取得する + GUID guid = GUID_NULL; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->GetGUID(&guid))); + ASSERT_EQ(GUID_NULL, guid); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetSource_BadPointer) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ソース情報を取得する + ASSERT_EQ(E_POINTER, pErrorInfo->GetSource(nullptr)); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetSource) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ソース情報を取得する + _bstr_t bstrSource; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->GetSource(bstrSource.GetAddress()))); + // 期待値を作るのが面倒なので比較は省略 +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetDescription_BadPointer) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // 説明を取得する + ASSERT_EQ(E_POINTER, pErrorInfo->GetDescription(nullptr)); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetDescription) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // 説明を取得する + _bstr_t bstrDescription; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->GetDescription(bstrDescription.GetAddress()))); + ASSERT_STREQ(msg, (const wchar_t*)bstrDescription); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetHelpFile_BadPointer) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ヘルプファイルのパスを取得する + ASSERT_EQ(E_POINTER, pErrorInfo->GetHelpFile(nullptr)); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetHelpFile) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ヘルプファイルのパスを取得する + _bstr_t bstrHelpFile; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->GetHelpFile(bstrHelpFile.GetAddress()))); + ASSERT_EQ(nullptr, (const wchar_t*)bstrHelpFile); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetHelpContext_BadPointer) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ヘルプコンテキストを取得する + ASSERT_EQ(E_POINTER, pErrorInfo->GetHelpContext(nullptr)); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, GetHelpContext) +{ + using namespace Microsoft::WRL; + + // IErrorInfoを生成する + constexpr const wchar_t msg[] = L"test"; + ComPtr pErrorInfo(reinterpret_cast(MakeMsgError(msg))); + + // ヘルプコンテキストを取得する + DWORD dwHelpContext; + ASSERT_TRUE(SUCCEEDED(pErrorInfo->GetHelpContext(&dwHelpContext))); + ASSERT_EQ(0, dwHelpContext); +} + +/*! + * @brief COMエラークラスの機能確認 + */ +TEST(CErrorInfo, StandardUsageTest) +{ + // テストに使う日本語メッセージ + constexpr const wchar_t message[] = L"エラーが発生しました!"; + + try { + // エラー情報を生成して例外を投げる + ::_com_raise_error(E_FAIL, MakeMsgError(message)); + + // 例外を投げたあとのコードは実行されない + FAIL(); + } + catch (const _com_error& ce) { + // 投げられた例外メッセージを取得できること + ASSERT_STREQ(message, (const wchar_t*)ce.Description()); + } +} diff --git a/tests/unittests/tests1.vcxproj b/tests/unittests/tests1.vcxproj index 9297387262..9418602a93 100644 --- a/tests/unittests/tests1.vcxproj +++ b/tests/unittests/tests1.vcxproj @@ -108,6 +108,7 @@ + diff --git a/tests/unittests/tests1.vcxproj.filters b/tests/unittests/tests1.vcxproj.filters index 491557c51e..a905c59d02 100644 --- a/tests/unittests/tests1.vcxproj.filters +++ b/tests/unittests/tests1.vcxproj.filters @@ -98,6 +98,9 @@ Test Files + + Test Files +