/*** *marshal.h * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: Marshalling classes * * [Public] * ****/ #pragma once #ifndef _INC_MSCLR_MARSHAL #define _INC_MSCLR_MARSHAL // #using is not used in the Windows build. #if !defined _CRT_WINDOWS && !defined UNDOCKED_WINDOWS_UCRT #using #endif #include #include #include #include #include #include #include #include #pragma comment(lib, "comsupp.lib") #pragma comment(lib, "oleaut32.lib") #pragma warning(push) #pragma warning(error : 4996) namespace msclr{ namespace interop{ namespace details { #define _EXCEPTION_GREATER_THAN_INT_MAX "Size of string exceeds INT_MAX." #define _EXCEPTION_NULLPTR "NULLPTR is not supported for this conversion." #define _EXCEPTION_MB2WC "Conversion from MultiByte to WideChar failed. Please check the content of the string and/or locale settings." #define _EXCEPTION_WC2MB "Conversion from WideChar to MultiByte failed. Please check the content of the string and/or locale settings." // helper class to allocate/deallocate a buffer with new[]/delete[] template class char_buffer { public: explicit char_buffer(size_t _size) { _ptr = new T[_size]; } ~char_buffer() { delete [] _ptr; } T *get() const { return _ptr; } T* release() { T *_ret_value = _ptr; _ptr = NULL; return _ret_value; } private: // no copy constructor nor operator= char_buffer(const char_buffer&); char_buffer& operator=(const char_buffer&); T *_ptr; }; inline _Check_return_ size_t GetAnsiStringSize(System::String^ _str) { cli::pin_ptr _pinned_ptr = PtrToStringChars(_str); auto _size = static_cast( ::WideCharToMultiByte(CP_THREAD_ACP, WC_NO_BEST_FIT_CHARS, _pinned_ptr, _str->Length, NULL, 0, NULL,NULL)); if (_size == 0 && _str->Length != 0) { throw gcnew System::ArgumentException(_EXCEPTION_WC2MB); } // adding 1 for terminating nul _size+=1; return _size; } inline void WriteAnsiString(_Out_writes_all_(_size) _Post_z_ char* _buf, size_t _size, System::String^ _str) { cli::pin_ptr _pinned_ptr = PtrToStringChars(_str); //checking for overflow if (_size > INT_MAX) { // this should never happen if _size was returned by GetAnsiStringSize() throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); } const auto _written = static_cast(::WideCharToMultiByte(CP_THREAD_ACP, WC_NO_BEST_FIT_CHARS, _pinned_ptr, _str->Length, _buf, static_cast(_size), NULL, NULL)); if( _written >= _size || (_written == 0 && _size != 1)) // allowing empty string { throw gcnew System::ArgumentException(_EXCEPTION_WC2MB); } _buf[_written] = '\0'; } inline _Check_return_ size_t GetUnicodeStringSize(_In_reads_z_(_count+1) const char* _str, size_t _count) { if (_count > INT_MAX) { throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); } auto _size = static_cast(::MultiByteToWideChar(CP_THREAD_ACP, 0, _str, static_cast(_count), NULL, 0)); if (_size == 0 && _count != 0) { throw gcnew System::ArgumentException(_EXCEPTION_MB2WC); } //adding 1 for terminating nul _size+=1; return _size; } inline void WriteUnicodeString(_Out_writes_all_(_size) _Post_z_ wchar_t* _dest, size_t _size, _In_reads_bytes_(_count)const char* _src, size_t _count) { //checking for overflow if (_size > INT_MAX || _count > INT_MAX) { throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); } size_t _written = static_cast(::MultiByteToWideChar(CP_THREAD_ACP, 0, _src, static_cast(_count), _dest, static_cast(_size))); if( _written >= _size || (_written == 0 && _size != 1)) // allowing empty string { throw gcnew System::ArgumentException(_EXCEPTION_MB2WC); } _dest[_written] = L'\0'; } inline System::String^ InternalAnsiToStringHelper(_In_reads_z_(_count+1)const char* _src, size_t _count) { const size_t _size = details::GetUnicodeStringSize(_src, _count); if (_size > INT_MAX || _size <=0 ) { throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); } details::char_buffer _wchar_buf(_size); if (_wchar_buf.get() == NULL) { throw gcnew System::InsufficientMemoryException(); } details::WriteUnicodeString(_wchar_buf.get(), _size, _src, _count); return gcnew System::String(_wchar_buf.get(), 0, static_cast(_size)-1); } inline System::String^ InternalUnicodeToStringHelper(_In_reads_z_(_count+1)const wchar_t* _src, size_t _count) { if (_count > INT_MAX) { throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); } return gcnew System::String(_src, 0 ,static_cast(_count)); } } // namespace details template ref class context_node; template ::_Needs_Context> class error_reporting_helper; template class error_reporting_helper<_To_Type, _From_Type, /* _Needs_Context */ false> { public: __declspec(deprecated("This conversion is not supported by the library or the header file needed for this conversion is not included. Please refer to the documentation on 'How to: Extend the Marshaling Library' for adding your own marshaling method.")) static _To_Type marshal_as(const _From_Type&) = delete; }; template class error_reporting_helper<_To_Type, _From_Type, /* _Needs_Context */ true> { public: __declspec(deprecated("This conversion requires a marshal_context. Please use a marshal_context for this conversion.")) static _To_Type marshal_as(const _From_Type&) = delete; }; template inline _To_Type _Marshal_as_impl(::std::false_type, const _From_Type& _from_object) { return error_reporting_helper<_To_Type, _From_Type>::marshal_as(_from_object); } template inline System::String^ _Marshal_as_impl(::std::true_type, const char* _from_object) { if (!_from_object) { return nullptr; } return details::InternalAnsiToStringHelper(_from_object, strlen(_from_object)); } template inline System::String^ _Marshal_as_impl(::std::true_type, const wchar_t* _from_object) { if (!_from_object) { return nullptr; } return gcnew System::String(_from_object); } template inline _To_Type marshal_as(const _From_Type& _from_object) { using _Decayed = ::std::decay_t<_From_Type>; return _Marshal_as_impl<_To_Type>(typename ::std::conjunction< ::std::is_same, ::std::disjunction< ::std::is_same, ::std::is_same, // Note: BSTR is actually a typedef for wchar_t*. As a result, BSTR will // get accepted as a null terminated character type sequence here. ::std::is_same, ::std::is_same > >::type{}, _from_object); } ref class context_node_base { public: static const bool _Needs_Context= true; }; template ref class context_node : public context_node_base { public: static const bool _Needs_Context= false; }; template<> ref class context_node : public context_node_base { private: char* _ptr; public: context_node(_Outref_result_maybenull_ _Post_z_ const char*& _to_object, System::String^ _from_object) { _ptr = NULL; if (_from_object == nullptr) { _to_object =nullptr; return; } size_t _size = details::GetAnsiStringSize(_from_object); details::char_buffer _char_buf(_size); if (_char_buf.get() == NULL) { throw gcnew System::InsufficientMemoryException(); } details::WriteAnsiString(_char_buf.get(), _size, _from_object); _ptr = _char_buf.release(); _to_object = _ptr; } ~context_node() { this->!context_node(); } protected: !context_node() { delete [] _ptr; } }; template<> ref class context_node : public context_node_base { private: System::IntPtr _ip; public: context_node(_Outref_ _Post_z_ const wchar_t*& _to_object, System::String^ _from_object) { _ip = System::Runtime::InteropServices::Marshal::StringToHGlobalUni (_from_object); _to_object = static_cast(_ip.ToPointer()); } ~context_node() { this->!context_node(); } protected: !context_node() { if(_ip != System::IntPtr::Zero) System::Runtime::InteropServices::Marshal::FreeHGlobal(_ip); } }; ref class marshal_context { internal: //The list of objects that need to be cleaned up. Basically all those that have a destructor System::Collections::Generic::LinkedList _clean_up_list; template ref class internal_marshaler; template ref class internal_marshaler<_To_Type, _From_Type, true> { public: // This is a supported context conversion. // Convert and add it to the list of cleanup static inline _To_Type marshal_as(const _From_Type& _from, System::Collections::Generic::LinkedList% _clean_up_list) { typedef context_node<_To_Type, _From_Type> _cn; _To_Type _to_object; //Create a context node and assign marshalling result to the 'to' variable _cn^ _obj = gcnew _cn(_to_object, _from); _clean_up_list.AddLast(_obj); return _to_object; } }; template ref class internal_marshaler<_To_Type, _From_Type, false> { public: // This is not a supported context conversion. // This may be a context-free conversion or an unsupported conversion. // Both cases will be handled by the marshal_as object. static inline _To_Type marshal_as(const _From_Type& _from, System::Collections::Generic::LinkedList% _clean_up_list) { (void)_clean_up_list; return ::msclr::interop::marshal_as<_To_Type, _From_Type>(_from); } }; public: template inline _To_Type marshal_as(const _From_Type& _from) { using _Decayed = ::std::decay_t<_From_Type>; using _From_adjusted = ::std::conditional_t<::std::is_same_v<_Decayed, const char*> || ::std::is_same_v<_Decayed, const wchar_t*>, _Decayed, _From_Type>; typedef context_node<_To_Type, _From_adjusted> _cn; return internal_marshaler<_To_Type, _From_adjusted, _cn::_Needs_Context>::marshal_as(_from, _clean_up_list); } template inline _To_Type marshal_as(_From_Type^ _from) { typedef context_node<_To_Type, _From_Type^> _cn; return internal_marshaler<_To_Type, _From_Type^, _cn::_Needs_Context>::marshal_as(_from, _clean_up_list); } template inline _To_Type marshal_as(System::IntPtr _from) { return ::msclr::interop::marshal_as<_To_Type>(_from); } marshal_context() {} ~marshal_context() { for each(Object^ _obj in _clean_up_list) delete _obj; } }; } //namespace interop } //namespace msclr #pragma warning(pop) // error:4996 #endif /* _INC_MSCLR_MARSHAL */