diff --git a/src/Shared/HStringPrivate.h b/src/Shared/HStringPrivate.h new file mode 100644 index 0000000..a715736 --- /dev/null +++ b/src/Shared/HStringPrivate.h @@ -0,0 +1,62 @@ +/* + * PROJECT: YY-Thunks + * FILE: hstring_private.h + * PURPOSE: Extra definitions for the Windows Runtime String (HSTRING) + * + * LICENSE: The MIT License + * + * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) + */ +#pragma once +#include + +#include "SharedDefs.h" + +/* + * @remark The following definitions are dumped from the debug symbol from the + * Windows 10 Build 14347's combase.dll. + */ +namespace YY::Thunks::internal +{ + namespace + { + /* + * @brief The flags of the Windows Runtime String. + * @remark Originally it's a C/C++ enum in the debug symbols. + */ + enum class RuntimeStringFlags : UINT32 + { + WRHF_NONE = 0x00000000, + WRHF_STRING_REFERENCE = 0x00000001, + WRHF_VALID_UNICODE_FORMAT_INFO = 0x00000002, + WRHF_WELL_FORMED_UNICODE = 0x00000004, + WRHF_HAS_EMBEDDED_NULLS = 0x00000008, + WRHF_EMBEDDED_NULLS_COMPUTED = 0x00000010, + WRHF_RESERVED_FOR_PREALLOCATED_STRING_BUFFER = 0x80000000, + }; + YY_APPLY_ENUM_CALSS_BIT_OPERATOR(RuntimeStringFlags) + + /* + * @brief The internal structure of Windows Runtime String header. + */ + typedef struct _HSTRING_HEADER_INTERNAL + { + RuntimeStringFlags Flags; + UINT32 Length; + UINT32 Padding1; + UINT32 Padding2; + PCWSTR StringRef; + } HSTRING_HEADER_INTERNAL, * PHSTRING_HEADER_INTERNAL; + static_assert(sizeof(HSTRING_HEADER) == sizeof(HSTRING_HEADER_INTERNAL)); + + /* + * @brief The internal structure of heap allocated Windows Runtime String. + */ + typedef struct _STRING_OPAQUE + { + HSTRING_HEADER_INTERNAL Header; + volatile LONG RefCount; + WCHAR String[ANYSIZE_ARRAY]; + } STRING_OPAQUE, * PSTRING_OPAQUE; + } +} diff --git a/src/Shared/SharedDefs.h b/src/Shared/SharedDefs.h new file mode 100644 index 0000000..b19d3ac --- /dev/null +++ b/src/Shared/SharedDefs.h @@ -0,0 +1,35 @@ +#pragma once +#include + +#define YY_APPLY_ENUM_CALSS_BIT_OPERATOR(_ENUM) \ + inline constexpr _ENUM& operator|=(_ENUM& _eLeft, _ENUM _eRight) \ + { \ + using _Type = std::underlying_type<_ENUM>::type; \ + (_Type&)_eLeft |= (_Type)_eRight; \ + return _eLeft; \ + } \ + \ + inline constexpr _ENUM operator|(_ENUM _eLeft, _ENUM _eRight) \ + { \ + using _Type = std::underlying_type<_ENUM>::type; \ + auto _Result = (_Type)_eLeft | (_Type)_eRight; \ + return _ENUM(_Result); \ + } \ + \ + inline constexpr _ENUM operator&(_ENUM _eLeft, _ENUM _eRight) \ + { \ + using _Type = std::underlying_type<_ENUM>::type; \ + return _ENUM((_Type)_eLeft & (_Type)_eRight); \ + } \ + \ + inline constexpr _ENUM operator~(_ENUM _eLeft) \ + { \ + using _Type = std::underlying_type<_ENUM>::type; \ + return _ENUM(~(_Type)_eLeft); \ + } \ + \ + inline constexpr bool HasFlags(_ENUM _eLeft, _ENUM _eRight) \ + { \ + using _Type = std::underlying_type<_ENUM>::type; \ + return (_Type)_eLeft & (_Type)_eRight; \ + } diff --git a/src/Shared/hstring_private.h b/src/Shared/hstring_private.h deleted file mode 100644 index 4da0039..0000000 --- a/src/Shared/hstring_private.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * PROJECT: YY-Thunks - * FILE: hstring_private.h - * PURPOSE: Extra definitions for the Windows Runtime String (HSTRING) - * - * LICENSE: The MIT License - * - * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) - */ - -#ifndef YY_THUNKS_HSTRING_PRIVATE -#define YY_THUNKS_HSTRING_PRIVATE - -#include - -/* - * @remark The following definitions are dumped from the debug symbol from the - * Windows 10 Build 14347's combase.dll. - */ -namespace YY::Thunks::internal -{ - /* - * @brief The flags of the Windows Runtime String. - * @remark Originally it's a C/C++ enum in the debug symbols. But we changed - * the definition to macro for reducing the C++ implicit cast - * operations to make both compiler and maintainer happily. - */ - #ifndef WINDOWS_RUNTIME_HSTRING_FLAGS - #define WINDOWS_RUNTIME_HSTRING_FLAGS - - #define WRHF_NONE 0x00000000 - #define WRHF_STRING_REFERENCE 0x00000001 - #define WRHF_VALID_UNICODE_FORMAT_INFO 0x00000002 - #define WRHF_WELL_FORMED_UNICODE 0x00000004 - #define WRHF_HAS_EMBEDDED_NULLS 0x00000008 - #define WRHF_EMBEDDED_NULLS_COMPUTED 0x00000010 - #define WRHF_RESERVED_FOR_PREALLOCATED_STRING_BUFFER 0x80000000 - - #endif // !WINDOWS_RUNTIME_HSTRING_FLAGS - - /* - * @brief The internal structure of Windows Runtime String header. - */ - typedef struct _HSTRING_HEADER_INTERNAL - { - UINT32 Flags; - UINT32 Length; - UINT32 Padding1; - UINT32 Padding2; - PCWSTR StringRef; - } HSTRING_HEADER_INTERNAL, *PHSTRING_HEADER_INTERNAL; - static_assert(sizeof(HSTRING_HEADER) == sizeof(HSTRING_HEADER_INTERNAL)); - - /* - * @brief The internal structure of heap allocated Windows Runtime String. - */ - typedef struct _STRING_OPAQUE - { - HSTRING_HEADER_INTERNAL Header; - volatile INT RefCount; - WCHAR String[ANYSIZE_ARRAY]; - } STRING_OPAQUE, *PSTRING_OPAQUE; -} - -#endif // !YY_THUNKS_HSTRING_PRIVATE diff --git a/src/Thunks/YY_Thunks.cpp b/src/Thunks/YY_Thunks.cpp index 7e71cce..55b667f 100644 --- a/src/Thunks/YY_Thunks.cpp +++ b/src/Thunks/YY_Thunks.cpp @@ -965,7 +965,7 @@ namespace YY::Thunks::internal static constexpr UNICODE_STRING __fastcall MakeNtString(_In_z_ const wchar_t* _szString) { const auto _cbString = StringLength(_szString) * sizeof(_szString[0]); - UNICODE_STRING _Result = { (USHORT)max(UINT16_MAX, _cbString), (USHORT)max(UINT16_MAX, _cbString + sizeof(_szString[0])), const_cast(_szString) }; + UNICODE_STRING _Result = { (USHORT)min(UINT16_MAX, _cbString), (USHORT)min(UINT16_MAX, _cbString + sizeof(_szString[0])), const_cast(_szString) }; return _Result; } @@ -973,7 +973,7 @@ namespace YY::Thunks::internal { const auto _cbString = StringLength(_szString) * sizeof(_szString[0]); - ANSI_STRING _Result = { (USHORT)max(UINT16_MAX, _cbString), (USHORT)max(UINT16_MAX, _cbString + sizeof(_szString[0])), const_cast(_szString)}; + ANSI_STRING _Result = { (USHORT)min(UINT16_MAX, _cbString), (USHORT)min(UINT16_MAX, _cbString + sizeof(_szString[0])), const_cast(_szString)}; return _Result; } diff --git a/src/Thunks/api-ms-win-core-winrt-string.hpp b/src/Thunks/api-ms-win-core-winrt-string.hpp index 7c6df76..867df26 100644 --- a/src/Thunks/api-ms-win-core-winrt-string.hpp +++ b/src/Thunks/api-ms-win-core-winrt-string.hpp @@ -1,5 +1,5 @@ #if (YY_Thunks_Support_Version < NTDDI_WIN8) -#include +#include #include #endif @@ -22,52 +22,47 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsCreateString, - _In_reads_opt_(length) PCNZWCH sourceString, - UINT32 length, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string + _In_reads_opt_(_cchLength) PCNZWCH _sSourceString, + UINT32 _cLength, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phString ) { - if (auto const pWindowsCreateString = try_get_WindowsCreateString()) + if (auto const _pfnWindowsCreateString = try_get_WindowsCreateString()) { - return pWindowsCreateString(sourceString, length, string); + return _pfnWindowsCreateString(_sSourceString, _cLength, _phString); } - if (!string) + if (!_phString) { return E_INVALIDARG; } - *string = nullptr; - if (!sourceString && 0 != length) + *_phString = nullptr; + if (_sSourceString == nullptr && 0 != _cLength) { return E_POINTER; } - SIZE_T RequiredSize = sizeof(internal::STRING_OPAQUE) + (length + 1) * sizeof(WCHAR); - if (MAXUINT32 < RequiredSize) + const uint64_t _cbRequiredSize = sizeof(internal::STRING_OPAQUE) + uint64_t(_cLength + 1) * sizeof(WCHAR); + if (MAXUINT32 < _cbRequiredSize) { return MEM_E_INVALID_SIZE; } - internal::PSTRING_OPAQUE Result = - reinterpret_cast(::HeapAlloc( - ::GetProcessHeap(), - HEAP_ZERO_MEMORY, - RequiredSize)); - if (!Result) + auto _pStringInternal = reinterpret_cast(internal::Alloc(_cbRequiredSize, HEAP_ZERO_MEMORY)); + if (!_pStringInternal) { return E_OUTOFMEMORY; } - Result->Header.Flags = WRHF_NONE; - Result->Header.Length = length; - Result->Header.StringRef = Result->String; - Result->RefCount = 1; - ::memcpy(Result->String, sourceString, length * sizeof(WCHAR)); - Result->String[length] = L'\0'; - - *string = reinterpret_cast(Result); + _pStringInternal->Header.Flags = internal::RuntimeStringFlags::WRHF_NONE; + _pStringInternal->Header.Length = _cLength; + _pStringInternal->Header.StringRef = _pStringInternal->String; + _pStringInternal->RefCount = 1; + ::memcpy(_pStringInternal->String, _sSourceString, _cLength * sizeof(WCHAR)); + _pStringInternal->String[_cLength] = L'\0'; + *_phString = reinterpret_cast(_pStringInternal); return S_OK; } #endif @@ -83,46 +78,43 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsCreateStringReference, - _In_reads_opt_(length + 1) PCWSTR sourceString, - UINT32 length, - _Out_ HSTRING_HEADER* hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string + _In_reads_opt_(_cLength + 1) PCWSTR _szSourceString, + UINT32 _cLength, + _Out_ HSTRING_HEADER* _phStringHeader, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phString ) { - if (auto const pWindowsCreateStringReference = try_get_WindowsCreateStringReference()) + if (auto const pfnWindowsCreateStringReference = try_get_WindowsCreateStringReference()) { - return pWindowsCreateStringReference(sourceString, length, hstringHeader, string); + return pfnWindowsCreateStringReference(_szSourceString, _cLength, _phStringHeader, _phString); } - if (!string || !hstringHeader) + if (_phString == nullptr || _phStringHeader == nullptr) { return E_INVALIDARG; } - *string = nullptr; + *_phString = nullptr; - if (!sourceString && 0 != length) + if (_szSourceString == nullptr && 0 != _cLength) { return E_POINTER; } - if (sourceString) + if (_szSourceString) { - if (L'0' != sourceString[length]) + if (L'\0' != _szSourceString[_cLength]) { return E_STRING_NOT_NULL_TERMINATED; } } - internal::PHSTRING_HEADER_INTERNAL Header = - reinterpret_cast(hstringHeader); - ::memset(Header, 0, sizeof(internal::HSTRING_HEADER_INTERNAL)); - - Header->Flags = WRHF_STRING_REFERENCE; - Header->Length = length; - Header->StringRef = sourceString; - - *string = reinterpret_cast(Header); + auto _pHeader = reinterpret_cast(_phStringHeader); + ::memset(_pHeader, 0, sizeof(*_pHeader)); + _pHeader->Flags = internal::RuntimeStringFlags::WRHF_STRING_REFERENCE; + _pHeader->Length = _cLength; + _pHeader->StringRef = _szSourceString; + *_phString = reinterpret_cast(_pHeader); return S_OK; } #endif @@ -138,27 +130,25 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsDeleteString, - _In_opt_ HSTRING string + _In_opt_ HSTRING _hString ) { - if (auto const pWindowsDeleteString = try_get_WindowsDeleteString()) + if (auto const _pfnWindowsDeleteString = try_get_WindowsDeleteString()) { - return pWindowsDeleteString(string); + return _pfnWindowsDeleteString(_hString); } - if (string) - { - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - if (!(OpaqueString->Header.Flags & WRHF_STRING_REFERENCE)) - { - if (0 == ::InterlockedDecrement( - reinterpret_cast(&OpaqueString->RefCount))) - { - ::HeapFree(::GetProcessHeap(), 0, OpaqueString); - } - } - } + if (!_hString) + return S_OK; + + auto _pStringInternal = reinterpret_cast(_hString); + + // WRHF_STRING_REFERENCE 表示内存被调用者接管,所以我们无需从栈空间释放它。 + if (internal::HasFlags(_pStringInternal->Header.Flags, internal::RuntimeStringFlags::WRHF_STRING_REFERENCE)) + return S_OK; + + if (0 == ::InterlockedDecrement(&_pStringInternal->RefCount)) + internal::Free(_pStringInternal); return S_OK; } @@ -175,39 +165,33 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsDuplicateString, - _In_opt_ HSTRING string, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* newString + _In_opt_ HSTRING _hString, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phNewString ) { - if (auto const pWindowsDuplicateString = try_get_WindowsDuplicateString()) + if (auto const _pfnWindowsDuplicateString = try_get_WindowsDuplicateString()) { - return pWindowsDuplicateString(string, newString); + return _pfnWindowsDuplicateString(_hString, _phNewString); } - if (!newString) - { + if (!_phNewString) return E_INVALIDARG; - } - *newString = nullptr; - if (string) - { - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - if (OpaqueString->Header.Flags & WRHF_STRING_REFERENCE) - { - return ::WindowsCreateString( - OpaqueString->Header.StringRef, - OpaqueString->Header.Length, - newString); - } + *_phNewString = nullptr; + if (!_hString) + return S_OK; - ::InterlockedIncrement( - reinterpret_cast(&OpaqueString->RefCount)); - *newString = string; + auto _pStringInternal = reinterpret_cast(_hString); + if (!internal::HasFlags(_pStringInternal->Header.Flags, internal::RuntimeStringFlags::WRHF_STRING_REFERENCE)) + { + ::InterlockedIncrement(&_pStringInternal->RefCount); + *_phNewString = _hString; + return S_OK; } - return S_OK; + // WRHF_STRING_REFERENCE 时内存生命周期被调用者接管,无法简单增加引用计数延长生命周期。 + // 所以需要使用 WindowsCreateString 创建副本。 + return ::WindowsCreateString(_pStringInternal->Header.StringRef, _pStringInternal->Header.Length, _phNewString); } #endif @@ -222,22 +206,19 @@ namespace YY::Thunks UINT32, STDAPICALLTYPE, WindowsGetStringLen, - _In_opt_ HSTRING string + _In_opt_ HSTRING _hString ) { - if (auto const pWindowsGetStringLen = try_get_WindowsGetStringLen()) + if (auto const _pfnWindowsGetStringLen = try_get_WindowsGetStringLen()) { - return pWindowsGetStringLen(string); + return _pfnWindowsGetStringLen(_hString); } - if (string) - { - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - return OpaqueString->Header.Length; - } + if (!_hString) + return 0ul; - return 0; + auto _pHeader = reinterpret_cast(_hString); + return _pHeader->Length; } #endif @@ -252,33 +233,32 @@ namespace YY::Thunks PCWSTR, STDAPICALLTYPE, WindowsGetStringRawBuffer, - _In_opt_ HSTRING string, - _Out_opt_ UINT32* length + _In_opt_ HSTRING _hString, + _Out_opt_ UINT32* _pcLength ) { - if (auto const pWindowsGetStringRawBuffer = try_get_WindowsGetStringRawBuffer()) + if (auto const _pfnWindowsGetStringRawBuffer = try_get_WindowsGetStringRawBuffer()) { - return pWindowsGetStringRawBuffer(string, length); + return _pfnWindowsGetStringRawBuffer(_hString, _pcLength); } - if (!string) + if (!_hString) { - if (length) + if (_pcLength) { - *length = 0; + *_pcLength = 0; } - return L"\0"; + return L""; } - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - if (length) + auto _pHeader = reinterpret_cast(_hString); + if (_pcLength) { - *length = OpaqueString->Header.Length; + *_pcLength = _pHeader->Length; } - return OpaqueString->Header.StringRef; + return _pHeader->StringRef; } #endif @@ -293,22 +273,16 @@ namespace YY::Thunks BOOL, STDAPICALLTYPE, WindowsIsStringEmpty, - _In_opt_ HSTRING string + _In_opt_ HSTRING _hString ) { - if (auto const pWindowsIsStringEmpty = try_get_WindowsIsStringEmpty()) + if (auto const _pfnWindowsIsStringEmpty = try_get_WindowsIsStringEmpty()) { - return pWindowsIsStringEmpty(string); + return _pfnWindowsIsStringEmpty(_hString); } - if (string) - { - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - return 0 == OpaqueString->Header.Length; - } - - return TRUE; + auto _pHeader = reinterpret_cast(_hString); + return _pHeader == nullptr || 0ul == _pHeader->Length; } #endif @@ -323,44 +297,46 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsStringHasEmbeddedNull, - _In_opt_ HSTRING string, - _Out_ BOOL* hasEmbedNull + _In_opt_ HSTRING _hString, + _Out_ BOOL* _pbHasEmbedNull ) { - if (auto const pWindowsStringHasEmbeddedNull = try_get_WindowsStringHasEmbeddedNull()) + if (auto const _pfnWindowsStringHasEmbeddedNull = try_get_WindowsStringHasEmbeddedNull()) { - return pWindowsStringHasEmbeddedNull(string, hasEmbedNull); + return _pfnWindowsStringHasEmbeddedNull(_hString, _pbHasEmbedNull); } - if (!hasEmbedNull) - { + if (!_pbHasEmbedNull) return E_INVALIDARG; + + *_pbHasEmbedNull = FALSE; + if (!_hString) + return S_OK; + + auto _pHeader = reinterpret_cast(_hString); + + // 已经存在缓存,直接从缓存获取 WRHF_HAS_EMBEDDED_NULLS 即可 + // 这样做是合理的,因为微软将HSTRING定义为一个只读的String。 + if (internal::HasFlags(_pHeader->Flags, internal::RuntimeStringFlags::WRHF_EMBEDDED_NULLS_COMPUTED)) + { + *_pbHasEmbedNull = internal::HasFlags(_pHeader->Flags, internal::RuntimeStringFlags::WRHF_HAS_EMBEDDED_NULLS); + return S_OK; } - *hasEmbedNull = FALSE; - if (string) + auto _szEnd = _pHeader->StringRef + _pHeader->Length; + for (auto _szStr = _pHeader->StringRef; _szStr < _szEnd; ++_szStr) { - internal::PSTRING_OPAQUE OpaqueString = - reinterpret_cast(string); - if (!(OpaqueString->Header.Flags & WRHF_EMBEDDED_NULLS_COMPUTED)) + if (*_szStr == L'\0') { - for (UINT32 i = 0; ; ++i) - { - if (i >= OpaqueString->Header.Length) - { - OpaqueString->Header.Flags |= WRHF_EMBEDDED_NULLS_COMPUTED; - break; - } - else if (OpaqueString->Header.StringRef[i] == L'\0') - { - OpaqueString->Header.Flags |= WRHF_EMBEDDED_NULLS_COMPUTED | WRHF_HAS_EMBEDDED_NULLS; - break; - } - } + constexpr auto kHasEmbedNullFlags = internal::RuntimeStringFlags::WRHF_EMBEDDED_NULLS_COMPUTED | internal::RuntimeStringFlags::WRHF_HAS_EMBEDDED_NULLS; + InterlockedOr((LONG*)&_pHeader->Flags, LONG(kHasEmbedNullFlags)); + *_pbHasEmbedNull = TRUE; + return S_OK; } - *hasEmbedNull = OpaqueString->Header.Flags & WRHF_HAS_EMBEDDED_NULLS; } + InterlockedOr((LONG*)&_pHeader->Flags, LONG(internal::RuntimeStringFlags::WRHF_EMBEDDED_NULLS_COMPUTED)); + *_pbHasEmbedNull = FALSE; return S_OK; } #endif @@ -376,65 +352,43 @@ namespace YY::Thunks HRESULT, STDAPICALLTYPE, WindowsCompareStringOrdinal, - _In_opt_ HSTRING string1, - _In_opt_ HSTRING string2, - _Out_ INT32* result + _In_opt_ HSTRING _hStringLeft, + _In_opt_ HSTRING _hStringRigth, + _Out_ INT32* _pnResult ) { - if (auto const pWindowsCompareStringOrdinal = try_get_WindowsCompareStringOrdinal()) + if (auto const _pfnWindowsCompareStringOrdinal = try_get_WindowsCompareStringOrdinal()) { - return pWindowsCompareStringOrdinal(string1, string2, result); + return _pfnWindowsCompareStringOrdinal(_hStringLeft, _hStringRigth, _pnResult); } - if (!result) - { + if (!_pnResult) return E_INVALIDARG; - } - *result = 0; - if (string1 && string2) + *_pnResult = 0; + const auto _hHeaderLeft = reinterpret_cast(_hStringLeft); + const auto _hHeaderRigth = reinterpret_cast(_hStringRigth); + if (_hHeaderLeft && _hHeaderLeft->Length) { - internal::PSTRING_OPAQUE OpaqueString1 = - reinterpret_cast(string1); - internal::PSTRING_OPAQUE OpaqueString2 = - reinterpret_cast(string2); - - int CompareResult = ::CompareStringOrdinal( - OpaqueString1->Header.StringRef, - OpaqueString1->Header.Length, - OpaqueString2->Header.StringRef, - OpaqueString2->Header.Length, - FALSE); - if (CompareResult == CSTR_LESS_THAN) - { - *result = -1; - } - else if (CompareResult == CSTR_GREATER_THAN) + if (_hHeaderRigth && _hHeaderRigth->Length) { - *result = 1; + const auto _nResult = ::CompareStringOrdinal(_hHeaderLeft->StringRef, _hHeaderLeft->Length, _hHeaderRigth->StringRef, _hHeaderRigth->Length, FALSE); + if (_nResult) + { + *_pnResult = _nResult - CSTR_EQUAL; + } } - } - else if (string1 && !string2) - { - internal::PSTRING_OPAQUE OpaqueString1 = - reinterpret_cast(string1); - if (OpaqueString1->Header.Length) + else { - *result = 1; + *_pnResult = 1; } } - else if (!string1 && string2) + else { - internal::PSTRING_OPAQUE OpaqueString2 = - reinterpret_cast(string2); - if (OpaqueString2->Header.Length) - { - *result = 1; - } + *_pnResult = _hHeaderRigth && _hHeaderRigth->Length ? -1 : 0; } return S_OK; } #endif - } diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 61c2e54..35d5de7 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj @@ -43,6 +43,7 @@ true Unicode false + true DynamicLibrary @@ -59,6 +60,7 @@ true Unicode false + true @@ -105,6 +107,7 @@ 4715;%(TreatSpecificWarningsAsErrors) stdcpp17 /execution-charset:utf-8 %(AdditionalOptions) + Disabled Windows @@ -169,6 +172,7 @@ 4715;%(TreatSpecificWarningsAsErrors) stdcpp17 /execution-charset:utf-8 %(AdditionalOptions) + Disabled Windows @@ -215,11 +219,11 @@ - - + + diff --git a/src/YY-Thunks.UnitTest/api-ms-win-core-winrt-string.UnitTest.cpp b/src/YY-Thunks.UnitTest/api-ms-win-core-winrt-string.UnitTest.cpp index f3447bd..4a7c368 100644 --- a/src/YY-Thunks.UnitTest/api-ms-win-core-winrt-string.UnitTest.cpp +++ b/src/YY-Thunks.UnitTest/api-ms-win-core-winrt-string.UnitTest.cpp @@ -3,6 +3,96 @@ namespace api_ms_win_core_winrt_string { + TEST_CLASS(WindowsCreateString) + { + AwaysNullGuard Guard; + + public: + WindowsCreateString() + { + Guard |= YY::Thunks::aways_null_try_get_WindowsCreateString; + Guard |= YY::Thunks::aways_null_try_get_WindowsDeleteString; + Guard |= YY::Thunks::aways_null_try_get_WindowsGetStringRawBuffer; + } + + TEST_METHOD(标准C字符串) + { + const wchar_t _szInput[] = L"TestString"; + const auto _cInput = _countof(_szInput) - 1; + + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szInput, sizeof(_szInput)) == 0); + + ::WindowsDeleteString(_hString); + } + + TEST_METHOD(中途带零的字符串) + { + const wchar_t _szInput[] = L"Test\0String"; + const auto _cInput = _countof(_szInput) - 1; + + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szInput, sizeof(_szInput)) == 0); + + ::WindowsDeleteString(_hString); + } + + TEST_METHOD(结尾多一个零的字符串) + { + const wchar_t _szInput[] = L"TestString\0"; + const auto _cInput = _countof(_szInput) - 1; + + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szInput, sizeof(_szInput)) == 0); + + ::WindowsDeleteString(_hString); + } + + TEST_METHOD(中途截断的字符串) + { + const wchar_t _szInput[] = L"TestString"; + const wchar_t _szExpect[] = L"Test"; + const size_t _cInput = _countof(_szExpect) - 1; + + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szExpect, sizeof(_szExpect)) == 0); + + ::WindowsDeleteString(_hString); + } + }; + TEST_CLASS(WindowsStringHasEmbeddedNull) { AwaysNullGuard Guard; @@ -11,49 +101,263 @@ namespace api_ms_win_core_winrt_string WindowsStringHasEmbeddedNull() { Guard |= YY::Thunks::aways_null_try_get_WindowsCreateString; + Guard |= YY::Thunks::aways_null_try_get_WindowsDeleteString; Guard |= YY::Thunks::aways_null_try_get_WindowsStringHasEmbeddedNull; } TEST_METHOD(标准C字符串) { - wchar_t RawString[] = L"TestString"; - const UINT32 Length = static_cast(sizeof(RawString) / sizeof(*RawString)); + const wchar_t _szInput[] = L"TestString"; + const auto _cInput = _countof(_szInput) - 1; - HSTRING String = nullptr; - HRESULT hr = ::WindowsCreateString(RawString, Length, &String); + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); Assert::AreEqual(hr, (HRESULT)S_OK); - BOOL Result = FALSE; - hr = ::WindowsStringHasEmbeddedNull(String, &Result); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); Assert::AreEqual(hr, (HRESULT)S_OK); - Assert::IsTrue(Result); + Assert::IsFalse(_bEmbeddedNull); + + ::WindowsDeleteString(_hString); } TEST_METHOD(中途带零的字符串) { - wchar_t RawString[] = L"Test\0String"; - const UINT32 Length = static_cast(sizeof(RawString) / sizeof(*RawString)); + const wchar_t _szInput[] = L"Test\0String"; + const auto _cInput = _countof(_szInput) - 1; - HSTRING String = nullptr; - HRESULT hr = ::WindowsCreateString(RawString, Length, &String); + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); Assert::AreEqual(hr, (HRESULT)S_OK); - BOOL Result = FALSE; - hr = ::WindowsStringHasEmbeddedNull(String, &Result); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); Assert::AreEqual(hr, (HRESULT)S_OK); - Assert::IsTrue(Result); + Assert::IsTrue(_bEmbeddedNull); + + ::WindowsDeleteString(_hString); } - TEST_METHOD(不带零的字符串) + TEST_METHOD(结尾多一个零的字符串) { - wchar_t RawString[] = L"TestString"; - const UINT32 Length = static_cast(sizeof(RawString) / sizeof(*RawString)) -1; + const wchar_t _szInput[] = L"TestString\0"; + const auto _cInput = _countof(_szInput) - 1; - HSTRING String = nullptr; - HRESULT hr = ::WindowsCreateString(RawString, Length, &String); + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); Assert::AreEqual(hr, (HRESULT)S_OK); - BOOL Result = FALSE; - hr = ::WindowsStringHasEmbeddedNull(String, &Result); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); Assert::AreEqual(hr, (HRESULT)S_OK); - Assert::IsFalse(Result); + Assert::IsTrue(_bEmbeddedNull); + + ::WindowsDeleteString(_hString); } }; + + TEST_CLASS(WindowsCompareStringOrdinal) + { + AwaysNullGuard Guard; + + public: + WindowsCompareStringOrdinal() + { + Guard |= YY::Thunks::aways_null_try_get_WindowsCreateStringReference; + Guard |= YY::Thunks::aways_null_try_get_WindowsDeleteString; + Guard |= YY::Thunks::aways_null_try_get_WindowsCompareStringOrdinal; + } + + TEST_METHOD(常规验证) + { + { + HSTRING_HEADER _HeaderLeft; + HSTRING _hStringLeft; + auto hr = WindowsCreateStringReference(L"123", 3, &_HeaderLeft, &_hStringLeft); + Assert::AreEqual(hr, (HRESULT)S_OK); + + HSTRING_HEADER _HeaderRigth; + HSTRING _hStringRigth; + hr = WindowsCreateStringReference(L"123", 3, &_HeaderRigth, &_hStringRigth); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(_hStringLeft, _hStringRigth, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, 0); + + WindowsDeleteString(_hStringLeft); + WindowsDeleteString(_hStringRigth); + } + + { + HSTRING_HEADER _HeaderLeft; + HSTRING _hStringLeft; + auto hr = WindowsCreateStringReference(L"1234", 4, &_HeaderLeft, &_hStringLeft); + Assert::AreEqual(hr, (HRESULT)S_OK); + + HSTRING_HEADER _HeaderRigth; + HSTRING _hStringRigth; + hr = WindowsCreateStringReference(L"123", 3, &_HeaderRigth, &_hStringRigth); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(_hStringLeft, _hStringRigth, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, 1); + + WindowsDeleteString(_hStringLeft); + WindowsDeleteString(_hStringRigth); + } + + { + HSTRING_HEADER _HeaderLeft; + HSTRING _hStringLeft; + auto hr = WindowsCreateStringReference(L"123", 3, &_HeaderLeft, &_hStringLeft); + Assert::AreEqual(hr, (HRESULT)S_OK); + + HSTRING_HEADER _HeaderRigth; + HSTRING _hStringRigth; + hr = WindowsCreateStringReference(L"1234", 4, &_HeaderRigth, &_hStringRigth); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(_hStringLeft, _hStringRigth, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, -1); + + WindowsDeleteString(_hStringLeft); + WindowsDeleteString(_hStringRigth); + } + } + + TEST_METHOD(空值验证) + { + { + HSTRING_HEADER _HeaderLeft; + HSTRING _hStringLeft; + auto hr = WindowsCreateStringReference(L"123", 3, &_HeaderLeft, &_hStringLeft); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(_hStringLeft, nullptr, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, 1); + + WindowsDeleteString(_hStringLeft); + } + + { + HSTRING_HEADER _HeaderRigth; + HSTRING _hStringRigth; + auto hr = WindowsCreateStringReference(L"123", 3, &_HeaderRigth, &_hStringRigth); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(nullptr, _hStringRigth, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, -1); + + WindowsDeleteString(_hStringRigth); + } + + { + INT32 _nResult = 0xCC; + auto hr = ::WindowsCompareStringOrdinal(nullptr, nullptr, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, 0); + } + + { + HSTRING_HEADER _HeaderLeft; + HSTRING _hStringLeft; + auto hr = WindowsCreateStringReference(L"", 0, &_HeaderLeft, &_hStringLeft); + Assert::AreEqual(hr, (HRESULT)S_OK); + + INT32 _nResult = 0xCC; + hr = ::WindowsCompareStringOrdinal(_hStringLeft, nullptr, &_nResult); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::AreEqual(_nResult, 0); + + WindowsDeleteString(_hStringLeft); + } + } + }; + + TEST_CLASS(WindowsDuplicateString) + { + AwaysNullGuard Guard; + + public: + WindowsDuplicateString() + { + Guard |= YY::Thunks::aways_null_try_get_WindowsCreateString; + Guard |= YY::Thunks::aways_null_try_get_WindowsCreateStringReference; + Guard |= YY::Thunks::aways_null_try_get_WindowsDuplicateString; + Guard |= YY::Thunks::aways_null_try_get_WindowsGetStringRawBuffer; + } + + TEST_METHOD(标准行为验证) + { + { + const wchar_t _szInput[] = L"TestString"; + const auto _cInput = _countof(_szInput) - 1; + + HSTRING _hString = nullptr; + HRESULT hr = ::WindowsCreateString(_szInput, _cInput, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + HSTRING _hNewString = nullptr; + hr = ::WindowsDuplicateString(_hString, &_hNewString); + Assert::AreEqual(hr, (HRESULT)S_OK); + // WindowsCreateString 的字符串直接增加引用计数即可 + Assert::AreEqual((void*)_hString, (void*)_hNewString); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hNewString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szInput, sizeof(_szInput)) == 0); + + ::WindowsDeleteString(_hString); + ::WindowsDeleteString(_hNewString); + } + + { + const wchar_t _szInput[] = L"TestString"; + const auto _cInput = _countof(_szInput) - 1; + + HSTRING _hString = nullptr; + HSTRING_HEADER _Header; + HRESULT hr = ::WindowsCreateStringReference(_szInput, _cInput, &_Header, &_hString); + Assert::AreEqual(hr, (HRESULT)S_OK); + + HSTRING _hNewString = nullptr; + hr = ::WindowsDuplicateString(_hString, &_hNewString); + Assert::AreEqual(hr, (HRESULT)S_OK); + // WindowsCreateString 的字符串直接增加引用计数即可 + Assert::AreNotEqual((void*)_hString, (void*)_hNewString); + + UINT32 _cWindowsStringLength; + auto _pszBuffer = ::WindowsGetStringRawBuffer(_hNewString, &_cWindowsStringLength); + Assert::IsNotNull(_pszBuffer); + Assert::AreEqual((size_t)_cWindowsStringLength, _cInput); + + Assert::IsTrue(memcmp(_pszBuffer, _szInput, sizeof(_szInput)) == 0); + + ::WindowsDeleteString(_hString); + ::WindowsDeleteString(_hNewString); + } + + { + HSTRING _hNewString = nullptr; + auto hr = ::WindowsDuplicateString(nullptr, &_hNewString); + Assert::AreEqual(hr, (HRESULT)S_OK); + // WindowsCreateString 的字符串直接增加引用计数即可 + Assert::AreEqual((void*)nullptr, (void*)_hNewString); + } + } + }; }