avmedia/source/win/framegrabber.cxx                       |   10 
 basic/source/sbx/sbxdec.cxx                               |    9 
 connectivity/source/drivers/ado/AConnection.cxx           |    3 
 connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx |   11 
 connectivity/source/drivers/ado/AUsers.cxx                |    3 
 connectivity/source/drivers/ado/AView.cxx                 |    8 
 connectivity/source/drivers/ado/AViews.cxx                |    4 
 connectivity/source/drivers/ado/Aolevariant.cxx           |   62 ----
 connectivity/source/drivers/ado/Awrapado.cxx              |  207 +++++++-------
 connectivity/source/drivers/ado/adoimp.cxx                |    5 
 connectivity/source/inc/ado/Aolevariant.hxx               |   21 -
 connectivity/source/inc/ado/adoimp.hxx                    |    5 
 dbaccess/source/ui/dlg/adodatalinks.cxx                   |   14 
 extensions/source/activex/SOActiveX.cxx                   |   16 -
 extensions/source/activex/SOActiveX.h                     |    2 
 extensions/source/config/WinUserInfo/WinUserInfoBe.cxx    |   23 -
 include/systools/win32/oleauto.hxx                        |   84 +++++
 17 files changed, 244 insertions(+), 243 deletions(-)

New commits:
commit a2c3ef6d8108355ce5daf6ff72310ac93ae745f0
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Mar 22 21:18:04 2022 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Tue Mar 22 22:23:56 2022 +0100

    Move BSTR wrapper to systools, and fix some wrong BSTR uses
    
    BSTR is documented [1] to be prefixed by a 32-bit integer specifying
    its length *in bytes* (not characters), so passing wchar_t* as BSTR
    is wrong, and the length member of rtl_uString can't substitute the
    proper BSTR length, since rtl_uString::length specifies length in
    characters. Any code taking BSTR and using SysStringLen to get its
    length would only get half of the passed OUString data.
    
    In dbaccess/source/ui/dlg/adodatalinks.cxx, the abovementioned
    error was implemented. OTOH, OLEVariant::getByteSequence() in
    connectivity/source/drivers/ado/Aolevariant.cxx passed BSTR from
    tagVARIANT to ctor of OLEString, which resulted in the BSTR being
    freed in both dtors of OLEString and OLEVariant (the latter calls
    VariantClear, which itself clears string when vtfield is VT_BSTR).
    
    [1] 
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr
    
    Change-Id: Iedbd62b20133644258af3660616add7b63cac258
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131950
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/avmedia/source/win/framegrabber.cxx 
b/avmedia/source/win/framegrabber.cxx
index a2adc90b7072..547b3ffa6a59 100644
--- a/avmedia/source/win/framegrabber.cxx
+++ b/avmedia/source/win/framegrabber.cxx
@@ -38,6 +38,7 @@
 #include <vcl/graph.hxx>
 #include <vcl/dibtools.hxx>
 #include <o3tl/char16_t2wchar_t.hxx>
+#include <systools/win32/oleauto.hxx>
 
 constexpr OUStringLiteral AVMEDIA_WIN_FRAMEGRABBER_IMPLEMENTATIONNAME = 
u"com.sun.star.comp.avmedia.FrameGrabber_DirectX";
 constexpr OUStringLiteral AVMEDIA_WIN_FRAMEGRABBER_SERVICENAME = 
u"com.sun.star.media.FrameGrabber_DirectX";
@@ -69,15 +70,8 @@ sal::systools::COMReference<IMediaDet> implCreateMediaDet( 
const OUString& rURL
         if( osl::FileBase::getSystemPathFromFileURL( rURL, aLocalStr )
             == osl::FileBase::E_None )
         {
-            BSTR bstrFilename = SysAllocString(o3tl::toW(aLocalStr.getStr()));
-            if( !SUCCEEDED( pDet->put_Filename( bstrFilename ) ) )
-            {
-                // Shouldn't we free this string unconditionally, not only in 
case of failure?
-                // I cannot find information why do we pass a newly allocated 
BSTR to the put_Filename
-                // and if it frees the string internally
-                SysFreeString(bstrFilename);
+            if( !SUCCEEDED( pDet->put_Filename(sal::systools::BStr(aLocalStr)) 
) )
                 pDet.clear();
-            }
         }
     }
 
diff --git a/basic/source/sbx/sbxdec.cxx b/basic/source/sbx/sbxdec.cxx
index 3c8f1eec4d78..cad5601f2f7b 100644
--- a/basic/source/sbx/sbxdec.cxx
+++ b/basic/source/sbx/sbxdec.cxx
@@ -17,7 +17,11 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <sal/config.h>
+#ifdef _WIN32
 #include <o3tl/char16_t2wchar_t.hxx>
+#include <systools/win32/oleauto.hxx>
+#endif
 
 #include <basic/sberrors.hxx>
 #include "sbxconv.hxx"
@@ -338,7 +342,7 @@ void SbxDecimal::getString( OUString& rString )
 #ifdef _WIN32
     static LCID nLANGID = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
 
