391 lines
11 KiB
C++
391 lines
11 KiB
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 <string.h>
|
|
#include <algorithm>
|
|
#include "ddeimp.hxx"
|
|
#include <svl/svdde.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <osl/thread.h>
|
|
#include <comphelper/solarmutex.hxx>
|
|
|
|
namespace {
|
|
|
|
DdeInstData * theDdeInstData;
|
|
|
|
}
|
|
|
|
DdeInstData* ImpGetInstData()
|
|
{
|
|
return theDdeInstData;
|
|
}
|
|
|
|
DdeInstData* ImpInitInstData()
|
|
{
|
|
theDdeInstData = new DdeInstData;
|
|
return theDdeInstData;
|
|
}
|
|
|
|
void ImpDeinitInstData()
|
|
{
|
|
delete theDdeInstData;
|
|
theDdeInstData = nullptr;
|
|
}
|
|
|
|
|
|
struct DdeImp
|
|
{
|
|
HCONV hConv;
|
|
UINT nStatus;
|
|
};
|
|
|
|
HDDEDATA CALLBACK DdeInternal::CliCallback( UINT nCode, UINT nCbType,
|
|
HCONV hConv, HSZ, HSZ hText2,
|
|
HDDEDATA hData, ULONG_PTR nInfo1, ULONG_PTR )
|
|
{
|
|
HDDEDATA nRet = DDE_FNOTPROCESSED;
|
|
const std::vector<DdeConnection*> &rAll = DdeConnection::GetConnections();
|
|
DdeConnection* self = nullptr;
|
|
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
assert(pInst);
|
|
|
|
for ( size_t i = 0; i < rAll.size(); ++i)
|
|
{
|
|
self = rAll[i];
|
|
|
|
if ( self->pImp->hConv == hConv )
|
|
break;
|
|
}
|
|
|
|
if( self )
|
|
{
|
|
bool bFound = false;
|
|
std::vector<DdeTransaction*>::iterator iter;
|
|
for( iter = self->aTransactions.begin(); iter != self->aTransactions.end(); ++iter )
|
|
{
|
|
switch( nCode )
|
|
{
|
|
case XTYP_XACT_COMPLETE:
|
|
if( static_cast<DWORD>((*iter)->nId) == nInfo1 )
|
|
{
|
|
nCode = (*iter)->nType & (XCLASS_MASK | XTYP_MASK);
|
|
(*iter)->bBusy = false;
|
|
(*iter)->Done( nullptr != hData );
|
|
bFound = true;
|
|
}
|
|
break;
|
|
|
|
case XTYP_DISCONNECT:
|
|
self->pImp->hConv = DdeReconnect( hConv );
|
|
self->pImp->nStatus = self->pImp->hConv
|
|
? DMLERR_NO_ERROR
|
|
: DdeGetLastError( pInst->hDdeInstCli );
|
|
iter = self->aTransactions.end();
|
|
nRet = nullptr;
|
|
bFound = true;
|
|
break;
|
|
|
|
case XTYP_ADVDATA:
|
|
bFound = *(*iter)->pName == hText2;
|
|
break;
|
|
}
|
|
if( bFound )
|
|
break;
|
|
}
|
|
|
|
if( iter != self->aTransactions.end() )
|
|
{
|
|
switch( nCode )
|
|
{
|
|
case XTYP_ADVDATA:
|
|
if( !hData )
|
|
{
|
|
static_cast<DdeLink*>(*iter)->Notify();
|
|
nRet = reinterpret_cast<HDDEDATA>(DDE_FACK);
|
|
break;
|
|
}
|
|
[[fallthrough]];
|
|
|
|
case XTYP_REQUEST:
|
|
DdeData d;
|
|
d.xImp->hData = hData;
|
|
d.xImp->nFmt = DdeData::GetInternalFormat( nCbType );
|
|
d.Lock();
|
|
(*iter)->Data( &d );
|
|
nRet = reinterpret_cast<HDDEDATA>(DDE_FACK);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
DdeConnection::DdeConnection( const OUString& rService, const OUString& rTopic ):
|
|
pImp(std::make_unique<DdeImp>())
|
|
{
|
|
pImp->nStatus = DMLERR_NO_ERROR;
|
|
pImp->hConv = nullptr;
|
|
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
if( !pInst )
|
|
pInst = ImpInitInstData();
|
|
pInst->nRefCount++;
|
|
pInst->nInstanceCli++;
|
|
if ( !pInst->hDdeInstCli )
|
|
{
|
|
pImp->nStatus = DMLERR_SYS_ERROR;
|
|
if ( !officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() )
|
|
{
|
|
pImp->nStatus = DdeInitializeW( &pInst->hDdeInstCli,
|
|
DdeInternal::CliCallback,
|
|
APPCLASS_STANDARD | APPCMD_CLIENTONLY |
|
|
CBF_FAIL_ALLSVRXACTIONS |
|
|
CBF_SKIP_REGISTRATIONS |
|
|
CBF_SKIP_UNREGISTRATIONS, 0L );
|
|
}
|
|
}
|
|
|
|
pService = new DdeString( pInst->hDdeInstCli, rService );
|
|
pTopic = new DdeString( pInst->hDdeInstCli, rTopic );
|
|
|
|
if ( pImp->nStatus == DMLERR_NO_ERROR )
|
|
{
|
|
pImp->hConv = DdeConnect( pInst->hDdeInstCli,pService->getHSZ(),pTopic->getHSZ(), nullptr);
|
|
if( !pImp->hConv )
|
|
pImp->nStatus = DdeGetLastError( pInst->hDdeInstCli );
|
|
}
|
|
|
|
pInst->aConnections.push_back( this );
|
|
}
|
|
|
|
DdeConnection::~DdeConnection()
|
|
{
|
|
if ( pImp->hConv )
|
|
DdeDisconnect( pImp->hConv );
|
|
|
|
delete pService;
|
|
delete pTopic;
|
|
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
assert(pInst);
|
|
|
|
std::vector<DdeConnection*>::iterator it(std::find(pInst->aConnections.begin(),
|
|
pInst->aConnections.end(),
|
|
this));
|
|
if (it != pInst->aConnections.end())
|
|
pInst->aConnections.erase(it);
|
|
|
|
pInst->nInstanceCli--;
|
|
pInst->nRefCount--;
|
|
if ( !pInst->nInstanceCli && pInst->hDdeInstCli )
|
|
{
|
|
if( DdeUninitialize( pInst->hDdeInstCli ) )
|
|
{
|
|
pInst->hDdeInstCli = 0;
|
|
if( pInst->nRefCount == 0 )
|
|
ImpDeinitInstData();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DdeConnection::IsConnected()
|
|
{
|
|
CONVINFO c;
|
|
c.cb = sizeof( c );
|
|
if ( DdeQueryConvInfo( pImp->hConv, QID_SYNC, &c ) )
|
|
return true;
|
|
else
|
|
{
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
pImp->hConv = DdeReconnect( pImp->hConv );
|
|
pImp->nStatus = pImp->hConv ? DMLERR_NO_ERROR : DdeGetLastError( pInst->hDdeInstCli );
|
|
return pImp->nStatus == DMLERR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
OUString DdeConnection::GetServiceName() const
|
|
{
|
|
return pService->toOUString();
|
|
}
|
|
|
|
OUString DdeConnection::GetTopicName() const
|
|
{
|
|
return pTopic->toOUString();
|
|
}
|
|
|
|
const std::vector<DdeConnection*>& DdeConnection::GetConnections()
|
|
{
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
assert(pInst);
|
|
return pInst->aConnections;
|
|
}
|
|
|
|
DdeTransaction::DdeTransaction( DdeConnection& d, const OUString& rItemName,
|
|
tools::Long n )
|
|
: rDde( d )
|
|
{
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
pName = new DdeString( pInst->hDdeInstCli, rItemName );
|
|
nTime = n;
|
|
nId = 0;
|
|
nType = 0;
|
|
bBusy = false;
|
|
|
|
rDde.aTransactions.push_back( this );
|
|
}
|
|
|
|
DdeTransaction::~DdeTransaction()
|
|
{
|
|
if ( nId && rDde.pImp->hConv )
|
|
{
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
DdeAbandonTransaction( pInst->hDdeInstCli, rDde.pImp->hConv, nId );
|
|
}
|
|
|
|
delete pName;
|
|
std::erase(rDde.aTransactions,this);
|
|
}
|
|
|
|
void DdeTransaction::Execute()
|
|
{
|
|
HSZ hItem = pName->getHSZ();
|
|
void const * pData = aDdeData.getData();
|
|
DWORD nData = static_cast<DWORD>(aDdeData.getSize());
|
|
SotClipboardFormatId nIntFmt = aDdeData.xImp->nFmt;
|
|
UINT nExtFmt = DdeData::GetExternalFormat( nIntFmt );
|
|
DdeInstData* pInst = ImpGetInstData();
|
|
|
|
if ( nType == XTYP_EXECUTE )
|
|
hItem = nullptr;
|
|
if ( nType != XTYP_EXECUTE && nType != XTYP_POKE )
|
|
{
|
|
pData = nullptr;
|
|
nData = 0;
|
|
}
|
|
if ( nTime )
|
|
{
|
|
HDDEDATA hData = DdeClientTransaction( static_cast<LPBYTE>(const_cast<void *>(pData)),
|
|
nData, rDde.pImp->hConv,
|
|
hItem, nExtFmt, static_cast<UINT>(nType),
|
|
static_cast<DWORD>(nTime), nullptr );
|
|
|
|
rDde.pImp->nStatus = DdeGetLastError( pInst->hDdeInstCli );
|
|
if( hData && nType == XTYP_REQUEST )
|
|
{
|
|
{
|
|
DdeData d;
|
|
d.xImp->hData = hData;
|
|
d.xImp->nFmt = nIntFmt;
|
|
d.Lock();
|
|
Data( &d );
|
|
}
|
|
DdeFreeDataHandle( hData );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nId && rDde.pImp->hConv )
|
|
DdeAbandonTransaction( pInst->hDdeInstCli, rDde.pImp->hConv, nId);
|
|
nId = 0;
|
|
bBusy = true;
|
|
DWORD result;
|
|
HDDEDATA hRet = DdeClientTransaction( static_cast<LPBYTE>(const_cast<void *>(pData)), nData,
|
|
rDde.pImp->hConv, hItem, nExtFmt,
|
|
static_cast<UINT>(nType), TIMEOUT_ASYNC,
|
|
&result );
|
|
nId = result;
|
|
rDde.pImp->nStatus = hRet ? DMLERR_NO_ERROR
|
|
: DdeGetLastError( pInst->hDdeInstCli );
|
|
}
|
|
}
|
|
|
|
OUString DdeTransaction::GetName() const
|
|
{
|
|
return pName->toOUString();
|
|
}
|
|
|
|
void DdeTransaction::Data( const DdeData* p )
|
|
{
|
|
comphelper::SolarMutex *pSolarMutex = comphelper::SolarMutex::get();
|
|
if ( pSolarMutex )
|
|
{
|
|
pSolarMutex->acquire();
|
|
aData.Call( p );
|
|
pSolarMutex = comphelper::SolarMutex::get();
|
|
if ( pSolarMutex )
|
|
pSolarMutex->release();
|
|
}
|
|
}
|
|
|
|
void DdeTransaction::Done( bool bDataValid )
|
|
{
|
|
aDone.Call( bDataValid );
|
|
}
|
|
|
|
DdeLink::DdeLink( DdeConnection& d, const OUString& aItemName, tools::Long n )
|
|
: DdeTransaction (d, aItemName, n)
|
|
{
|
|
}
|
|
|
|
DdeLink::~DdeLink()
|
|
{
|
|
nType = sal_uInt16(XTYP_ADVSTOP);
|
|
nTime = 0;
|
|
}
|
|
|
|
void DdeLink::Notify()
|
|
{
|
|
aNotify.Call( nullptr );
|
|
}
|
|
|
|
DdeRequest::DdeRequest( DdeConnection& d, const OUString& i, tools::Long n )
|
|
: DdeTransaction( d, i, n )
|
|
{
|
|
nType = XTYP_REQUEST;
|
|
}
|
|
|
|
DdeHotLink::DdeHotLink( DdeConnection& d, const OUString& i )
|
|
: DdeLink( d, i, 0 )
|
|
{
|
|
nType = XTYP_ADVSTART;
|
|
}
|
|
|
|
DdePoke::DdePoke( DdeConnection& d, const OUString& i, const DdeData& rData,
|
|
tools::Long n )
|
|
: DdeTransaction( d, i, n )
|
|
{
|
|
aDdeData = rData;
|
|
nType = XTYP_POKE;
|
|
}
|
|
|
|
DdeExecute::DdeExecute( DdeConnection& d, const OUString& rData, tools::Long n )
|
|
: DdeTransaction( d, OUString(), n )
|
|
{
|
|
aDdeData = DdeData( rData.getStr(), sizeof(sal_Unicode) * (rData.getLength() + 1), SotClipboardFormatId::STRING );
|
|
nType = XTYP_EXECUTE;
|
|
}
|
|
|
|
tools::Long DdeConnection::GetError() const
|
|
{
|
|
return pImp->nStatus;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|