// // corecrt_internal_win32_buffer.h // // Copyright (c) Microsoft Corporation. All rights reserved. // // This internal header defines template-based utilities for handling call-twice // Win32 APIs, where you first call the Win32 API with null or a fixed sized buffer, // and if there is not enough space, allocate a dynamically sized buffer. #pragma once #include #pragma pack(push, _CRT_PACKING) //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // __crt_win32_buffer_debug_info // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // This is a class that can be used to describe the block_use, file_name, line_number // debug data that is sometimes shuffled around between function calls. class __crt_win32_buffer_debug_info { #ifndef _DEBUG public: __crt_win32_buffer_debug_info(int, char const *, int) { } #else /* ^^^^ Release ^^^^ / vvvv Debug vvvv */ public: __crt_win32_buffer_debug_info( int const initial_block_use, char const * const initial_file_name, int const initial_line_number ) : _block_use(initial_block_use), _file_name(initial_file_name), _line_number(initial_line_number) { } int block_use() const { return _block_use; } char const * file_name() const { return _file_name; } int line_number() const { return _line_number; } private: int _block_use; char const * _file_name; int _line_number; #endif /* _DEBUG */ }; class __crt_win32_buffer_empty_debug_info { }; //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // __crt_win32_buffer resize policies // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // These classes are used to describe the different resize policies that a // __crt_win32_buffer can have. struct __crt_win32_buffer_internal_dynamic_resizing { using debug_info_type = __crt_win32_buffer_empty_debug_info; _Check_return_ static errno_t allocate(void ** const address, size_t const size, debug_info_type const&) { void * const ret = _malloc_crt(size); *address = ret; if (ret == nullptr) { return ENOMEM; } return 0; } static void deallocate(void * const ptr, debug_info_type const&) { _free_crt(ptr); } }; struct __crt_win32_buffer_public_dynamic_resizing { using debug_info_type = __crt_win32_buffer_debug_info; _Check_return_ static errno_t allocate(void ** const address, size_t const size, debug_info_type const& debug_info) { UNREFERENCED_PARAMETER(debug_info); // only used in debug mode void * const ret = _malloc_dbg( size, debug_info.block_use(), debug_info.file_name(), debug_info.line_number() ); *address = ret; if (ret == nullptr) { return ENOMEM; } return 0; } static void deallocate(void * const ptr, debug_info_type const& debug_info) { UNREFERENCED_PARAMETER(debug_info); // only used in debug mode _free_dbg(ptr, debug_info.block_use()); } }; struct __crt_win32_buffer_no_resizing { using debug_info_type = __crt_win32_buffer_empty_debug_info; _Check_return_ static errno_t allocate(void ** const, size_t const, debug_info_type const&) { errno = ERANGE; // buffer not large enough return ERANGE; } static void deallocate(void * const, debug_info_type const&) { } }; //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // __crt_win32_buffer, __crt_internal_win32_buffer // __crt_public_win32_buffer, __crt_no_alloc_win32_buffer // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Class and typedefs for buffers that support calling a win32 function and automatically // resizing if needed. template class __crt_win32_buffer : private ResizePolicy::debug_info_type { // Buffer type to enable Win32 call-twice-if-not-enough-space APIs. // Using this type allows us to use a local buffer if possible and allocate if needed. public: using char_type = Character; using debug_info_type = typename ResizePolicy::debug_info_type; __crt_win32_buffer() : debug_info_type(), _initial_string(nullptr), _initial_capacity(0), _string(nullptr), _capacity(0), _size(0), _is_dynamic(false) { } explicit __crt_win32_buffer(debug_info_type const& debug_info) : debug_info_type(debug_info), _initial_string(nullptr), _initial_capacity(0), _string(nullptr), _capacity(0), _size(0), _is_dynamic(false) { } template __crt_win32_buffer(Character (&buffer)[N]) : debug_info_type(), _initial_string(buffer), _initial_capacity(N), _string(buffer), _capacity(N), _size(0), _is_dynamic(false) { } template __crt_win32_buffer(Character (&buffer)[N], debug_info_type const& debug_info) : debug_info_type(debug_info), _initial_string(buffer), _initial_capacity(N), _string(buffer), _capacity(N), _size(0), _is_dynamic(false) { } __crt_win32_buffer( Character * const buffer, size_t const buffer_capacity ) : debug_info_type(), _initial_string(buffer), _initial_capacity(buffer_capacity), _string(buffer), _capacity(buffer_capacity), _size(0), _is_dynamic(false) { } __crt_win32_buffer( Character * const buffer, size_t const buffer_capacity, debug_info_type const& debug_info ) : debug_info_type(debug_info), _initial_string(buffer), _initial_capacity(buffer_capacity), _string(buffer), _capacity(buffer_capacity), _size(0), _is_dynamic(false) { } ~__crt_win32_buffer() { _deallocate(); } __crt_win32_buffer(__crt_win32_buffer const&) = delete; __crt_win32_buffer& operator=(__crt_win32_buffer const&) = delete; __crt_win32_buffer(__crt_win32_buffer&&) = delete; __crt_win32_buffer& operator=(__crt_win32_buffer&&) = delete; char_type * data() { return _string; } char_type const * data() const { return _string; } size_t size() const { return _size; } void size(size_t new_size) { _size = new_size; } size_t capacity() const { return _capacity; } void reset() { _deallocate(); _reset_no_dealloc(); } char_type * detach() { if (_string == nullptr || _size == 0) { return nullptr; } char_type * return_val{}; if (!_is_dynamic && _size > 0) { // This pointer needs to live longer than the stack buffer // Allocate + Copy ResizePolicy::allocate( reinterpret_cast(&return_val), _size * sizeof(Character), debug_info() ); memcpy_s(return_val, _size, _string, _capacity); } else { return_val = _string; } _reset_no_dealloc(); return return_val; } template errno_t call_win32_function(Win32Function const& win32_function) { // Suitable for more Win32 calls, where a size is returned // if there is not enough space. size_t const required_size = win32_function(_string, static_cast(_capacity)); if (required_size == 0) { __acrt_errno_map_os_error(GetLastError()); return errno; } if (required_size <= _capacity) { // Had enough space, data was written, save size and return _size = required_size; return 0; } size_t const required_size_plus_null_terminator = required_size + 1; errno_t const alloc_err = allocate(required_size_plus_null_terminator); if (alloc_err) { return alloc_err; } // Upon success, return value is number of characters written, minus the null terminator. size_t const required_size2 = win32_function(_string, static_cast(_capacity)); if (required_size2 == 0) { __acrt_errno_map_os_error(GetLastError()); return errno; } // Capacity should be large enough at this point. _size = required_size2; return 0; } debug_info_type const& debug_info() const { return static_cast(*this); } errno_t allocate(size_t requested_size) { _deallocate(); errno_t err = ResizePolicy::allocate( reinterpret_cast(&_string), requested_size * sizeof(Character), debug_info() ); if (err) { _is_dynamic = false; _capacity = 0; return err; } _is_dynamic = true; _capacity = requested_size; return 0; } void set_to_nullptr() { _deallocate(); _string = nullptr; _capacity = 0; _size = 0; } private: void _reset_no_dealloc() { _string = _initial_string; _capacity = _initial_capacity; _size = 0; } void _deallocate() { if (_is_dynamic) { ResizePolicy::deallocate(_string, debug_info()); _is_dynamic = false; } } char_type * const _initial_string; size_t _initial_capacity; char_type * _string; size_t _capacity; size_t _size; bool _is_dynamic; }; template using __crt_internal_win32_buffer = __crt_win32_buffer; template using __crt_public_win32_buffer = __crt_win32_buffer; template using __crt_no_alloc_win32_buffer = __crt_win32_buffer; //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // UTF-8 or ACP Helper // // Some POSIX functions have historically used the ACP for doing narrow->wide // conversions. In order to support UTF-8 with these functions, they've been // modified so that they use CP_UTF8 when current locale is UTF-8, but the ACP // otherwise in order to preserve backwards compatibility. // // These POSIX functions can call __acrt_get_utf8_acp_compatibility_codepage to grab // the code page they should use for their conversions. // // The Win32 ANSI "*A" APIs also use this to preserve their behavior as using the ACP, unless // the current locale is set to UTF-8. //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ inline unsigned int __acrt_get_utf8_acp_compatibility_codepage() { _LocaleUpdate locale_update(nullptr); unsigned int const current_code_page = locale_update.GetLocaleT()->locinfo->_public._locale_lc_codepage; if (current_code_page == CP_UTF8) { return CP_UTF8; } bool const use_oem_code_page = !__acrt_AreFileApisANSI(); if (use_oem_code_page) { return CP_OEMCP; } return CP_ACP; } //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Win32 APIs using __crt_win32_buffer // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // See complete list of internal conversion functions in corecrt_internal_traits.h template errno_t __acrt_convert_wcs_mbs( FromChar const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, CvtFunction const& cvt_func, _locale_t locale ) { // Common code path for conversions using mbstowcs and wcstombs. if (null_terminated_input_string == nullptr) { win32_buffer.set_to_nullptr(); return 0; } // No empty string special case - mbstowcs/wcstombs handles them. size_t const required_size = cvt_func(nullptr, null_terminated_input_string, 0, locale); if (required_size == static_cast(-1)) { return errno; } size_t const required_size_plus_null_terminator = required_size + 1; if (required_size_plus_null_terminator > win32_buffer.capacity()) { errno_t const alloc_err = win32_buffer.allocate(required_size_plus_null_terminator); if (alloc_err != 0) { return alloc_err; } } size_t const chars_converted = cvt_func(win32_buffer.data(), null_terminated_input_string, win32_buffer.capacity(), locale); if (chars_converted == static_cast(-1) || chars_converted == win32_buffer.capacity()) { // check for error or if output is not null terminated return errno; } win32_buffer.size(chars_converted); return 0; } template errno_t __acrt_convert_wcs_mbs_cp( FromChar const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, CvtFunction const& cvt_func, unsigned int const code_page ) { // Common code path for conversions using MultiByteToWideChar and WideCharToMultiByte with null terminated inputs. if (null_terminated_input_string == nullptr) { win32_buffer.set_to_nullptr(); return 0; } // Special Case: Empty strings are not valid input to MultiByteToWideChar/WideCharToMultiByte if (null_terminated_input_string[0] == '\0') { if (win32_buffer.capacity() == 0) { errno_t alloc_err = win32_buffer.allocate(1); if (alloc_err != 0) { return alloc_err; } } win32_buffer.data()[0] = '\0'; win32_buffer.size(0); return 0; } size_t const required_size_plus_null_terminator = cvt_func( code_page, null_terminated_input_string, nullptr, 0 ); if (required_size_plus_null_terminator == 0) { __acrt_errno_map_os_error(::GetLastError()); return errno; } if (required_size_plus_null_terminator > win32_buffer.capacity()) { errno_t alloc_err = win32_buffer.allocate(required_size_plus_null_terminator); if (alloc_err != 0) { return alloc_err; } } size_t const chars_converted_plus_null_terminator = cvt_func( code_page, null_terminated_input_string, win32_buffer.data(), win32_buffer.capacity() ); if (chars_converted_plus_null_terminator == 0) { __acrt_errno_map_os_error(::GetLastError()); return errno; } win32_buffer.size(chars_converted_plus_null_terminator - 1); // size does not include the null terminator return 0; } template errno_t __acrt_wcs_to_mbs( wchar_t const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, _locale_t locale = nullptr ) { _BEGIN_SECURE_CRT_DEPRECATION_DISABLE return __acrt_convert_wcs_mbs( null_terminated_input_string, win32_buffer, _wcstombs_l, locale ); _END_SECURE_CRT_DEPRECATION_DISABLE } template errno_t __acrt_wcs_to_mbs_cp( wchar_t const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, unsigned int const code_page ) { auto const wcs_to_mbs = []( unsigned int const code_page, wchar_t const * const null_terminated_input_string, char * const buffer, size_t const buffer_size) { // Return value includes null terminator. return __acrt_WideCharToMultiByte( code_page, 0, null_terminated_input_string, -1, buffer, static_cast(buffer_size), nullptr, nullptr ); }; return __acrt_convert_wcs_mbs_cp( null_terminated_input_string, win32_buffer, wcs_to_mbs, code_page ); } template errno_t __acrt_mbs_to_wcs( char const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, _locale_t locale = nullptr ) { _BEGIN_SECURE_CRT_DEPRECATION_DISABLE return __acrt_convert_wcs_mbs( null_terminated_input_string, win32_buffer, _mbstowcs_l, locale ); _END_SECURE_CRT_DEPRECATION_DISABLE } template errno_t __acrt_mbs_to_wcs_cp( char const * const null_terminated_input_string, __crt_win32_buffer& win32_buffer, unsigned int const code_page ) { auto const mbs_to_wcs = []( unsigned int const code_page, char const * const null_terminated_input_string, wchar_t * const buffer, size_t const buffer_size) { // Return value includes null terminator. return __acrt_MultiByteToWideChar( code_page, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, null_terminated_input_string, -1, buffer, static_cast(buffer_size) ); }; return __acrt_convert_wcs_mbs_cp( null_terminated_input_string, win32_buffer, mbs_to_wcs, code_page ); } // Array overloads are useful for __try contexts where objects with unwind semantics cannot be used. template size_t __acrt_wcs_to_mbs_array( wchar_t const * const null_terminated_input_string, char (&buffer)[N], _locale_t locale = nullptr ) { __crt_no_alloc_win32_buffer win32_buffer(buffer); if (__acrt_wcs_to_mbs(null_terminated_input_string, win32_buffer, locale) != 0) { return 0; } return win32_buffer.size(); } template size_t __acrt_wcs_to_mbs_cp_array( wchar_t const * const null_terminated_input_string, char (&buffer)[N], unsigned int const code_page ) { __crt_no_alloc_win32_buffer win32_buffer(buffer); if (__acrt_wcs_to_mbs_cp(null_terminated_input_string, win32_buffer, code_page) != 0) { return 0; } return win32_buffer.size(); } template size_t __acrt_mbs_to_wcs_array( char const * const null_terminated_input_string, wchar_t (&buffer)[N], _locale_t locale = nullptr ) { __crt_no_alloc_win32_buffer win32_buffer(buffer); if (__acrt_mbs_to_wcs(null_terminated_input_string, win32_buffer, locale) != 0) { return 0; } return win32_buffer.size(); } template size_t __acrt_mbs_to_wcs_cp_array( char const * const null_terminated_input_string, wchar_t (&buffer)[N], unsigned int const code_page ) { __crt_no_alloc_win32_buffer win32_buffer(buffer); if (__acrt_wcs_to_mbs_cp(null_terminated_input_string, win32_buffer, code_page) != 0) { return 0; } return win32_buffer.size(); } template errno_t __acrt_get_current_directory_wide( __crt_win32_buffer& win32_buffer ) { return win32_buffer.call_win32_function([](wchar_t * buffer, DWORD buffer_length) { return ::GetCurrentDirectoryW(buffer_length, buffer); }); } template errno_t __acrt_get_current_directory_narrow_acp_or_utf8( __crt_win32_buffer& win32_buffer ) { wchar_t default_buffer_space[_MAX_PATH]; __crt_internal_win32_buffer wide_buffer(default_buffer_space); errno_t const err = __acrt_get_current_directory_wide(wide_buffer); if (err != 0) { return err; } return __acrt_wcs_to_mbs_cp( wide_buffer.data(), win32_buffer, __acrt_get_utf8_acp_compatibility_codepage() ); } template errno_t __acrt_get_full_path_name_wide( wchar_t const * const lpFileName, __crt_win32_buffer& win32_buffer ) { return win32_buffer.call_win32_function([lpFileName](wchar_t * buffer, DWORD buffer_length) { return ::GetFullPathNameW( lpFileName, buffer_length, buffer, nullptr ); }); } template errno_t __acrt_get_full_path_name_narrow_acp_or_utf8( char const * const lpFileName, __crt_win32_buffer& win32_buffer ) { wchar_t default_buffer_space[_MAX_PATH]; __crt_internal_win32_buffer wide_buffer(default_buffer_space); wchar_t default_file_name_space[_MAX_PATH]; __crt_internal_win32_buffer wide_file_name(default_file_name_space); unsigned int const code_page = __acrt_get_utf8_acp_compatibility_codepage(); errno_t const cvt_err = __acrt_mbs_to_wcs_cp( lpFileName, wide_file_name, code_page ); if (cvt_err != 0) { return cvt_err; } errno_t const err = __acrt_get_full_path_name_wide(wide_file_name.data(), wide_buffer); if (err != 0) { return err; } return __acrt_wcs_to_mbs_cp( wide_buffer.data(), win32_buffer, code_page ); } #pragma pack(pop)