-    BSTR pBStr = nullptr;
+    sal::systools::BStr pBStr;
     // VarBstrFromDec allocates new BSTR that needs to be released with 
SysFreeString
     HRESULT hResult = VarBstrFromDec( &maDec, nLANGID, 0, &pBStr );
     if( hResult == S_OK )
@@ -362,8 +366,7 @@ void SbxDecimal::getString( OUString& rString )
                 i++;
             }
         }
-        rString = o3tl::toU( pBStr );
-        SysFreeString( pBStr );
+        rString = pBStr;
     }
 #else
     (void)rString;
diff --git a/connectivity/source/drivers/ado/AConnection.cxx 
b/connectivity/source/drivers/ado/AConnection.cxx
index c393d6a4dc3e..0287ee9e050f 100644
--- a/connectivity/source/drivers/ado/AConnection.cxx
+++ b/connectivity/source/drivers/ado/AConnection.cxx
@@ -35,6 +35,7 @@
 #include <comphelper/servicehelper.hxx>
 #include <connectivity/dbexception.hxx>
 #include <osl/file.hxx>
+#include <systools/win32/oleauto.hxx>
 #include <strings.hrc>
 
 using namespace dbtools;
@@ -64,7 +65,7 @@ OConnection::OConnection(ODriver*   _pDriver)
         HRESULT hr = pIUnknown->CreateInstanceLic(nullptr,
                                             nullptr,
                                             ADOS::IID_ADOCONNECTION_21,
-                                            ADOS::GetKeyStr().asBSTR(),
+                                            ADOS::GetKeyStr(),
                                             
reinterpret_cast<void**>(&m_aAdoConnection));
 
         if( !FAILED( hr ) )
diff --git a/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx 
b/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx
index 037fdb6fe41d..b7872070603f 100644
--- a/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx
+++ b/connectivity/source/drivers/ado/ADatabaseMetaDataImpl.cxx
@@ -29,6 +29,9 @@
 #include <ado/AIndex.hxx>
 #include <ado/AKey.hxx>
 #include <ado/ATable.hxx>
+
+#include <systools/win32/oleauto.hxx>
+
 #include <com/sun/star/sdbc/DataType.hpp>
 #include <com/sun/star/sdbc/ProcedureResult.hpp>
 #include <com/sun/star/sdbc/ColumnValue.hpp>
@@ -505,10 +508,10 @@ OUString WpADOCatalog::GetObjectOwner(std::u16string_view 
_rName, ObjectTypeEnum
 {
     OLEVariant _rVar;
     _rVar.setNoArg();
-    OLEString aBSTR;
-    OLEString sStr1(_rName);
-    pInterface->GetObjectOwner(sStr1.asBSTR(),_eNum,_rVar,aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    sal::systools::BStr sStr1(_rName);
+    pInterface->GetObjectOwner(sStr1, _eNum, _rVar, &aBSTR);
+    return OUString(aBSTR);
 }
 
 void OAdoTable::fillPropertyValues()
diff --git a/connectivity/source/drivers/ado/AUsers.cxx 
b/connectivity/source/drivers/ado/AUsers.cxx
index 9d0dab6e8d4c..4074330915a6 100644
--- a/connectivity/source/drivers/ado/AUsers.cxx
+++ b/connectivity/source/drivers/ado/AUsers.cxx
@@ -27,6 +27,7 @@
 #include <comphelper/servicehelper.hxx>
 #include <comphelper/types.hxx>
 #include <connectivity/dbexception.hxx>
+#include <systools/win32/oleauto.hxx>
 #include <strings.hrc>
 
 using namespace comphelper;
@@ -61,7 +62,7 @@ sdbcx::ObjectType OUsers::appendObject( const OUString& 
_rForName, const Referen
         m_pCatalog->getConnection()->throwGenericSQLException( 
STR_INVALID_USER_DESCRIPTOR_ERROR,static_cast<XTypeProvider*>(this) );
 
     ADOUsers* pUsers = m_aCollection;
-    
pUsers->Append(OLEVariant(pUser->getImpl()),OLEString(pUser->getPassword()).asBSTR());
+    pUsers->Append(OLEVariant(pUser->getImpl()), 
sal::systools::BStr(pUser->getPassword()));
 
     return createObject( _rForName );
 }
diff --git a/connectivity/source/drivers/ado/AView.cxx 
b/connectivity/source/drivers/ado/AView.cxx
index 15f6a5cc9a6d..3b88116bcf2e 100644
--- a/connectivity/source/drivers/ado/AView.cxx
+++ b/connectivity/source/drivers/ado/AView.cxx
@@ -23,6 +23,8 @@
 #include <ado/Awrapado.hxx>
 #include <comphelper/servicehelper.hxx>
 #include <comphelper/types.hxx>
+#include <systools/win32/oleauto.hxx>
+
 #include <TConnection.hxx>
 
 
@@ -76,9 +78,9 @@ void OAdoView::getFastPropertyValue(Any& rValue,sal_Int32 
nHandle) const
                     if(!aVar.isNull() && !aVar.isEmpty())
                     {
                         ADOCommand* pCom = 
static_cast<ADOCommand*>(aVar.getIDispatch());
-                        OLEString aBSTR;
-                        pCom->get_CommandText(aBSTR.getAddress());
-                        rValue <<= aBSTR.asOUString();
+                        sal::systools::BStr aBSTR;
+                        pCom->get_CommandText(&aBSTR);
+                        rValue <<= OUString(aBSTR);
                     }
                 }
                 break;
diff --git a/connectivity/source/drivers/ado/AViews.cxx 
b/connectivity/source/drivers/ado/AViews.cxx
index 4764833c8829..38f9ae4d7a36 100644
--- a/connectivity/source/drivers/ado/AViews.cxx
+++ b/connectivity/source/drivers/ado/AViews.cxx
@@ -28,6 +28,8 @@
 #include <comphelper/types.hxx>
 #include <connectivity/dbexception.hxx>
 #include <rtl/ref.hxx>
+#include <systools/win32/oleauto.hxx>
+
 #include <strings.hrc>
 
 using namespace ::comphelper;
@@ -74,7 +76,7 @@ sdbcx::ObjectType OViews::appendObject( const OUString& 
_rForName, const Referen
     aCommand.put_Name(sName);
     
aCommand.put_CommandText(getString(descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_COMMAND))));
     ADOViews* pViews = m_aCollection;
