diff --git a/ThunksList.md b/ThunksList.md index f4d67ba..b15a22f 100644 --- a/ThunksList.md +++ b/ThunksList.md @@ -96,15 +96,15 @@ ## api-ms-win-core-winrt-string-l1-1-0.dll | 函数 | Fallback | ---- | ----------- -| WindowsCreateString | 不存在时,返回 E_NOTIMPL。 -| WindowsCreateStringReference | 不存在时,返回 E_NOTIMPL。 -| WindowsDeleteString | 不存在时,返回 E_NOTIMPL。 -| WindowsDuplicateString | 不存在时,返回 E_NOTIMPL。 -| WindowsGetStringLen | 不存在时,返回 E_NOTIMPL。 -| WindowsGetStringRawBuffer | 不存在时,返回 E_NOTIMPL。 -| WindowsIsStringEmpty | 不存在时,返回 E_NOTIMPL。 -| WindowsStringHasEmbeddedNull | 不存在时,返回 E_NOTIMPL。 -| WindowsCompareStringOrdinal | 不存在时,返回 E_NOTIMPL。 +| WindowsCreateString | 不存在时,内部实现。 +| WindowsCreateStringReference | 不存在时,内部实现。 +| WindowsDeleteString | 不存在时,内部实现。 +| WindowsDuplicateString | 不存在时,内部实现。 +| WindowsGetStringLen | 不存在时,内部实现。 +| WindowsGetStringRawBuffer | 不存在时,内部实现。 +| WindowsIsStringEmpty | 不存在时,内部实现。 +| WindowsStringHasEmbeddedNull | 不存在时,内部实现。 +| WindowsCompareStringOrdinal | 不存在时,内部实现。 ## advapi32.dll | 函数 | Fallback 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/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 4096054..e9d5af0 100644 --- a/src/Thunks/api-ms-win-core-winrt-string.hpp +++ b/src/Thunks/api-ms-win-core-winrt-string.hpp @@ -1,238 +1,395 @@ -#include +#if (YY_Thunks_Support_Version < NTDDI_WIN8) +#include -namespace YY +#include +#endif + +#if (YY_Thunks_Support_Version < NTDDI_WIN8) && __YY_Thunks_libs && !defined(__Comment_Lib_runtimeobject) +#define __Comment_Lib_runtimeobject +// 193行 WindowsCreateString +#pragma comment(lib, "runtimeobject.lib") +#endif + +namespace YY::Thunks { - namespace Thunks - { #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 12, - HRESULT, - STDAPICALLTYPE, - WindowsCreateString, - _In_reads_opt_(length) PCNZWCH sourceString, - UINT32 length, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string - ) - { - if (auto const pWindowsCreateString = try_get_WindowsCreateString()) - { - return pWindowsCreateString(sourceString, length, string); - } + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 12, + HRESULT, + STDAPICALLTYPE, + WindowsCreateString, + _In_reads_opt_(_cLength) PCNZWCH _sSourceString, + UINT32 _cLength, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phString + ) + { + if (auto const _pfnWindowsCreateString = try_get_WindowsCreateString()) + { + return _pfnWindowsCreateString(_sSourceString, _cLength, _phString); + } + + if (!_phString) + { + return E_INVALIDARG; + } + + *_phString = nullptr; + if (_sSourceString == nullptr && 0 != _cLength) + { + return E_POINTER; + } - if (string) - *string = nullptr; + const uint64_t _cbRequiredSize = sizeof(internal::STRING_OPAQUE) + uint64_t(_cLength + 1) * sizeof(WCHAR); + if (MAXUINT32 < _cbRequiredSize) + { + return MEM_E_INVALID_SIZE; + } - return E_NOTIMPL; + auto _pStringInternal = reinterpret_cast(internal::Alloc(_cbRequiredSize, HEAP_ZERO_MEMORY)); + if (!_pStringInternal) + { + return E_OUTOFMEMORY; } + + _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 #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 16, - HRESULT, - STDAPICALLTYPE, - WindowsCreateStringReference, - _In_reads_opt_(length + 1) PCWSTR sourceString, - UINT32 length, - _Out_ HSTRING_HEADER* hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string - ) - { - if (auto const pWindowsCreateStringReference = try_get_WindowsCreateStringReference()) + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 16, + HRESULT, + STDAPICALLTYPE, + WindowsCreateStringReference, + _In_reads_opt_(_cLength + 1) PCWSTR _szSourceString, + UINT32 _cLength, + _Out_ HSTRING_HEADER* _phStringHeader, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phString + ) + { + if (auto const pfnWindowsCreateStringReference = try_get_WindowsCreateStringReference()) + { + return pfnWindowsCreateStringReference(_szSourceString, _cLength, _phStringHeader, _phString); + } + + if (_phString == nullptr || _phStringHeader == nullptr) + { + return E_INVALIDARG; + } + *_phString = nullptr; + + if (_szSourceString == nullptr && 0 != _cLength) + { + return E_POINTER; + } + + if (_szSourceString) + { + if (L'\0' != _szSourceString[_cLength]) { - return pWindowsCreateStringReference(sourceString, length, hstringHeader, string); + return E_STRING_NOT_NULL_TERMINATED; } + } - if (string) - *string = nullptr; + auto _pHeader = reinterpret_cast(_phStringHeader); + ::memset(_pHeader, 0, sizeof(*_pHeader)); - return E_NOTIMPL; - } + _pHeader->Flags = internal::RuntimeStringFlags::WRHF_STRING_REFERENCE; + _pHeader->Length = _cLength; + _pHeader->StringRef = _szSourceString; + *_phString = reinterpret_cast(_pHeader); + return S_OK; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 4, - HRESULT, - STDAPICALLTYPE, - WindowsDeleteString, - _In_opt_ HSTRING string - ) - { - if (auto const pWindowsDeleteString = try_get_WindowsDeleteString()) - { - return pWindowsDeleteString(string); - } - - return E_NOTIMPL; + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 4, + HRESULT, + STDAPICALLTYPE, + WindowsDeleteString, + _In_opt_ HSTRING _hString + ) + { + if (auto const _pfnWindowsDeleteString = try_get_WindowsDeleteString()) + { + return _pfnWindowsDeleteString(_hString); } + + 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; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 8, - HRESULT, - STDAPICALLTYPE, - WindowsDuplicateString, - _In_opt_ HSTRING string, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* newString - ) - { - if (auto const pWindowsDuplicateString = try_get_WindowsDuplicateString()) - { - return pWindowsDuplicateString(string, newString); - } + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 8, + HRESULT, + STDAPICALLTYPE, + WindowsDuplicateString, + _In_opt_ HSTRING _hString, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* _phNewString + ) + { + if (auto const _pfnWindowsDuplicateString = try_get_WindowsDuplicateString()) + { + return _pfnWindowsDuplicateString(_hString, _phNewString); + } + if (!_phNewString) + return E_INVALIDARG; - if (newString) - *newString = nullptr; + *_phNewString = nullptr; + if (!_hString) + return S_OK; - return E_NOTIMPL; + auto _pStringInternal = reinterpret_cast(_hString); + if (!internal::HasFlags(_pStringInternal->Header.Flags, internal::RuntimeStringFlags::WRHF_STRING_REFERENCE)) + { + ::InterlockedIncrement(&_pStringInternal->RefCount); + *_phNewString = _hString; + return S_OK; } + + // WRHF_STRING_REFERENCE 时内存生命周期被调用者接管,无法简单增加引用计数延长生命周期。 + // 所以需要使用 WindowsCreateString 创建副本。 + return ::WindowsCreateString(_pStringInternal->Header.StringRef, _pStringInternal->Header.Length, _phNewString); + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 4, - UINT32, - STDAPICALLTYPE, - WindowsGetStringLen, - _In_opt_ HSTRING string - ) - { - if (auto const pWindowsGetStringLen = try_get_WindowsGetStringLen()) - { - return pWindowsGetStringLen(string); - } - - return 0; + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 4, + UINT32, + STDAPICALLTYPE, + WindowsGetStringLen, + _In_opt_ HSTRING _hString + ) + { + if (auto const _pfnWindowsGetStringLen = try_get_WindowsGetStringLen()) + { + return _pfnWindowsGetStringLen(_hString); } + + if (!_hString) + return 0ul; + + auto _pHeader = reinterpret_cast(_hString); + return _pHeader->Length; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 8, - PCWSTR, - STDAPICALLTYPE, - WindowsGetStringRawBuffer, - _In_opt_ HSTRING string, - _Out_opt_ UINT32* length - ) - { - if (auto const pWindowsGetStringRawBuffer = try_get_WindowsGetStringRawBuffer()) + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 8, + PCWSTR, + STDAPICALLTYPE, + WindowsGetStringRawBuffer, + _In_opt_ HSTRING _hString, + _Out_opt_ UINT32* _pcLength + ) + { + if (auto const _pfnWindowsGetStringRawBuffer = try_get_WindowsGetStringRawBuffer()) + { + return _pfnWindowsGetStringRawBuffer(_hString, _pcLength); + } + + if (!_hString) + { + if (_pcLength) { - return pWindowsGetStringRawBuffer(string, length); + *_pcLength = 0; } - if (length) - *length = 0; + return L""; + } - return nullptr; + auto _pHeader = reinterpret_cast(_hString); + if (_pcLength) + { + *_pcLength = _pHeader->Length; } + + return _pHeader->StringRef; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 4, - BOOL, - STDAPICALLTYPE, - WindowsIsStringEmpty, - _In_opt_ HSTRING string - ) - { - if (auto const pWindowsIsStringEmpty = try_get_WindowsIsStringEmpty()) - { - return pWindowsIsStringEmpty(string); - } - - return TRUE; + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 4, + BOOL, + STDAPICALLTYPE, + WindowsIsStringEmpty, + _In_opt_ HSTRING _hString + ) + { + if (auto const _pfnWindowsIsStringEmpty = try_get_WindowsIsStringEmpty()) + { + return _pfnWindowsIsStringEmpty(_hString); } + + auto _pHeader = reinterpret_cast(_hString); + return _pHeader == nullptr || 0ul == _pHeader->Length; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 8, - HRESULT, - STDAPICALLTYPE, - WindowsStringHasEmbeddedNull, - _In_opt_ HSTRING string, - _Out_ BOOL* hasEmbedNull - ) - { - if (auto const pWindowsStringHasEmbeddedNull = try_get_WindowsStringHasEmbeddedNull()) + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 8, + HRESULT, + STDAPICALLTYPE, + WindowsStringHasEmbeddedNull, + _In_opt_ HSTRING _hString, + _Out_ BOOL* _pbHasEmbedNull + ) + { + if (auto const _pfnWindowsStringHasEmbeddedNull = try_get_WindowsStringHasEmbeddedNull()) + { + return _pfnWindowsStringHasEmbeddedNull(_hString, _pbHasEmbedNull); + } + + 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; + } + + auto _szEnd = _pHeader->StringRef + _pHeader->Length; + for (auto _szStr = _pHeader->StringRef; _szStr < _szEnd; ++_szStr) + { + if (*_szStr == L'\0') { - return pWindowsStringHasEmbeddedNull(string, hasEmbedNull); + 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; } - - return E_NOTIMPL; } + + InterlockedOr((LONG*)&_pHeader->Flags, LONG(internal::RuntimeStringFlags::WRHF_EMBEDDED_NULLS_COMPUTED)); + *_pbHasEmbedNull = FALSE; + return S_OK; + } #endif #if (YY_Thunks_Support_Version < NTDDI_WIN8) - //Windows 8 [desktop apps | UWP apps] - //Windows Server 2012 [desktop apps | UWP apps] - __DEFINE_THUNK( - api_ms_win_core_winrt_string_l1_1_0, - 12, - HRESULT, - STDAPICALLTYPE, - WindowsCompareStringOrdinal, - _In_opt_ HSTRING string1, - _In_opt_ HSTRING string2, - _Out_ INT32* result - ) - { - if (auto const pWindowsCompareStringOrdinal = try_get_WindowsCompareStringOrdinal()) + //Windows 8 [desktop apps | UWP apps] + //Windows Server 2012 [desktop apps | UWP apps] + __DEFINE_THUNK( + api_ms_win_core_winrt_string_l1_1_0, + 12, + HRESULT, + STDAPICALLTYPE, + WindowsCompareStringOrdinal, + _In_opt_ HSTRING _hStringLeft, + _In_opt_ HSTRING _hStringRigth, + _Out_ INT32* _pnResult + ) + { + if (auto const _pfnWindowsCompareStringOrdinal = try_get_WindowsCompareStringOrdinal()) + { + return _pfnWindowsCompareStringOrdinal(_hStringLeft, _hStringRigth, _pnResult); + } + + if (!_pnResult) + return E_INVALIDARG; + + *_pnResult = 0; + const auto _hHeaderLeft = reinterpret_cast(_hStringLeft); + const auto _hHeaderRigth = reinterpret_cast(_hStringRigth); + if (_hHeaderLeft && _hHeaderLeft->Length) + { + if (_hHeaderRigth && _hHeaderRigth->Length) { - return pWindowsCompareStringOrdinal(string1, string2, result); + const auto _nResult = ::CompareStringOrdinal(_hHeaderLeft->StringRef, _hHeaderLeft->Length, _hHeaderRigth->StringRef, _hHeaderRigth->Length, FALSE); + if (_nResult) + { + *_pnResult = _nResult - CSTR_EQUAL; + } + } + else + { + *_pnResult = 1; } - - return E_NOTIMPL; } -#endif + else + { + *_pnResult = _hHeaderRigth && _hHeaderRigth->Length ? -1 : 0; + } + return S_OK; } -} \ No newline at end of file +#endif +} diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 2bd4c24..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 @@ -181,6 +185,7 @@ + @@ -214,10 +219,11 @@ - + + diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters index a1dc22a..8045d73 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters @@ -87,6 +87,9 @@ 源文件\UnitTest + + 源文件\UnitTest + @@ -278,9 +281,6 @@ Shared - - Shared - Thunks @@ -359,6 +359,12 @@ Thunks + + Shared + + + Shared + 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 new file mode 100644 index 0000000..0bd486e --- /dev/null +++ b/src/YY-Thunks.UnitTest/api-ms-win-core-winrt-string.UnitTest.cpp @@ -0,0 +1,363 @@ +#include "pch.h" +#include "Thunks/api-ms-win-core-winrt-string.hpp" + +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; + + public: + 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字符串) + { + 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); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::IsFalse(_bEmbeddedNull); + + ::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); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); + Assert::AreEqual(hr, (HRESULT)S_OK); + Assert::IsTrue(_bEmbeddedNull); + + ::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); + + BOOL _bEmbeddedNull = FALSE; + hr = ::WindowsStringHasEmbeddedNull(_hString, &_bEmbeddedNull); + Assert::AreEqual(hr, (HRESULT)S_OK); + 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); + } + } + }; +}