Quelle ddesvr.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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 .
*/
#include "ddeimp.hxx"
#include <algorithm>
#include <memory>
#include <comphelper/string.hxx>
#include <rtl/ustring.hxx>
#include <svl/svdde.hxx>
#include <osl/thread.h>
#include <o3tl/sorted_vector.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
#include <officecfg/Office/Common.hxx>
namespace {
enum DdeItemType
{
DDEITEM,
DDEGETPUTITEM
};
}
struct DdeItemImpData
{
HCONV nHCnv;
sal_uInt16 nCnt;
explicit DdeItemImpData( HCONV nH ) : nHCnv( nH ), nCnt( 1 ) {}
};
HDDEDATA CALLBACK DdeInternal::SvrCallback(
UINT nCode, UINT nCbType, HCONV hConv, HSZ hText1, HSZ hText2,
HDDEDATA hData, ULONG_PTR, ULONG_PTR )
{
const DdeInstData& rInst = ImpGetInstData();
switch ( nCode )
{
case XTYP_WILDCONNECT:
{
std::vector<HSZPAIR> aPairs;
WCHAR chTopicBuf[256];
if ( hText1 )
DdeQueryStringW( rInst.hDdeInstSvr, hText1, chTopicBuf,
SAL_N_ELEMENTS(chTopicBuf), CP_WINUNICODE );
for (
auto & pService : DdeService::GetServices())
{
if (hText2 && !(*pService->pName == hText2))
continue ;
OUString sTopics(pService->Topics().replaceAll(
"\n" ,
"" ).replaceAll(
"\r" ,
"" ));
if (sTopics.isEmpty())
continue ;
for (sal_Int32 n = 0; -1 != n;)
{
OUString s(sTopics.getToken(0,
'\t' , n));
if (hText1 && s != o3tl::toU(chTopicBuf))
continue ;
DdeString aDStr(rInst.hDdeInstSvr, s);
if (
auto pTopic = FindTopic(*pService, aDStr.getHSZ()))
{
auto & pair = aPairs.emplace_back();
pair.hszSvc = pService->pName->getHSZ();
pair.hszTopic = pTopic->pName->getHSZ();
}
}
}
if (aPairs.empty())
return nullptr;
aPairs.emplace_back();
// trailing zero
HDDEDATA h = DdeCreateDataHandle(
rInst.hDdeInstSvr,
reinterpret_cast <LPBYTE>(aPairs.data()),
sizeof (HSZPAIR) * aPairs.size(),
0, nullptr, nCbType, 0);
return h;
}
case XTYP_CONNECT:
if (
auto pService = FindService(hText2))
if (FindTopic(*pService, hText1))
return reinterpret_cast <HDDEDATA>(DDE_FACK);
return nullptr;
case XTYP_CONNECT_CONFIRM:
if (
auto pService = FindService(hText2))
{
if (
auto pTopic = FindTopic(*pService, hText1))
{
auto pC =
new Conversation;
pC->hConv = hConv;
pC->pTopic = pTopic;
pService->m_vConv.emplace_back( pC );
}
}
return nullptr;
}
DdeService* pService = nullptr;
Conversation* pC = nullptr;
for (
auto & rpService : DdeService::GetServices())
{
for ( size_t i = 0, n = rpService->m_vConv.size(); i < n; ++i )
{
pC = rpService->m_vConv[ i ].get();
if ( pC->hConv == hConv )
pService = rpService;
}
}
if (!pService)
return reinterpret_cast <HDDEDATA>(DDE_FNOTPROCESSED);
assert(pC);
if ( nCode == XTYP_DISCONNECT)
{
DisconnectTopic(*pC->pTopic, hConv);
auto it = std::find_if(pService->m_vConv.begin(), pService->m_vConv.end(),
[&pC](
const std::unique_ptr<Conversation>& rxConv) {
return rxCon
v.get() == pC; });
if (it != pService->m_vConv.end())
pService->m_vConv.erase( it );
return nullptr;
}
bool bExec = nCode == XTYP_EXECUTE;
DdeTopic* pTopic = pC->pTopic;
DdeItem* pItem;
if (pTopic && !bExec && pService->HasCbFormat(nCbType))
pItem = FindItem( *pTopic, hText2 );
else
pItem = nullptr;
if ( !pItem && !bExec )
return static_cast <HDDEDATA>(DDE_FNOTPROCESSED);
if ( pItem )
pTopic->aItem = pItem->GetName();
else
pTopic->aItem.clear();
bool bRes = false ;
switch ( nCode )
{
case XTYP_REQUEST:
case XTYP_ADVREQ:
{
OUString aRes; // Must be free not until the end!
DdeData* pData;
if ( pTopic->IsSystemTopic() )
{
if ( pTopic->aItem == SZDDESYS_ITEM_TOPICS )
aRes = pService->Topics();
else if ( pTopic->aItem == SZDDESYS_ITEM_SYSITEMS )
aRes = pService->SysItems();
else if ( pTopic->aItem == SZDDESYS_ITEM_STATUS )
aRes = pService->Status();
else if ( pTopic->aItem == SZDDESYS_ITEM_FORMATS )
aRes = pService->Formats();
else if ( pTopic->aItem == SZDDESYS_ITEM_HELP )
aRes = OUString();
else
aRes = OUString();
if ( !aRes.isEmpty() )
pData = new DdeData( aRes );
else
pData = nullptr;
}
else if ( DDEGETPUTITEM == pItem->nType )
{
pData = static_cast <DdeGetPutItem*>(pItem)->Get( DdeData::GetInternalFormat( nCbType ) );
}
else
{
pData = pTopic->Get( DdeData::GetInternalFormat( nCbType ));
}
if ( pData )
{
return DdeCreateDataHandle( rInst.hDdeInstSvr,
static_cast <LPBYTE>(const_cast <void *>(pData->xImp->pData)),
pData->xImp->nData,
0, hText2,
DdeData::GetExternalFormat(
pData->xImp->nFmt ),
0 );
}
}
break ;
case XTYP_POKE:
if ( !pTopic->IsSystemTopic() )
{
DdeData d;
d.xImp->hData = hData;
d.xImp->nFmt = DdeData::GetInternalFormat( nCbType );
d.Lock();
if ( DDEGETPUTITEM == pItem->nType )
bRes = static_cast <DdeGetPutItem*>(pItem)->Put( &d );
else
bRes = pTopic->Put( &d );
}
if ( bRes )
return reinterpret_cast <HDDEDATA>(DDE_FACK);
else
return reinterpret_cast <HDDEDATA>(DDE_FNOTPROCESSED);
case XTYP_ADVSTART:
{
// Is the Item turning into a HotLink for the first time?
if ( !pItem->pImpData && pTopic->StartAdviseLoop() )
{
// Then the Item has been exchanged
std::vector<DdeItem*>::iterator it(std::find(pTopic->aItems.begin(),
pTopic->aItems.end(),
pItem));
if (it != pTopic->aItems.end())
pTopic->aItems.erase(it);
std::vector<DdeItem*>::iterator iter;
iter = std::find_if(pTopic->aItems.begin(), pTopic->aItems.end(),
[&hText2](const DdeItem* pDdeItem) { return *pDdeItem->pName == hText2; });
if (iter != pTopic->aItems.end())
{
// It was exchanged indeed
delete pItem;
pItem = nullptr;
}
if ( pItem )
// It was not exchange, so back in
pTopic->aItems.push_back(pItem);
else
pItem = iter != pTopic->aItems.end() ? *iter : nullptr;
}
if (pItem)
{
IncMonitor(pItem, hConv);
}
}
return reinterpret_cast <HDDEDATA>(TRUE );
case XTYP_ADVSTOP:
DecMonitor(pItem, hConv);
return reinterpret_cast <HDDEDATA>(TRUE );
case XTYP_EXECUTE:
{
DdeData aExec;
aExec.xImp->hData = hData;
aExec.xImp->nFmt = DdeData::GetInternalFormat( nCbType );
aExec.Lock();
OUString aName;
aName = static_cast <const sal_Unicode *>(aExec.xImp->pData);
if ( pTopic->IsSystemTopic() )
bRes = false ;
else
bRes = pTopic->Execute( &aName );
}
if ( bRes )
return reinterpret_cast <HDDEDATA>(DDE_FACK);
else
return reinterpret_cast <HDDEDATA>(DDE_FNOTPROCESSED);
}
return nullptr;
}
DdeService* DdeInternal::FindService( HSZ hService )
{
DdeServices& rSvc = DdeService::GetServices();
auto aI = std::find_if(rSvc.begin(), rSvc.end(),
[&hService](const DdeService* s) { return *s->pName == hService; });
if (aI != rSvc.end())
return *aI;
return nullptr;
}
DdeTopic* DdeInternal::FindTopic( DdeService& rService, HSZ hTopic )
{
std::vector<DdeTopic*> &rTopics = rService.aTopics;
auto cfName = [&hTopic](const DdeTopic* pTopic) { return *pTopic->pName == hTopic; };
auto iter = std::find_if(rTopics.begin(), rTopics.end(), cfName);
if (iter == rTopics.end())
{
// Let's query our subclass
const DdeInstData& rInst = ImpGetInstData();
if (DWORD sz = DdeQueryStringW(rInst.hDdeInstSvr, hTopic, nullptr, 0, CP_WINUNICODE))
{
auto chBuf = std::make_unique<WCHAR[]>(sz + 1);
sz = DdeQueryStringW(rInst.hDdeInstSvr, hTopic, chBuf.get(), sz + 1, CP_WINUNICODE);
if (sz && rService.MakeTopic(OUString(o3tl::toU(chBuf.get()), sz)))
{
iter = std::find_if(rTopics.begin(), rTopics.end(), cfName);
}
}
}
if (iter != rTopics.end())
return *iter;
return nullptr;
}
DdeItem* DdeInternal::FindItem( DdeTopic& rTopic, HSZ hItem )
{
std::vector<DdeItem*>::iterator iter;
std::vector<DdeItem*> &rItems = rTopic.aItems;
const DdeInstData& rInst = ImpGetInstData();
bool bContinue = false ;
do
{ // middle check loop
iter = std::find_if(rItems.begin(), rItems.end(),
[&hItem](const DdeItem* pItem) { return *pItem->pName == hItem; });
if (iter != rItems.end())
return *iter;
bContinue = !bContinue;
if ( !bContinue )
break ;
// Let's query our subclass
WCHAR chBuf[250];
DdeQueryStringW(rInst.hDdeInstSvr,hItem,chBuf,SAL_N_ELEMENTS(chBuf),CP_WINUNICODE );
bContinue = rTopic.MakeItem( OUString(o3tl::toU(chBuf)) );
// We need to search again
}
while ( bContinue );
return nullptr;
}
DdeService::DdeService( const OUString& rService )
{
DdeInstData& rInst = ImpGetInstData();
rInst.nRefCount++;
rInst.nInstanceSvr++;
if ( !rInst.hDdeInstSvr )
{
nStatus = DMLERR_SYS_ERROR;
if ( !officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() )
{
nStatus = sal::static_int_cast< short >(
DdeInitializeW( &rInst.hDdeInstSvr,
DdeInternal::SvrCallback,
APPCLASS_STANDARD |
CBF_SKIP_REGISTRATIONS |
CBF_SKIP_UNREGISTRATIONS, 0 ) );
}
rInst.pServicesSvr = new DdeServices;
}
else
nStatus = DMLERR_NO_ERROR;
if ( rInst.pServicesSvr )
rInst.pServicesSvr->push_back( this );
pName = new DdeString( rInst.hDdeInstSvr, rService );
if ( nStatus == DMLERR_NO_ERROR )
{
if ( !DdeNameService( rInst.hDdeInstSvr, pName->getHSZ(), nullptr,
DNS_REGISTER | DNS_FILTEROFF ) )
{
nStatus = DMLERR_SYS_ERROR;
}
}
AddFormat( SotClipboardFormatId::STRING );
pSysTopic = new DdeTopic( SZDDESYS_TOPIC );
pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS ) );
pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS ) );
pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_STATUS ) );
pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS ) );
pSysTopic->AddItem( DdeItem( SZDDESYS_ITEM_HELP ) );
AddTopic( *pSysTopic );
}
DdeService::~DdeService()
{
DdeInstData& rInst = ImpGetInstData();
if ( rInst.pServicesSvr )
std::erase(*rInst.pServicesSvr, this );
delete pSysTopic;
delete pName;
rInst.nInstanceSvr--;
rInst.nRefCount--;
if ( !rInst.nInstanceSvr && rInst.hDdeInstSvr )
{
if ( DdeUninitialize( rInst.hDdeInstSvr ) )
{
rInst.hDdeInstSvr = 0;
delete rInst.pServicesSvr;
rInst.pServicesSvr = nullptr;
}
}
}
OUString DdeService::GetName() const
{
return pName->toOUString();
}
DdeServices& DdeService::GetServices()
{
return *(ImpGetInstData().pServicesSvr);
}
void DdeService::AddTopic( const DdeTopic& rTopic )
{
RemoveTopic( rTopic );
aTopics.push_back(const_cast <DdeTopic *>(&rTopic));
}
void DdeService::RemoveTopic( const DdeTopic& rTopic )
{
auto iter = std::find_if(aTopics.begin(), aTopics.end(),
[&rTopic](const DdeTopic* pTopic) { return DdeCmpStringHandles(pTopic->pName->getHSZ(), rTopic.pName->getHSZ()) == 0; });
if (iter != aTopics.end())
{
aTopics.erase(iter);
// Delete all conversions!
// Or else we work on deleted topics!
for ( size_t n = m_vConv.size(); n; )
{
auto const & pC = m_vConv[ --n ];
if ( pC->pTopic == &rTopic )
m_vConv.erase( m_vConv.begin() + n );
}
}
}
bool DdeService::HasCbFormat( sal_uInt32 nFmt )
{
return std::find(aFormats.begin(), aFormats.end(), nFmt) != aFormats.end();
}
bool DdeService::HasFormat(SotClipboardFormatId nFmt)
{
return HasCbFormat( DdeData::GetExternalFormat( nFmt ));
}
void DdeService::AddFormat(SotClipboardFormatId nFmt)
{
sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt );
if (HasCbFormat(nExternalFmt))
return ;
aFormats.push_back( nExternalFmt );
}
void DdeService::RemoveFormat(SotClipboardFormatId nFmt)
{
sal_uInt32 nExternalFmt = DdeData::GetExternalFormat( nFmt );
auto it = std::find(aFormats.begin(), aFormats.end(), nExternalFmt);
if (it != aFormats.end())
aFormats.erase( it );
}
DdeTopic::DdeTopic( const OUString& rName )
{
pName = new DdeString( ImpGetInstData().hDdeInstSvr, rName );
}
DdeTopic::~DdeTopic()
{
for (auto & rpItem : aItems)
{
rpItem->pMyTopic = nullptr;
delete rpItem;
}
delete pName;
}
OUString DdeTopic::GetName() const
{
return pName->toOUString();
}
bool DdeTopic::IsSystemTopic()
{
return GetName() == SZDDESYS_TOPIC;
}
DdeItem* DdeTopic::AddItem( const DdeItem& r )
{
DdeItem* s;
if ( DDEGETPUTITEM == r.nType )
s = new DdeGetPutItem( r );
else
s = new DdeItem( r );
aItems.push_back( s );
s->pMyTopic = this ;
return s;
}
void DdeTopic::InsertItem( DdeItem* pNew )
{
if ( pNew )
{
aItems.push_back( pNew );
pNew->pMyTopic = this ;
}
}
void DdeTopic::RemoveItem( const DdeItem& r )
{
auto iter = std::find_if(aItems.begin(), aItems.end(),
[&r](const DdeItem* pItem) { return DdeCmpStringHandles(pItem->pName->getHSZ(), r.pName->getHSZ()) == 0; });
if ( iter != aItems.end() )
{
(*iter)->pMyTopic = nullptr;
delete *iter;
aItems.erase(iter);
}
}
void DdeTopic::NotifyClient( const OUString& rItem )
{
auto iter = std::find_if(aItems.begin(), aItems.end(),
[&rItem](const DdeItem* pItem) { return pItem->GetName().equals(rItem) && pItem->pImpData; });
if (iter != aItems.end())
DdePostAdvise( ImpGetInstData().hDdeInstSvr, pName->getHSZ(), (*iter)->pName->getHSZ() );
}
void DdeInternal::DisconnectTopic(DdeTopic & rTopic, HCONV nId)
{
for (const auto & rpItem : rTopic.aItems)
{
DecMonitor(rpItem, nId);
}
}
DdeData* DdeTopic::Get(SotClipboardFormatId /*nFmt*/)
{
return nullptr;
}
bool DdeTopic::Put( const DdeData* )
{
return false ;
}
bool DdeTopic::Execute( const OUString* )
{
return false ;
}
bool DdeTopic::StartAdviseLoop()
{
return false ;
}
DdeItem::DdeItem( const sal_Unicode* p )
: DdeItem(OUString(p))
{
}
DdeItem::DdeItem( const OUString& r)
{
pName = new DdeString( ImpGetInstData().hDdeInstSvr, r );
nType = DDEITEM;
pMyTopic = nullptr;
pImpData = nullptr;
}
DdeItem::DdeItem( const DdeItem& r)
: DdeItem(r.pName->toOUString())
{
}
DdeItem::~DdeItem()
{
if ( pMyTopic )
std::erase(pMyTopic->aItems, this );
delete pName;
delete pImpData;
}
OUString DdeItem::GetName() const
{
return pName->toOUString();
}
void DdeItem::NotifyClient()
{
if ( pMyTopic && pImpData )
{
DdePostAdvise( ImpGetInstData().hDdeInstSvr, pMyTopic->pName->getHSZ(), pName->getHSZ() );
}
}
void DdeInternal::IncMonitor(DdeItem *const pItem, HCONV nHCnv)
{
if (!pItem->pImpData)
{
pItem->pImpData = new std::vector<DdeItemImpData>;
if (DDEGETPUTITEM == pItem->nType)
{
static_cast <DdeGetPutItem*>(pItem)->AdviseLoop( true );
}
}
else
{
for (size_t n = pItem->pImpData->size(); n; )
{
if ((*pItem->pImpData)[ --n ].nHCnv == nHCnv)
{
++(*pItem->pImpData)[ n ].nHCnv;
return ;
}
}
}
pItem->pImpData->push_back( DdeItemImpData( nHCnv ) );
}
void DdeInternal::DecMonitor(DdeItem *const pItem, HCONV nHCnv)
{
if (pItem->pImpData)
{
for ( size_t n = 0; n < pItem->pImpData->size(); ++n )
{
DdeItemImpData* pData = &(*pItem->pImpData)[n];
if ( pData->nHCnv == nHCnv )
{
if ( !pData->nCnt || !--pData->nCnt )
{
if (1 < pItem->pImpData->size())
{
pItem->pImpData->erase(pItem->pImpData->begin() + n);
}
else
{
delete pItem->pImpData;
pItem->pImpData = nullptr;
if (DDEGETPUTITEM == pItem->nType)
{
static_cast <DdeGetPutItem*>(pItem)->AdviseLoop(false );
}
}
}
return ;
}
}
}
}
short DdeItem::GetLinks()
{
short nCnt = 0;
if ( pImpData )
{
for (const auto & rData : *pImpData)
{
nCnt += rData.nCnt;
}
}
return nCnt;
}
DdeGetPutItem::DdeGetPutItem( const sal_Unicode* p )
: DdeItem( p )
{
nType = DDEGETPUTITEM;
}
DdeGetPutItem::DdeGetPutItem( const OUString& rStr )
: DdeItem( rStr )
{
nType = DDEGETPUTITEM;
}
DdeGetPutItem::DdeGetPutItem( const DdeItem& rItem )
: DdeItem( rItem )
{
nType = DDEGETPUTITEM;
}
DdeData* DdeGetPutItem::Get(SotClipboardFormatId)
{
return nullptr;
}
bool DdeGetPutItem::Put( const DdeData* )
{
return false ;
}
void DdeGetPutItem::AdviseLoop( bool )
{
}
OUString DdeService::SysItems()
{
OUString s;
for ( const auto & rpTopic : aTopics )
{
if ( rpTopic->GetName() == SZDDESYS_TOPIC )
{
short n = 0;
for ( const auto & rpItem : rpTopic->aItems )
{
if ( n )
s += "\t" ;
s += rpItem->GetName();
n++;
}
s += "\r\n" ;
}
}
return s;
}
OUString DdeService::Topics()
{
OUString s;
short n = 0;
for ( const auto & rpTopic : aTopics )
{
if ( n )
s += "\t" ;
s += rpTopic->GetName();
n++;
}
s += "\r\n" ;
return s;
}
OUString DdeService::Formats()
{
OUString s;
short n = 0;
for (size_t i = 0; i < aFormats.size(); ++i, ++n)
{
sal_uInt32 f = aFormats[ i ];
if ( n )
s += "\t" ;
switch ( f )
{
case CF_TEXT:
s += "TEXT" ;
break ;
case CF_BITMAP:
s += "BITMAP" ;
break ;
default :
{
WCHAR buf[128];
GetClipboardFormatNameW( f, buf, SAL_N_ELEMENTS(buf) );
s += o3tl::toU(buf);
}
break ;
}
}
s += "\r\n" ;
return s;
}
OUString DdeService::Status()
{
return "Ready\r\n" ;
}
bool DdeTopic::MakeItem( const OUString& )
{
return false ;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland
2026-04-04