-    if(FAILED(pViews->Append(OLEString(sName).asBSTR(),aCommand)))
+    if (FAILED(pViews->Append(sal::systools::BStr(sName), aCommand)))
         
ADOS::ThrowException(m_pCatalog->getConnection()->getConnection(),static_cast<XTypeProvider*>(this));
 
     OTables* pTables = 
static_cast<OTables*>(static_cast<OCatalog&>(m_rParent).getPrivateTables());
diff --git a/connectivity/source/drivers/ado/Aolevariant.cxx 
b/connectivity/source/drivers/ado/Aolevariant.cxx
index 8b6ad10b4662..c082c1987ff0 100644
--- a/connectivity/source/drivers/ado/Aolevariant.cxx
+++ b/connectivity/source/drivers/ado/Aolevariant.cxx
@@ -37,64 +37,6 @@ using namespace com::sun::star::uno;
 using namespace com::sun::star::bridge::oleautomation;
 using namespace connectivity::ado;
 
-OLEString::OLEString()
-    :m_sStr(nullptr)
-{
-}
-OLEString::OLEString(const BSTR& _sBStr)
-    :m_sStr(_sBStr)
-{
-}
-OLEString::OLEString(std::u16string_view _sBStr)
-{
-    m_sStr = SysAllocStringLen(o3tl::toW(_sBStr.data()), _sBStr.length());
-}
-OLEString::~OLEString()
-{
-    if(m_sStr)
-        ::SysFreeString(m_sStr);
-}
-OLEString& OLEString::operator=(std::u16string_view _rSrc)
-{
-    if(m_sStr)
-        ::SysFreeString(m_sStr);
-    m_sStr = SysAllocStringLen(o3tl::toW(_rSrc.data()), _rSrc.length());
-    return *this;
-}
-OLEString& OLEString::operator=(const OLEString& _rSrc)
-{
-    if(this != &_rSrc)
-    {
-        if(m_sStr)
-            ::SysFreeString(m_sStr);
-        m_sStr = ::SysAllocString(_rSrc.m_sStr);
-    }
-    return *this;
-}
-OLEString& OLEString::operator=(const BSTR& _rSrc)
-{
-    if(m_sStr)
-        ::SysFreeString(m_sStr);
-    m_sStr = _rSrc;
-    return *this;
-}
-OUString OLEString::asOUString() const
-{
-    return (m_sStr != nullptr) ? 
OUString(o3tl::toU(m_sStr),::SysStringLen(m_sStr)) : OUString();
-}
-BSTR OLEString::asBSTR() const
-{
-    return m_sStr;
-}
-BSTR* OLEString::getAddress()
-{
-    return &m_sStr;
-}
-sal_Int32 OLEString::length() const
-{
-    return (m_sStr != nullptr) ? ::SysStringLen(m_sStr) : 0;
-}
-
 OLEVariant::OLEVariant()
 {
     VariantInit(this);
@@ -427,8 +369,8 @@ css::uno::Sequence< sal_Int8 > 
OLEVariant::getByteSequence() const
     css::uno::Sequence< sal_Int8 > aRet;
     if(V_VT(this) == VT_BSTR)
     {
-        OLEString sStr(V_BSTR(this));
-        aRet = css::uno::Sequence<sal_Int8>(reinterpret_cast<const 
sal_Int8*>(sStr.asBSTR()),sizeof(sal_Unicode)*sStr.length());
+        aRet = 
css::uno::Sequence<sal_Int8>(reinterpret_cast<sal_Int8*>(V_BSTR(this)),
+                                            ::SysStringByteLen(V_BSTR(this)));
     }
     else if(!isNull())
     {
diff --git a/connectivity/source/drivers/ado/Awrapado.cxx 
b/connectivity/source/drivers/ado/Awrapado.cxx
index 44a38090b5d3..c4abecf1db4f 100644
--- a/connectivity/source/drivers/ado/Awrapado.cxx
+++ b/connectivity/source/drivers/ado/Awrapado.cxx
@@ -23,6 +23,7 @@
 #include <comphelper/types.hxx>
 #include <rtl/ustrbuf.hxx>
 #include <sal/log.hxx>
+#include <systools/win32/oleauto.hxx>
 
 using namespace connectivity::ado;
 
@@ -42,16 +43,16 @@ WpADOProperties WpADOConnection::get_Properties() const
 OUString WpADOConnection::GetConnectionString() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_ConnectionString(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_ConnectionString(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOConnection::PutConnectionString(std::u16string_view aCon) const
 {
     assert(pInterface);
-    OLEString bstr(aCon);
-    bool bErg = SUCCEEDED(pInterface->put_ConnectionString(bstr.asBSTR()));
+    sal::systools::BStr bstr(aCon);
+    bool bErg = SUCCEEDED(pInterface->put_ConnectionString(bstr));
 
     return bErg;
 }
@@ -93,8 +94,8 @@ bool WpADOConnection::Close()
 bool WpADOConnection::Execute(std::u16string_view CommandText,OLEVariant& 
RecordsAffected,long Options, WpADORecordset** ppiRset)
 {
     assert(pInterface);
-    OLEString sStr1(CommandText);
-    bool bErg = 
SUCCEEDED(pInterface->Execute(sStr1.asBSTR(),&RecordsAffected,Options,reinterpret_cast<ADORecordset**>(ppiRset)));
+    sal::systools::BStr sStr1(CommandText);
+    bool bErg = 
SUCCEEDED(pInterface->Execute(sStr1,&RecordsAffected,Options,reinterpret_cast<ADORecordset**>(ppiRset)));
     return bErg;
 }
 
@@ -120,10 +121,10 @@ bool WpADOConnection::RollbackTrans( )
 bool WpADOConnection::Open(std::u16string_view ConnectionString, 
std::u16string_view UserID,std::u16string_view Password,long Options)
 {
     assert(pInterface);
-    OLEString sStr1(ConnectionString);
-    OLEString sStr2(UserID);
-    OLEString sStr3(Password);
-    bool bErg = 
SUCCEEDED(pInterface->Open(sStr1.asBSTR(),sStr2.asBSTR(),sStr3.asBSTR(),Options));
+    sal::systools::BStr sStr1(ConnectionString);
+    sal::systools::BStr sStr2(UserID);
+    sal::systools::BStr sStr3(Password);
+    bool bErg = SUCCEEDED(pInterface->Open(sStr1, sStr2, sStr3, Options));
     return bErg;
 }
 
@@ -136,15 +137,15 @@ bool WpADOConnection::GetErrors(ADOErrors** pErrors)
 OUString WpADOConnection::GetDefaultDatabase() const
 {
     assert(pInterface);
-    OLEString aBSTR; pInterface->get_DefaultDatabase(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR; pInterface->get_DefaultDatabase(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOConnection::PutDefaultDatabase(std::u16string_view _bstr)
 {
     assert(pInterface);
-    OLEString bstr(_bstr);
-    bool bErg = SUCCEEDED(pInterface->put_DefaultDatabase(bstr.asBSTR()));
+    sal::systools::BStr bstr(_bstr);
+    bool bErg = SUCCEEDED(pInterface->put_DefaultDatabase(bstr));
 
     return bErg;
 }
@@ -208,15 +209,15 @@ bool WpADOConnection::put_Mode(const ConnectModeEnum 
&eNum)
 OUString WpADOConnection::get_Provider() const
 {
     assert(pInterface);
-    OLEString aBSTR; pInterface->get_Provider(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR; pInterface->get_Provider(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOConnection::put_Provider(std::u16string_view _bstr)
 {
     assert(pInterface);
-    OLEString bstr(_bstr);
-    return SUCCEEDED(pInterface->put_Provider(bstr.asBSTR()));
+    sal::systools::BStr bstr(_bstr);
+    return SUCCEEDED(pInterface->put_Provider(bstr));
 }
 
 sal_Int32 WpADOConnection::get_State() const
@@ -236,9 +237,9 @@ bool WpADOConnection::OpenSchema(SchemaEnum eNum,OLEVariant 
const & Restrictions
 OUString WpADOConnection::get_Version() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Version(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Version(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOCommand::putref_ActiveConnection(const WpADOConnection& rCon)
@@ -263,7 +264,7 @@ void WpADOCommand::Create()
         HRESULT hr = pInterface2->CreateInstanceLic(nullptr,
                                             nullptr,
                                             ADOS::IID_ADOCOMMAND_21,
-                                            ADOS::GetKeyStr().asBSTR(),
+                                            ADOS::GetKeyStr(),
                                             
reinterpret_cast<void**>(&pCommand));
 
         if( !FAILED( hr ) )
@@ -282,16 +283,16 @@ sal_Int32 WpADOCommand::get_State() const
 OUString WpADOCommand::get_CommandText() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_CommandText(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_CommandText(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOCommand::put_CommandText(std::u16string_view aCon)
 {
     assert(pInterface);
-    OLEString bstr(aCon);
-    bool bErg = SUCCEEDED(pInterface->put_CommandText(bstr.asBSTR()));
+    sal::systools::BStr bstr(aCon);
+    bool bErg = SUCCEEDED(pInterface->put_CommandText(bstr));
 
     return bErg;
 }
@@ -334,8 +335,8 @@ ADOParameter* 
WpADOCommand::CreateParameter(std::u16string_view _bstr,DataTypeEn
 {
     assert(pInterface);
     ADOParameter* pPara = nullptr;
-    OLEString bstr(_bstr);
-    bool bErg = 
SUCCEEDED(pInterface->CreateParameter(bstr.asBSTR(),Type,Direction,nSize,Value,&pPara));
+    sal::systools::BStr bstr(_bstr);
+    bool bErg = SUCCEEDED(pInterface->CreateParameter(bstr, Type, Direction, 
nSize, Value, &pPara));
 
     return bErg ? pPara : nullptr;
 }
@@ -366,16 +367,16 @@ CommandTypeEnum WpADOCommand::get_CommandType() const
 OUString WpADOCommand::GetName() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 bool WpADOCommand::put_Name(std::u16string_view Name)
 {
     assert(pInterface);
-    OLEString bstr(Name);
-    bool bErg = SUCCEEDED(pInterface->put_Name(bstr.asBSTR()));
+    sal::systools::BStr bstr(Name);
+    bool bErg = SUCCEEDED(pInterface->put_Name(bstr));
 
     return bErg;
 }
@@ -388,17 +389,17 @@ bool WpADOCommand::Cancel()
 OUString WpADOError::GetDescription() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Description(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Description(&aBSTR);
+    return OUString(aBSTR);
 }
 
 OUString WpADOError::GetSource() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Source(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Source(&aBSTR);
+    return OUString(aBSTR);
 }
 
 sal_Int32 WpADOError::GetNumber() const
@@ -412,9 +413,9 @@ sal_Int32 WpADOError::GetNumber() const
 OUString WpADOError::GetSQLState() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_SQLState(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_SQLState(&aBSTR);
+    return OUString(aBSTR);
 }
 
 sal_Int32 WpADOError::GetNativeError() const
@@ -468,9 +469,9 @@ sal_Int32 WpADOField::GetDefinedSize() const
 OUString WpADOField::GetName() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 DataTypeEnum WpADOField::GetADOType() const
@@ -620,9 +621,9 @@ bool WpADOProperty::PutValue(const OLEVariant &aValVar)
 OUString WpADOProperty::GetName() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 DataTypeEnum WpADOProperty::GetADOType() const
@@ -655,7 +656,7 @@ bool WpADOProperty::PutAttributes(sal_Int32 _nDefSize)
         HRESULT hr = pInterface2->CreateInstanceLic(nullptr,
                                             nullptr,
                                             ADOS::IID_ADORECORDSET_21,
-                                            ADOS::GetKeyStr().asBSTR(),
+                                            ADOS::GetKeyStr(),
                                             reinterpret_cast<void**>(&pRec));
 
         if( !FAILED( hr ) )
@@ -892,9 +893,9 @@ bool WpADORecordset::UpdateBatch(AffectEnum AffectRecords)
 OUString WpADOParameter::GetName() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 DataTypeEnum WpADOParameter::GetADOType() const
@@ -976,30 +977,30 @@ bool WpADOParameter::put_Size(sal_Int32 _nSize)
 OUString WpADOColumn::get_Name() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 OUString WpADOColumn::get_RelatedColumn() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_RelatedColumn(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_RelatedColumn(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOColumn::put_Name(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 void WpADOColumn::put_RelatedColumn(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_RelatedColumn(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_RelatedColumn(bstr);
 }
 
 DataTypeEnum WpADOColumn::get_Type() const
@@ -1090,16 +1091,16 @@ WpADOProperties WpADOColumn::get_Properties() const
 OUString WpADOKey::get_Name() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOKey::put_Name(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 
 KeyTypeEnum WpADOKey::get_Type() const
@@ -1119,16 +1120,16 @@ void WpADOKey::put_Type(const KeyTypeEnum& _eNum)
 OUString WpADOKey::get_RelatedTable() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_RelatedTable(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_RelatedTable(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOKey::put_RelatedTable(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_RelatedTable(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_RelatedTable(bstr);
 }
 
 RuleEnum WpADOKey::get_DeleteRule() const
@@ -1170,16 +1171,16 @@ WpADOColumns WpADOKey::get_Columns() const
 OUString WpADOIndex::get_Name() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOIndex::put_Name(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 
 bool WpADOIndex::get_Clustered() const
@@ -1281,24 +1282,24 @@ ADOProcedures* WpADOCatalog::get_Procedures()
 OUString WpADOTable::get_Name() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOTable::put_Name(std::u16string_view _rName)
 {
     assert(pInterface);
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 
 OUString WpADOTable::get_Type() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Type(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Type(&aBSTR);
+    return OUString(aBSTR);
 }
 
 WpADOColumns WpADOTable::get_Columns() const
@@ -1344,9 +1345,9 @@ WpADOProperties WpADOTable::get_Properties() const
 OUString WpADOView::get_Name() const
 {
     assert(pInterface);
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOView::get_Command(OLEVariant& _rVar) const
@@ -1363,15 +1364,15 @@ void WpADOView::put_Command(OLEVariant const & _rVar)
 
 OUString WpADOGroup::get_Name() const
 {
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOGroup::put_Name(std::u16string_view _rName)
 {
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 
 RightsEnum WpADOGroup::GetPermissions(
@@ -1405,22 +1406,22 @@ WpADOUsers WpADOGroup::get_Users( )
 
 OUString WpADOUser::get_Name() const
 {
-    OLEString aBSTR;
-    pInterface->get_Name(aBSTR.getAddress());
-    return aBSTR.asOUString();
+    sal::systools::BStr aBSTR;
+    pInterface->get_Name(&aBSTR);
+    return OUString(aBSTR);
 }
 
 void WpADOUser::put_Name(std::u16string_view _rName)
 {
-    OLEString bstr(_rName);
-    pInterface->put_Name(bstr.asBSTR());
+    sal::systools::BStr bstr(_rName);
+    pInterface->put_Name(bstr);
 }
 
 bool WpADOUser::ChangePassword(std::u16string_view _rPwd,std::u16string_view 
_rNewPwd)
 {
-    OLEString sStr1(_rPwd);
-    OLEString sStr2(_rNewPwd);
-    bool bErg = 
SUCCEEDED(pInterface->ChangePassword(sStr1.asBSTR(),sStr2.asBSTR()));
+    sal::systools::BStr sStr1(_rPwd);
+    sal::systools::BStr sStr2(_rNewPwd);
+    bool bErg = SUCCEEDED(pInterface->ChangePassword(sStr1, sStr2));
     return bErg;
 }
 
diff --git a/connectivity/source/drivers/ado/adoimp.cxx 
b/connectivity/source/drivers/ado/adoimp.cxx
index bd01f36fc684..0f0d022681ea 100644
--- a/connectivity/source/drivers/ado/adoimp.cxx
+++ b/connectivity/source/drivers/ado/adoimp.cxx
@@ -23,6 +23,7 @@
 #include <ado/Awrapado.hxx>
 #include <ado/adoimp.hxx>
 #include <osl/diagnose.h>
+#include <systools/win32/oleauto.hxx>
 #include <com/sun/star/sdbc/DataType.hpp>
 
 
@@ -67,9 +68,9 @@ const IID ADOS::IID_ADOUSER_25              =   
MYADOID(0x00000619);
 const CLSID ADOS::CLSID_ADOVIEW_25          =   MYADOID(0x00000612);
 const IID ADOS::IID_ADOVIEW_25              =   MYADOID(0x00000613);
 
-OLEString& ADOS::GetKeyStr()
+sal::systools::BStr& ADOS::GetKeyStr()
 {
-    static OLEString sKeyStr(u"gxwaezucfyqpwjgqbcmtsncuhwsnyhiohwxz");
+    static sal::systools::BStr 
sKeyStr(u"gxwaezucfyqpwjgqbcmtsncuhwsnyhiohwxz");
     return sKeyStr;
 }
 
diff --git a/connectivity/source/inc/ado/Aolevariant.hxx 
b/connectivity/source/inc/ado/Aolevariant.hxx
index 256be7dd6c0d..ba0653156a94 100644
--- a/connectivity/source/inc/ado/Aolevariant.hxx
+++ b/connectivity/source/inc/ado/Aolevariant.hxx
@@ -33,27 +33,6 @@ namespace com::sun::star::util
 
 namespace connectivity::ado
 {
-        class OLEString
-        {
-            BSTR m_sStr;
-        public:
-            OLEString();
-            OLEString(const BSTR& _sBStr);
-            OLEString(std::u16string_view _sBStr);
-            OLEString(const OLEString& _rRh)
-            {
-                OLEString::operator=(_rRh);
-            }
-            ~OLEString();
-            OLEString& operator=(std::u16string_view _rSrc);
-            OLEString& operator=(const BSTR& _rSrc);
-            OLEString& operator=(const OLEString& _rSrc);
-            OUString asOUString() const;
-            BSTR asBSTR() const;
-            BSTR* getAddress();
-            sal_Int32 length() const;
-        };
-
         class OLEVariant    :   public ::tagVARIANT
         {
         public:
diff --git a/connectivity/source/inc/ado/adoimp.hxx 
b/connectivity/source/inc/ado/adoimp.hxx
index 58e268d9c68a..60c6fd313d77 100644
--- a/connectivity/source/inc/ado/adoimp.hxx
+++ b/connectivity/source/inc/ado/adoimp.hxx
@@ -24,16 +24,17 @@
 
 struct ADOConnection;
 
+namespace sal::systools { class BStr; };
+
 namespace connectivity::ado
 {
 
         class WpADOField;
-        class OLEString;
         class ADOS
         {
         public:
             // Also here: Free BSTR with SysFreeString()!
-            static OLEString& GetKeyStr();
+            static sal::systools::BStr& GetKeyStr();
 
             static const CLSID  CLSID_ADOCATALOG_25;
             static const IID    IID_ADOCATALOG_25;
diff --git a/dbaccess/source/ui/dlg/adodatalinks.cxx 
b/dbaccess/source/ui/dlg/adodatalinks.cxx
index be60cdb9b089..82af63688cc1 100644
--- a/dbaccess/source/ui/dlg/adodatalinks.cxx
+++ b/dbaccess/source/ui/dlg/adodatalinks.cxx
@@ -27,6 +27,7 @@
 #include <comphelper/scopeguard.hxx>
 #include <o3tl/char16_t2wchar_t.hxx>
 #include <systools/win32/comtools.hxx>
+#include <systools/win32/oleauto.hxx>
 
 #include <initguid.h>
 #include <adoid.h>
@@ -57,12 +58,11 @@ OUString PromptNew(sal_IntPtr hWnd)
         sal::systools::COMReference<ADOConnection> piTmpConnection(piDispatch,
                                                                    
sal::systools::COM_QUERY_THROW);
 
-        BSTR _result = nullptr;
+        sal::systools::BStr _result;
         
sal::systools::ThrowIfFailed(piTmpConnection->get_ConnectionString(&_result),
                                      "get_ConnectionString failed");
 
-        // FIXME: Don't we need SysFreeString(_result)?
-        return OUString(o3tl::toU(_result), SysStringLen(_result));
+        return OUString(_result);
     }
     catch (const sal::systools::ComError&)
     {
@@ -80,9 +80,8 @@ OUString PromptEdit(sal_IntPtr hWnd, OUString const & connstr)
         sal::systools::COMReference<ADOConnection> piTmpConnection;
         piTmpConnection.CoCreateInstance(CLSID_CADOConnection, nullptr, 
CLSCTX_INPROC_SERVER);
 
-        // FIXME: BSTR is not just cast from a random string
         sal::systools::ThrowIfFailed(
-            
piTmpConnection->put_ConnectionString(const_cast<BSTR>(o3tl::toW(connstr.getStr()))),
+            
piTmpConnection->put_ConnectionString(sal::systools::BStr(connstr)),
             "put_ConnectionString failed");
 
         // Instantiate DataLinks object.
@@ -111,12 +110,11 @@ OUString PromptEdit(sal_IntPtr hWnd, OUString const & 
connstr)
             piTmpConnection.set(piDispatch, sal::systools::COM_QUERY_THROW);
         }
 
-        BSTR _result = nullptr;
+        sal::systools::BStr _result;
         
sal::systools::ThrowIfFailed(piTmpConnection->get_ConnectionString(&_result),
                                      "get_ConnectionString failed");
 
-        // FIXME: Don't we need SysFreeString(_result)?
-        return OUString(o3tl::toU(_result), SysStringLen(_result));
+        return OUString(_result);
     }
     catch (const sal::systools::ComError&)
     {
diff --git a/extensions/source/activex/SOActiveX.cxx 
b/extensions/source/activex/SOActiveX.cxx
index 2d8dc2a73b91..88b3ad7691ca 100644
--- a/extensions/source/activex/SOActiveX.cxx
+++ b/extensions/source/activex/SOActiveX.cxx
@@ -359,7 +359,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::Load( 
LPPROPERTYBAG pPropBag, LPER
         // all information from the 'object' tag is in strings
         if (aVal[ind].vt == VT_BSTR && !wcscmp(aPropNames[ind].pstrName, 
L"src"))
         {
-            mCurFileUrl = wcsdup( aVal[ind].bstrVal );
+            mCurFileUrl.AssignBSTR(aVal[ind].bstrVal);
         }
         else if( aVal[ind].vt == VT_BSTR
                 && !wcscmp(aPropNames[ind].pstrName, L"readonly"))
@@ -384,16 +384,10 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::Load( 
LPPROPERTYBAG pPropBag, LPER
         return hr;
 
     mbReadyForActivation = FALSE;
-    if (BSTR bStrUrl = SysAllocString(mCurFileUrl))
-    {
-        hr = CBindStatusCallback<CSOActiveX>::Download(
-            this, &CSOActiveX::CallbackCreateXInputStream, bStrUrl, 
m_spClientSite, FALSE);
-        SysFreeString(bStrUrl);
-        if (hr == MK_S_ASYNCHRONOUS)
-            hr = S_OK;
-    }
-    else
-        hr = E_OUTOFMEMORY;
+    hr = CBindStatusCallback<CSOActiveX>::Download(
+        this, &CSOActiveX::CallbackCreateXInputStream, mCurFileUrl, 
m_spClientSite, FALSE);
+    if (hr == MK_S_ASYNCHRONOUS)
+        hr = S_OK;
 
     if ( !SUCCEEDED( hr ) )
     {
diff --git a/extensions/source/activex/SOActiveX.h 
b/extensions/source/activex/SOActiveX.h
index f3a66e183ed5..37d983ae91d5 100644
--- a/extensions/source/activex/SOActiveX.h
+++ b/extensions/source/activex/SOActiveX.h
@@ -81,7 +81,7 @@ protected:
     CComPtr<IDispatch>      mpDispFrame;
     CComPtr<IDispatch>      mpInstanceLocker;
     CComPtr<IDispatch>      mpDispWin;
-    OLECHAR const *         mCurFileUrl;
+    CComBSTR                mCurFileUrl;
     BOOL                    mbLoad;
     BOOL                    mbViewOnly;
     WNDCLASSW               mPWinClass;
diff --git a/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx 
b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx
index 9b721d50b36a..787769b50367 100644
--- a/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx
+++ b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx
@@ -25,6 +25,7 @@
 #include <Security.h>
 
 #include <systools/win32/comtools.hxx>
+#include <systools/win32/oleauto.hxx>
 
 namespace extensions
 {
@@ -93,12 +94,12 @@ public:
             sal::systools::COMReference<IADsADSystemInfo> 
pADsys(CLSID_ADSystemInfo, nullptr,
                                                                  
CLSCTX_INPROC_SERVER);
 
-            smartBSTR sUserDN;
-            sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN.ptr), 
"get_UserName failed");
+            sal::systools::BStr sUserDN;
+            sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN), 
"get_UserName failed");
             // If this user is an AD user, then without an active connection 
to the domain, all the
             // above will succeed, and m_sUserDN will be correctly 
initialized, but the following
             // call to ADsGetObject will fail, and we will attempt reading 
cached values.
-            m_sUserDN = o3tl::toU(sUserDN.ptr);
+            m_sUserDN = sUserDN;
             OUString sLdapUserDN = "LDAP://" + m_sUserDN;
             sal::systools::COMReference<IADsUser> pUser;
             
sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), 
IID_IADsUser,
@@ -146,29 +147,23 @@ public:
     virtual OUString GetMail() override { return m_aMap[mail]; }
 
 private:
-    struct smartBSTR
-    {
-        BSTR ptr = nullptr;
-        ~smartBSTR() { SysFreeString(ptr); }
-    };
-
     typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*);
     static OUString Str(IADsUser* pUser, getstrfunc func)
     {
-        smartBSTR sBstr;
-        if (FAILED((pUser->*func)(&sBstr.ptr)))
+        sal::systools::BStr sBstr;
+        if (FAILED((pUser->*func)(&sBstr)))
             return "";
-        return OUString(o3tl::toU(sBstr.ptr));
+        return OUString(sBstr);
     }
     static OUString Str(IADsUser* pUser, const wchar_t* property)
     {
-        smartBSTR sBstrProp{ SysAllocString(property) };
+        sal::systools::BStr sBstrProp{ o3tl::toU(property) };
         struct AutoVariant : public VARIANT
         {
             AutoVariant() { VariantInit(this); }
             ~AutoVariant() { VariantClear(this); }
         } varArr;
-        if (FAILED(pUser->GetEx(sBstrProp.ptr, &varArr)))
+        if (FAILED(pUser->GetEx(sBstrProp, &varArr)))
             return "";
         SAFEARRAY* sa = V_ARRAY(&varArr);
         LONG nStart, nEnd;
diff --git a/include/systools/win32/oleauto.hxx 
b/include/systools/win32/oleauto.hxx
new file mode 100644
index 000000000000..c01ac1225f73
--- /dev/null
+++ b/include/systools/win32/oleauto.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cassert>
+#include <string_view>
+#include <utility>
+
+#include <prewin.h>
+#include <oleauto.h>
+#include <postwin.h>
+
+#include <o3tl/char16_t2wchar_t.hxx>
+
+namespace sal::systools
+{
+// BSTR safe wrapper
+class BStr
+{
+public:
+    BStr() = default;
+    BStr(std::u16string_view sv)
+        : m_Str(::SysAllocStringLen(o3tl::toW(sv.data()), sv.length()))
+    {
+    }
+    BStr(const BStr& src)
+        : BStr(std::u16string_view(src))
+    {
+    }
+    BStr(BStr&& src)
+        : m_Str(std::exchange(src.m_Str, nullptr))
+    {
+    }
+    ~BStr() { ::SysFreeString(m_Str); }
+    BStr& operator=(std::u16string_view sv)
+    {
+        ::SysFreeString(
+            std::exchange(m_Str, ::SysAllocStringLen(o3tl::toW(sv.data()), 
sv.length())));
+        return *this;
+    }
+    BStr& operator=(const BStr& src)
+    {
+        if (&src != this)
+            operator=(std::u16string_view(src));
+        return *this;
+    }
+    BStr& operator=(BStr&& src)
+    {
+        ::SysFreeString(std::exchange(m_Str, std::exchange(src.m_Str, 
nullptr)));
+        return *this;
+    }
+    operator std::u16string_view() const { return { o3tl::toU(m_Str), length() 
}; }
+    operator BSTR() const { return m_Str; }
+    BSTR* operator&()
+    {
+        assert(!m_Str);
+        return &m_Str;
+    }
+    UINT length() const { return ::SysStringLen(m_Str); }
+
+private:
+    BSTR m_Str = nullptr;
+};
+
+} // sal::systools
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to