/** \file * BSTR wrapper classes. */ /* * Copyright © 2000-2004 Sofus Mortensen, Michael Geddes * Copyright © 2012 Alexander Lamaison * * This material is provided "as is", with absolutely no warranty * expressed or implied. Any use is at your own risk. Permission to * use or copy this software for any purpose is hereby granted without * fee, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is * granted, provided the above notices are retained, and a notice that * the code was modified is included with the above copyright notice. * * This header is part of Comet version 2. * https://github.com/alamaison/comet */ #ifndef COMET_BSTR_H #define COMET_BSTR_H #include #ifdef COMET_BROKEN_WTYPES #include #endif // COMET_BROKEN_WTYPES #include #include #include #ifndef COMET_GCC_HEADERS #include #endif // COMET_GCC_HEADERS #include #include #undef max #include #include #include #include #include #include #include #include #pragma warning(push) #pragma warning(disable : 4522 4521) #pragma comment( lib, "oleaut32" ) namespace comet { #ifndef NORM_IGNOREKASHIDA #define NORM_IGNOREKASHIDA 0x00040000 #endif // NORM_IGNOREKASHIDA /*! \addtogroup COMType */ //@{ //! Comparsion flags. /*! Can be used with \link comet::bstr_t::cmp cmp \endlink or the comparsion functors. \sa cmp less less_equal greater greater_equal equal_to not_equal_to */ enum compare_flags_t { cf_ignore_case = NORM_IGNORECASE, //!< Ignore case. cf_ingore_nonspace = NORM_IGNORENONSPACE, //!< Ignore nonspacing chars. cf_ignore_symbols = NORM_IGNORESYMBOLS, //!< Ignore symbols. cf_ignore_width = NORM_IGNOREWIDTH, //!< Ignore string width. cf_ignore_kanatype = NORM_IGNOREKANATYPE, //!< Ignore Kana type. cf_ignore_kashida = NORM_IGNOREKASHIDA //!< Ignore Arabic kashida characters. }; //@} namespace impl { inline const wchar_t* null_to_empty(const wchar_t* s) { return s ? s : L""; } } // namespace /*! \addtogroup COMType */ //@{ /*! \class bstr_t bstr.h comet/bstr.h * BSTR Wrapper. * \sa bstr_t */ class bstr_t { public: typedef wchar_t value_type; #if !(defined(_STLP_DEBUG) || (defined(_HAS_ITERATOR_DEBUGGING)) && _MSC_VER >= 1400) typedef std::wstring::iterator iterator; typedef std::wstring::const_iterator const_iterator; typedef std::wstring::reverse_iterator reverse_iterator; typedef std::wstring::const_reverse_iterator const_reverse_iterator; #else // _STLP_DEBUG typedef wchar_t *iterator; typedef const wchar_t *const_iterator; #if defined(COMET_STD_ITERATOR) typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #else // workaround for broken reverse_iterator implementations due to no partial specialisation typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #endif #endif // _STLP_DEBUG typedef std::wstring::size_type size_type; typedef std::wstring::difference_type difference_type; typedef wchar_t& reference; typedef const wchar_t const_reference; private: BSTR str_; void construct() { str_ = 0; } void construct(BSTR s, bool copy) throw(std::bad_alloc) { if (copy) str_ = copy_str(s); else str_ = s; } void construct(const wchar_t* s) throw(std::bad_alloc) { str_ = copy_str(s); } void construct(const wchar_t* s, size_t len) throw(std::bad_alloc) { str_ = copy_str(s, len); } void construct(const char* s) throw(std::bad_alloc, std::runtime_error) { convert_str(s, -1); } void construct(const char* s, size_t len) { if (len >= static_cast(std::numeric_limits::max())) throw std::length_error( "String exceeded maximum length for conversion"); convert_str(s, static_cast(len+1)); } void construct(const uuid_t& u, bool braces) { str_ = impl::bad_alloc_check(::SysAllocStringLen(0, braces?38:36)); u.copy_to_str(str_+(braces?1:0)); if (braces) { str_[0]=L'{'; str_[37]=L'}'; } } void construct(const wchar_t* s1, size_t l1, const wchar_t* s2, size_t l2) throw(std::bad_alloc) { str_ = impl::bad_alloc_check(::SysAllocStringLen(NULL, UINT(l1+l2))); if (l1) memcpy(str_, s1, sizeof(wchar_t)*(l1)); if (l2) memcpy(str_+l1, s2, sizeof(wchar_t)*(1+l2)); } void destroy() throw() { if (str_) ::SysFreeString(str_); } bool is_regular() const throw() { return !str_ || length() == wcslen(str_); } static BSTR copy_str(const wchar_t* src) throw(std::bad_alloc) { return src ? impl::bad_alloc_check(::SysAllocString(src)) : 0; } static BSTR copy_str(const wchar_t* src, size_t len) throw(std::bad_alloc) { return src ? impl::bad_alloc_check(::SysAllocStringLen(src, UINT(len))) : 0; } static BSTR copy_str(BSTR src) throw(std::bad_alloc) { return src ? impl::bad_alloc_check(::SysAllocStringLen(src, ::SysStringLen(src))) : 0; } void convert_str(const char* s, int l) throw(std::bad_alloc, std::runtime_error) { if (s != 0) { #if defined(_MBCS) || !defined(COMET_NO_MBCS) int wl = ::MultiByteToWideChar(CP_ACP, 0, s, l, NULL,0); #else int wl = ((l>=0)?l: (1+strlen(s))); COMET_ASSERT( wl == ::MultiByteToWideChar( CP_ACP, 0, s, l, NULL,0)); #endif str_ = impl::bad_alloc_check(::SysAllocStringLen(0, wl - 1)); if (::MultiByteToWideChar(CP_ACP, 0, s, l, str_, wl) == 0) { destroy(); throw std::runtime_error("MultiByteToWideChar has failed"); } } else str_ = 0; } public: /*! Default constructor Constructs a null string. */ bstr_t() throw() { construct(); } //! Copy constructor /*! \param s String initialise bstr_t from. \exception std::bad_alloc On memory exhaustion std::bad_alloc is thrown. */ bstr_t(const bstr_t& s) throw(std::bad_alloc) { construct(s.str_, true); } //! Construct string from const wchar_t* /*! \param s String to initialise bstr_t from. \exception std::bad_alloc On memory exhaustion std::bad_alloc is thrown. */ bstr_t(const wchar_t* s) throw(std::bad_alloc) { construct(s); } bstr_t(const wchar_t* s, size_t len) throw(std::bad_alloc) { construct(s, len); } //! Construct string from const char* /*! \param s String to initialise bstr_t from. \exception std::bad_alloc On memory exhaustion std::bad_alloc is thrown. \exception std::runtime_error Should string conversion fail, std::runtime_error will be thrown. */ bstr_t(const char* s) throw(std::runtime_error) { construct(s); } bstr_t(const char* s, size_t len) throw(std::bad_alloc) { construct(s, len); } //! Construct string from const std::string& /*! \param s String to initialise bstr_t from. \exception std::bad_alloc On memory exhaustion std::bad_alloc is thrown. \exception std::length_error If this given string is too long to be converted, std::length_error is thrown. \exception std::runtime_error Should string conversion fail, std::runtime_error is thrown. */ bstr_t(const std::string& s) { construct(s.c_str(), s.length()); } //! Construct string from BSTR /*! Takes ownership of specified BSTR. To prevent misuse the BSTR must be wrapped using auto_attach. \code bstr_t bs( auto_attach( myBSTR ) ); \endcode \param s String to initialise bstr_t from. */ bstr_t(const impl::auto_attach_t& s) throw() { construct(s.get(), false); } //! Construct string from const std::wstring& /*! \param s String to initialise bstr_t from. \exception std::bad_alloc On memory exhaustion std::bad_alloc is thrown. */ bstr_t(const std::wstring& s) throw(std::bad_alloc) { construct(s.c_str(), s.length()); } explicit bstr_t(size_type sz, wchar_t c) throw(std::bad_alloc) { str_ = impl::bad_alloc_check(::SysAllocStringLen(0, UINT(sz))); std::fill(begin(), end(), c); } explicit bstr_t(size_type sz) throw(std::bad_alloc) { str_ = impl::bad_alloc_check(::SysAllocStringLen(0, UINT(sz))); } template explicit bstr_t(IT first, IT last) { str_ = 0; assign(first, last); } explicit bstr_t(const uuid_t& u, bool braces = false) { construct(u, braces); } private: bstr_t(const wchar_t* s1, size_t l1, const wchar_t* s2, size_t l2) throw(std::bad_alloc) { construct(s1, l1, s2, l2); } public: //! Destructor /*! Deletes the wrapped BSTR. */ ~bstr_t() throw() { destroy(); } //! Swap void swap(bstr_t& x) throw() { std::swap(str_, x.str_); } //! Explicit conversion to const wchar_t* const wchar_t* c_str() const throw() { return impl::null_to_empty(str_); } //! Explicit conversion to std::wstring std::wstring w_str() const throw() { return impl::null_to_empty(str_); } #ifdef _MBCS #ifndef COMET_NO_MBCS_WARNING #pragma message( "Warning: _MBCS is defined. bstr_t::s_str may return an std::string containing multibyte characters" ) #endif #endif //! Explicit conversion to std::string std::string s_str() const { if (is_empty()) return std::string(); if (length() > static_cast(std::numeric_limits::max())) throw std::length_error("String is too large to be converted"); int ol = static_cast(length()); #if defined(_MBCS) || !defined(COMET_NO_MBCS) // Calculate the required length of the buffer int l = WideCharToMultiByte(CP_ACP, 0, str_, ol, NULL, 0, NULL, NULL); #else // _MBCS int l = ol; COMET_ASSERT( l == WideCharToMultiByte(CP_ACP, 0, str_, ol, NULL, 0, NULL, NULL)); #endif // _MBCS // Create the buffer std::string rv(l, std::string::value_type()); // Do the conversion. if (0 == WideCharToMultiByte( CP_ACP, 0, str_, ol, &rv[0], l, NULL, NULL)) { DWORD err = GetLastError(); raise_exception(HRESULT_FROM_WIN32(err)); } return rv; } //! Explicit conversion to "tstring". #ifdef _UNICODE std::wstring t_str() const { return w_str(); } #else std::string t_str() const { return s_str(); } #endif //! Implicit conversion to std::wstring operator std::wstring() const { return w_str(); } //! Implicit conversion to std::string operator std::string() const { return s_str(); } //! Returns true if and only if wrapped str is null bool is_null() const throw() { return str_ == 0; } /** Returns true if and only if wrapped str has zero length. */ bool is_empty() const throw() { return length() == 0; } //! Returns true if and only if wrapped str has zero length. bool empty() const throw() { return length() == 0; } //! Returns length of wrapped string. size_t length() const throw() { return is_null() ? 0 : ::SysStringLen(str_); } size_t size() const throw() { return length(); } /*! \internal */ BSTR get_raw() const { return str_; } friend std::basic_ostream &operator<<(std::basic_ostream &os, const bstr_t &val) { os << val.s_str(); return os; } friend std::basic_ostream &operator<<(std::basic_ostream &os, const bstr_t &val) { os << val.w_str(); return os; } /// \name Boolean operators //@{ bool operator==(const wchar_t* s) const { return 0 == wcscmp(c_str(), impl::null_to_empty(s) ) && is_regular(); } bool operator!=(const wchar_t* s) const { return !operator==(s); } bool operator<(const wchar_t* s) const { return wcscmp(c_str(), impl::null_to_empty(s)) < 0 && is_regular(); } bool operator>(const wchar_t* s) const { return wcscmp(c_str(), impl::null_to_empty(s)) > 0 && !is_regular(); } bool operator>=(const wchar_t* s) const { return !operator<(s); } bool operator<=(const wchar_t* s) const { return !operator>(s); } bool operator==(const std::wstring& s) const { size_t l = length(); if (l != s.length()) return false; return 0 == memcmp(str_, s.c_str(), sizeof(wchar_t)*l); } bool operator!=(const std::wstring& s) const { return !operator==(s); } bool operator<(const std::wstring& s) const { return std::lexicographical_compare(str_, str_+length(), s.begin(), s.end()); } bool operator>(const std::wstring& s) const { return std::lexicographical_compare(str_, str_+length(), s.begin(), s.end(), std::greater()); } bool operator>=(const std::wstring& s) const { return !operator<(s); } bool operator<=(const std::wstring& s) const { return !operator>(s); } bool operator==(const bstr_t& s) const { if (str_ == 0 && s.str_ == 0) return true; return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_EQ; } bool operator!=(const bstr_t& s) const { return !operator==(s); } bool operator<(const bstr_t& s) const { if (str_ == 0) { return s.str_ != 0; } if (s.str_ == 0) return false; return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_LT; } bool operator>(const bstr_t& s) const { if (str_ == 0) { return s.str_ != 0; } if (s.str_ == 0) return false; return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_GT; } bool operator>=(const bstr_t& s) const { return !operator<(s); } bool operator<=(const bstr_t& s) const { return !operator>(s); } //@} //! String comparsion function. /*! \param s String to compare \param flags Comparison Flags \retval <0 if less \retval 0 if Equal \retval >0 if greater */ int cmp(const bstr_t& s, compare_flags_t flags = compare_flags_t(0)) const { HRESULT res = ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), flags); switch(res) { case VARCMP_EQ: return 0; case VARCMP_GT: return 1; case VARCMP_LT: return -1; case VARCMP_NULL: return ((str_==0)?0:1) - ((s.str_==0)?0:1); } if (str_==0 || s.str_ ==0) return ((str_==0)?0:1) - ((s.str_==0)?0:1); raise_exception(res); return 0; } //!\name Comparison Functors //@{ //! Less Functor. /*! Useful for STL containers. \code typedef stl::map < comet::bstr_t, long, bstr_t::less > string_long_map; \endcode \param CF comparison flags. \relates bstr_t */ template struct less : std::binary_function< bstr_t,bstr_t,bool>{ /// Functor. bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) <0; } }; //! less or equal functor. /*! \relates bstr_t */ template struct less_equal : std::binary_function< bstr_t,bstr_t,bool> { /// Functor. bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) <=0; } }; //! greater functor. /*! \relates bstr_t */ template struct greater : std::binary_function< bstr_t,bstr_t,bool> { /// Functor. bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) > 0; } }; //! greater or equal functor. /*! \relates bstr_t */ template struct greater_equal : std::binary_function< bstr_t,bstr_t,bool> { /// Functor. bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) >=0; } }; //! equality functor. template struct equal_to : std::binary_function< bstr_t,bstr_t,bool> { bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) == 0; } }; //! Inequality functor. /*! \relates bstr_t */ template struct not_equal_to : std::binary_function< bstr_t,bstr_t,bool>{ /// Functor. bool operator()(const bstr_t& l, const bstr_t& r) const { return l.cmp(r, CF) != 0; } }; //@} iterator begin() { return iterator(str_); } iterator end() { return iterator(str_ + length()); } const_iterator begin() const { return const_iterator(str_); } const_iterator end() const { return const_iterator(str_ + length()); } reverse_iterator rbegin() { return reverse_iterator(str_); } reverse_iterator rend() { return reverse_iterator(str_ + length()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(str_); } const_reverse_iterator rend() const { return const_reverse_iterator(str_ + length()); } reference at(size_type i) { rangecheck(i); return str_[i]; } const_reference at(size_type i) const { rangecheck(i); return str_[i]; } private: // check range (may be private because it is static) void rangecheck (size_type i) const { if (i >= length()) { throw std::range_error("bstr_t"); } } public: const_reference operator[](size_type idx) const { return str_[idx]; } reference operator[](size_type idx) { return str_[idx]; } //! Assign string to be \p sz of character \p c . void assign(size_type sz, wchar_t c) throw(std::bad_alloc) { bstr_t t(sz, c); swap(t); } //! Assign string from two iterators. template void assign(IT first, IT last) { bstr_t t( std::distance(first, last) ); #pragma warning(push) #pragma warning(disable:4996) std::copy(first, last, t.begin()); #pragma warning(pop) swap(t); } //! Assignment operator from any (non integer) constructable. template bstr_t& operator=(const T& x) { COMET_STATIC_ASSERT( type_traits::is_integer::result == false ); bstr_t t(x); swap(t); return *this; } //! Default assignment. bstr_t& operator=(const bstr_t& x) throw(std::bad_alloc) { bstr_t t(x); swap(t); return *this; } //! Concat operation bstr_t operator+(const bstr_t& s) const throw(std::bad_alloc) { return bstr_t(str_, length(), s.str_, s.length()); } //! Concat with const wchar_t* bstr_t operator+(const wchar_t* s) const throw(std::bad_alloc) { return bstr_t(str_, length(), s, wcslen(s)); } //! Concat with std::wstring bstr_t operator+(const std::wstring& s) const throw(std::bad_alloc) { return bstr_t(str_, length(), s.c_str(), s.length()); } //! Concat assignment bstr_t& operator+=(const bstr_t& s) throw(std::bad_alloc) { bstr_t t(str_, length(), s.str_, s.length()); swap(t); return *this; } //! Concat assignment with const wchar_t* bstr_t& operator+=(const wchar_t* s) throw(std::bad_alloc) { bstr_t t(str_, length(), s, wcslen(s)); swap(t); return *this; } //! Concat assignment with std::wstring bstr_t& operator+=(const std::wstring& s) throw(std::bad_alloc) { bstr_t t(str_, length(), s.c_str(), s.length()); swap(t); return *this; } // Detach a raw BSTR from it's wrapper - detach function is dangerous. BSTR detach() { BSTR s(str_); str_ = 0; return s; } public: //!\name Create a reference to a BSTR /*! Creates a bstr_t that is a reference to the BSTR. It will not be reference counted and will not be deleted when the bstr_t goes out of scope. This is used by the interface wrappers for [in] BSTR's. Typically clients do not need create_reference. */ //@{ static const bstr_t& create_const_reference(const BSTR& s) throw() { return *reinterpret_cast(&s); } static bstr_t& create_reference(BSTR& s) throw() { return *reinterpret_cast(&s); } //@} //! Detaches specified bstr static BSTR detach(bstr_t& s) { return s.detach(); } /*! \internal */ template static BSTR detach(const T& s) { return bstr_t(s).detach(); } /*! \internal */ BSTR* get_ptr_to_raw() const { return const_cast(&str_); } //! [in] adapter. /*! Used when calling raw interfaces that require an [in] BSTR argument. \code bstr_t bs; HRESULT hr = pRawInterface->raw_Method(bs.in()); \endcode Only use this wrapper when forced to deal with raw interface. */ BSTR in() const throw() { return str_; } //! [in] adapter. /*! Used when calling raw interfaces that require an [in] BSTR* argument. \code bstr_t bs; HRESULT hr = pRawInterface->raw_Method(bs.in_ptr()); \endcode Only use this wrapper when forced to deal with raw interface. */ BSTR* in_ptr() const throw() { return const_cast(&str_); } //! [out] adapter. /*! Used when calling raw interfaces that require an [out] BSTR * argument. \code bstr_t bs; HRESULT hr = pRawInterface->raw_MethodThatReturnsBSTR(bs.out()); \endcode Only use this wrapper when forced to deal with raw interface. */ BSTR* out() throw() { destroy(); return &str_; } //! [in, out] adapter. /*! Used when calling raw interfaces that require an [in, out] BSTR * argument. \code bstr_t bs; HRESULT hr = pRawInterface->raw_MethodThatChangesBSTR(bs.inout()); \endcode Only use this wrapper when forced to deal with raw interface. \note If the wrapped BSTR is shared. The bstr_t is copied so that only this version is modified. \exception std::bad_alloc Throws std::bad_alloc if the bstr_t is being copied and memory is exhausted. */ BSTR* inout() throw(std::bad_alloc) { return &str_; } friend bstr_t operator+(const std::wstring& s, const bstr_t& t) throw(std::bad_alloc); friend bstr_t operator+(const wchar_t* s, const bstr_t& t) throw(std::bad_alloc); }; //! Concat operation inline bstr_t operator+(const std::wstring& s, const bstr_t& t) throw(std::bad_alloc) { return bstr_t(s.c_str(), s.length(), t.str_, t.length()); } //! Concat operation inline bstr_t operator+(const wchar_t* s, const bstr_t& t) throw(std::bad_alloc) { return bstr_t(s, wcslen(s), t.str_, t.length()); } //@} /*! \name Boolean Operators on String * \relates bstr_t */ //@{ inline bool operator==(const wchar_t* s1, const bstr_t& s2) throw() { return s2 == s1; } inline bool operator!=(const wchar_t* s1, const bstr_t& s2) throw() { return s2 != s1; } inline bool operator<(const wchar_t* s1, const bstr_t& s2) throw() { return s2 > s1; } inline bool operator>(const wchar_t* s1, const bstr_t& s2) throw() { return s2 < s1; } inline bool operator<=(const wchar_t* s1, const bstr_t& s2) throw() { return s2 >= s1; } inline bool operator>=(const wchar_t* s1, const bstr_t& s2) throw() { return s2 <= s1; } inline bool operator==(const std::wstring& s1, const bstr_t& s2) throw() { return s2 == s1; } inline bool operator!=(const std::wstring& s1, const bstr_t& s2) throw() { return s2 != s1; } inline bool operator<(const std::wstring& s1, const bstr_t& s2) throw() { return s2 > s1; } inline bool operator>(const std::wstring& s1, const bstr_t& s2) throw() { return s2 < s1; } inline bool operator<=(const std::wstring& s1, const bstr_t& s2) throw() { return s2 >= s1; } inline bool operator>=(const std::wstring& s1, const bstr_t& s2) throw() { return s2 <= s1; } //@} // Implementation of uuid_t construct from bstr. inline uuid_t::uuid_t(const bstr_t& bs) { if (init_from_str(bs.c_str(), bs.length()) == false) throw std::runtime_error(err_msg()); } inline currency_t& currency_t::parse( const bstr_t &str, LCID locale ) { VarCyFromStr( str.in(), locale, 0, &cy_ ) | raise_exception; return *this; } } // namespace namespace { COMET_STATIC_ASSERT( sizeof(comet::bstr_t) == sizeof(BSTR) ); COMET_STATIC_ASSERT( sizeof(comet::bstr_t) == sizeof(BSTR) ); } namespace std { template<> inline void swap( comet::bstr_t& x, comet::bstr_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } } #include #pragma warning(pop) #endif /* COMET_BSTR_H */