/* -*- 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 .
 */
 
// SOActiveX.cpp : Implementation of CSOActiveX
 
#include "StdAfx2.h"
#include "SOActiveX.h"
#include "SOComWindowPeer.h"
#include "SODispatchInterceptor.h"
#include "SOActionsApproval.h"
 
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#endif
#include <so_activex.h>
#if defined __clang__
#pragma clang diagnostic pop
#endif
 
#define STAROFFICE_WINDOWCLASS L"SOParentWindow"
 
 
void OutputError_Impl( HWND hw, HRESULT ErrorCode )
{
    LPWSTR sMessage = nullptr;
    FormatMessageW(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        nullptr,
        ErrorCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        reinterpret_cast<LPWSTR>(&sMessage),
        0,
        nullptr
    );
    MessageBoxW( hw, sMessage, nullptr, MB_OK | MB_ICONINFORMATION );
    HeapFree( GetProcessHeap(), 0, sMessage );
}
 
HRESULT ExecuteFunc( IDispatch* idispUnoObject,
                     OLECHAR const * sFuncName,
                     CComVariant* params,
                     unsigned int count,
                     CComVariant* pResult )
{
    if( !idispUnoObject )
        return E_FAIL;
 
    DISPID id;
    HRESULT hr = idispUnoObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sFuncName), 1, LOCALE_USER_DEFAULT, &id);
    if( !SUCCEEDED( hr ) ) return hr;
 
    DISPPARAMS dispparams= { params, nullptr, count, 0};
 
    // DEBUG
    EXCEPINFO myInfo;
    hr = idispUnoObject->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD,
                    &dispparams, pResult, &myInfo, nullptr);
 
    // for debugging purposes
    // USES_CONVERSION;
    // if ( !SUCCEEDED( hr ) )
    //  ::MessageBox( NULL, OLE2A( myInfo.bstrDescription ), OLE2A( myInfo.bstrSource ), MB_OK | MB_ICONINFORMATION );
 
    return hr;
}
 
HRESULT GetIDispByFunc( IDispatch* idispUnoObject,
                          OLECHAR const * sFuncName,
                          CComVariant* params,
                          unsigned int count,
                          CComPtr<IDispatch>& pdispResult )
{
    if( !idispUnoObject )
        return E_FAIL;
 
    CComVariant result;
    HRESULT hr = ExecuteFunc( idispUnoObject, sFuncName, params, count, &result );
    if( !SUCCEEDED( hr ) ) return hr;
 
    if( result.vt != VT_DISPATCH || result.pdispVal == nullptr )
        return E_FAIL;
 
    pdispResult = CComPtr<IDispatch>( result.pdispVal );
 
    return S_OK;
}
 
HRESULT PutPropertiesToIDisp( IDispatch* pdispObject,
                              OLECHAR const ** sMemberNames,
                              CComVariant* pVariant,
                              unsigned int count )
{
    for( unsigned int ind = 0; ind < count; ind++ )
    {
        DISPID id;
        HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sMemberNames[ind]), 1, LOCALE_USER_DEFAULT, &id );
        if( !SUCCEEDED( hr ) ) return hr;
 
        hr = CComDispatchDriver::PutProperty( pdispObject, id, &pVariant[ind] );
        if( !SUCCEEDED( hr ) ) return hr;
    }
 
    return S_OK;
}
 
HRESULT GetPropertiesFromIDisp( IDispatch* pdispObject,
                                OLECHAR const ** sMemberNames,
                                CComVariant* pVariant,
                                unsigned int count )
{
    for( unsigned int ind = 0; ind < count; ind++ )
    {
        DISPID id;
        HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sMemberNames[ind]), 1, LOCALE_USER_DEFAULT, &id );
        if( !SUCCEEDED( hr ) ) return hr;
 
        hr = CComDispatchDriver::GetProperty( pdispObject, id, &pVariant[ind] );
        if( !SUCCEEDED( hr ) ) return hr;
    }
 
    return S_OK;
}
 
// CSOActiveX
 
CSOActiveX::CSOActiveX()
: mCookie(0)
, mCurFileUrl( L"private:factory/swriter" )
, mbLoad( FALSE )
, mbViewOnly( TRUE )
, mParentWin( nullptr )
, mOffWin( nullptr )
, mpDispatchInterceptor( nullptr )
, mnVersion( SO_NOT_DETECTED )
, mbReadyForActivation( FALSE )
, mbDrawLocked( FALSE )
{
    CLSID const clsFactory = {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}};
    HRESULT hr = CoCreateInstance( clsFactory, nullptr, CLSCTX_ALL, __uuidof(IDispatch), reinterpret_cast<void**>(&mpDispFactory));
    if( !SUCCEEDED( hr ) )
        OutputError_Impl( nullptr, hr );
 
    mPWinClass.style            = CS_HREDRAW|CS_VREDRAW;
    mPWinClass.lpfnWndProc      = DefWindowProcW;
    mPWinClass.cbClsExtra       = 0;
    mPWinClass.cbWndExtra       = 0;
    mPWinClass.hInstance        = GetModuleHandleW(nullptr); //myInstance;
    mPWinClass.hIcon            = nullptr;
    mPWinClass.hCursor          = nullptr;
    mPWinClass.hbrBackground    = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
    mPWinClass.lpszMenuName     = nullptr;
    mPWinClass.lpszClassName    = STAROFFICE_WINDOWCLASS;
 
    RegisterClassW(&mPWinClass);
}
 
CSOActiveX::~CSOActiveX()
{
    Cleanup();
 
}
 
HRESULT CSOActiveX::Cleanup()
{
    CComVariant dummyResult;
 
    if( mpDispatchInterceptor )
    {
        if( mpDispFrame )
        {
            // remove dispatch interceptor
            CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );
                CComVariant aVariant( pIDispDispInter );
            ExecuteFunc( mpDispFrame,
                         L"releaseDispatchProviderInterceptor",
                         &aVariant,
                         1,
                         &dummyResult );
        }
 
        mpDispatchInterceptor->ClearParent();
        mpDispatchInterceptor->Release();
        mpDispatchInterceptor = nullptr;
    }
 
    mpDispTempFile = CComPtr< IDispatch >();
    mbReadyForActivation = FALSE;
 
    if( mpInstanceLocker )
    {
        ExecuteFunc( mpInstanceLocker, L"dispose", nullptr, 0, &dummyResult );
        mpInstanceLocker = CComPtr< IDispatch >();
    }
 
    if( mpDispFrame )
    {
        BOOL bCloserActivated = FALSE;
 
        CComPtr<IDispatch> pDispDocumentCloser;
        CComVariant aDocCloser( L"com.sun.star.embed.DocumentCloser" );
        HRESULT hr = GetIDispByFunc( mpDispFactory,
                                     L"createInstance",
                                     &aDocCloser,
                                     1,
                                     pDispDocumentCloser );
        if ( SUCCEEDED( hr ) && pDispDocumentCloser )
        {
            SAFEARRAY FAR* pInitFrame = SafeArrayCreateVector( VT_VARIANT, 0, 1 );
            long nInitInd = 0;
            CComVariant pFrameVariant( mpDispFrame );
            SafeArrayPutElement( pInitFrame, &nInitInd, &pFrameVariant );
            CComVariant aVarInitFrame;
            aVarInitFrame.vt = VT_ARRAY | VT_VARIANT; aVarInitFrame.parray = pInitFrame;
            hr = ExecuteFunc( pDispDocumentCloser, L"initialize", &aVarInitFrame, 1, &dummyResult );
            if( SUCCEEDED( hr ) )
            {
                // the following call will let the closing happen
                hr = ExecuteFunc( pDispDocumentCloser, L"dispose", nullptr, 0, &dummyResult );
                bCloserActivated = SUCCEEDED( hr );
            }
        }
 
        if ( !bCloserActivated )
        {
            CComVariant aPropVar;
            aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_TRUE;
            if ( !SUCCEEDED( ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult ) ) )
                ExecuteFunc( mpDispFrame, L"dispose", nullptr, 0, &dummyResult );
        }
 
        mpDispFrame = CComPtr< IDispatch >();
    }
 
    if( ::IsWindow( mOffWin ) )
        ::DestroyWindow( mOffWin );
 
    TerminateOffice();
 
    return S_OK;
}
 
HRESULT CSOActiveX::TerminateOffice()
{
    // create desktop
    CComPtr<IDispatch> pdispDesktop;
    CComVariant aDesktopServiceName( L"com.sun.star.frame.Desktop" );
 
    HRESULT hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aDesktopServiceName, 1, pdispDesktop );
    if( !pdispDesktop || !SUCCEEDED( hr ) ) return hr;
 
    // create tree of frames
    CComPtr<IDispatch> pdispChildren;
    hr = GetIDispByFunc( pdispDesktop, L"getFrames", nullptr, 0, pdispChildren );
    if( !pdispChildren || !SUCCEEDED( hr ) ) return hr;
 
    CComVariant aFrames;
    CComVariant nFlag( 4 );
    hr = ExecuteFunc( pdispChildren, L"queryFrames", &nFlag, 1, &aFrames );
    if ( SUCCEEDED( hr ) )
    {
        if ( ( aFrames.vt == ( VT_ARRAY | VT_DISPATCH ) || aFrames.vt == ( VT_ARRAY | VT_VARIANT ) )
          && ( !aFrames.parray || (aFrames.parray->cDims == 1 && aFrames.parray->rgsabound[0].cElements == 0) ) )
        {
            // there is no frames open
            // TODO: check whether the frames are hidden if they are open?
            CComVariant dummyResult;
            hr = ExecuteFunc( pdispDesktop, L"terminate", nullptr, 0, &dummyResult );
        }
    }
 
    return hr;
}
 
STDMETHODIMP CSOActiveX::InitNew ()
{
    mnVersion = GetVersionConnected();
    mbLoad = TRUE;
    return S_OK;
}
 
STDMETHODIMP CSOActiveX::Load ( LPSTREAM /*pStm*/ )
{
    mnVersion = GetVersionConnected();
    mbLoad = TRUE;
 
    // may be later?
    // for now just ignore
 
    return S_OK;
}
 
STDMETHODIMP CSOActiveX::Load( LPPROPERTYBAG pPropBag, LPERRORLOG /*pErrorLog*/ )
{
    mnVersion = GetVersionConnected();
 
    IPropertyBag2* pPropBag2;
    HRESULT hr = pPropBag->QueryInterface( IID_IPropertyBag2, reinterpret_cast<void**>(&pPropBag2) );
    //ATLASSERT( hr >= 0 );
 
    if( !SUCCEEDED( hr ) )
        return hr;
 
    unsigned long aNum;
    hr = pPropBag2->CountProperties( &aNum );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
        return hr;
 
    PROPBAG2* aPropNames = new PROPBAG2[aNum];
    unsigned long aReaded;
 
    hr = pPropBag2->GetPropertyInfo( 0,
                                     aNum,
                                     aPropNames,
                                     &aReaded );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] aPropNames;
        return hr;
    }
 
    CComVariant* aVal = new CComVariant[aNum];
    HRESULT*     hvs = new HRESULT[aNum];
    hr = pPropBag2->Read( aNum,
                          aPropNames,
                          nullptr,
                          aVal,
                          hvs );
    //ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] hvs;
        delete[] aVal;
        delete[] aPropNames;
        return hr;
    }
 
    USES_CONVERSION;
    for( unsigned long ind = 0; ind < aNum; ind++ )
    {
        // all information from the 'object' tag is in strings
        if( aVal[ind].vt == VT_BSTR && !strcmp( OLE2T( aPropNames[ind].pstrName ), "src" ) )
        {
            mCurFileUrl = wcsdup( aVal[ind].bstrVal );
        }
        else if( aVal[ind].vt == VT_BSTR
                && !strcmp( OLE2T( aPropNames[ind].pstrName ), "readonly" ) )
        {
            if( !strcmp( OLE2T( aVal[ind].bstrVal ), "true" ) )
            {
                // the default value
                mbViewOnly = TRUE;
            }
            else
            {
                mbViewOnly = FALSE;
            }
        }
    }
 
    delete[] hvs;
    delete[] aVal;
    delete[] aPropNames;
 
    if( !mpDispFactory )
        return hr;
 
    mbReadyForActivation = FALSE;
    hr = CBindStatusCallback<CSOActiveX>::Download( this, &CSOActiveX::CallbackCreateXInputStream, const_cast<OLECHAR *>(mCurFileUrl), m_spClientSite, FALSE );
    if ( hr == MK_S_ASYNCHRONOUS )
        hr = S_OK;
 
    if ( !SUCCEEDED( hr ) )
    {
        // trigger initialization without stream
        mbLoad = TRUE;
 
        Invalidate();
        UpdateWindow();
    }
 
    return hr;
}
 
HRESULT CSOActiveX::GetUnoStruct( OLECHAR const * sStructName, CComPtr<IDispatch>& pdispResult )
{
    CComVariant aComStruct( sStructName );
    return GetIDispByFunc( mpDispFactory, L"Bridge_GetStruct", &aComStruct, 1, pdispResult );
}
 
HRESULT CSOActiveX::GetUrlStruct( OLECHAR const * sUrl, CComPtr<IDispatch>& pdispUrl )
{
    HRESULT hr = GetUnoStruct( L"com.sun.star.util.URL", pdispUrl );
    if( !SUCCEEDED( hr ) ) return hr;
 
    OLECHAR const * sURLMemberName = L"Complete";
    DISPID nURLID;
    hr = pdispUrl->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sURLMemberName), 1, LOCALE_USER_DEFAULT, &nURLID );
    if( !SUCCEEDED( hr ) ) return hr;
    CComVariant aComUrl( sUrl );
    hr = CComDispatchDriver::PutProperty( pdispUrl, nURLID, &aComUrl );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComPtr<IDispatch> pdispTransformer;
    CComVariant aServiceName( L"com.sun.star.util.URLTransformer" );
    hr = GetIDispByFunc( mpDispFactory,
                         L"createInstance",
                         &aServiceName,
                         1,
                         pdispTransformer );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComVariant dummyResult;
    CComVariant aParam[2];
    aParam[1].ppdispVal = &pdispUrl;
    aParam[1].vt = VT_DISPATCH | VT_BYREF;
    aParam[0] = CComVariant( L"file:///" );
 
    hr = ExecuteFunc( pdispTransformer, L"parseSmart", aParam, 2, &dummyResult );
    if( !SUCCEEDED( hr ) || dummyResult.vt != VT_BOOL || !dummyResult.boolVal ) return hr;
 
    return S_OK;
}
 
HRESULT CSOActiveX::SetLayoutManagerProps()
{
    if ( !mpDispFrame )
        return E_FAIL;
 
    CComVariant pVarLayoutMgr;
    OLECHAR const * sLMPropName = L"LayoutManager";
    HRESULT hr = GetPropertiesFromIDisp( mpDispFrame, &sLMPropName, &pVarLayoutMgr, 1 );
    if( pVarLayoutMgr.vt != VT_DISPATCH || pVarLayoutMgr.pdispVal == nullptr )
        return E_FAIL;
 
      CComPtr<IDispatch> pdispLM( pVarLayoutMgr.pdispVal );
 
 
    if( !SUCCEEDED( hr ) || !pdispLM )
        return E_FAIL;
 
    OLECHAR const * sATName = L"AutomaticToolbars";
    CComVariant pATProp;
    pATProp.vt = VT_BOOL; pATProp.boolVal = VARIANT_FALSE ;
    hr = PutPropertiesToIDisp( pdispLM, &sATName, &pATProp, 1 );
 
    return hr;
}
 
HRESULT CSOActiveX::CreateFrameOldWay( HWND hwnd, int width, int height )
{
    if( !mpDispFactory )
        return E_FAIL;
 
    // create window handle holder
    CComPtr< CComObject< SOComWindowPeer > > pPeerToSend = new CComObject<SOComWindowPeer>();
    pPeerToSend->SetHWNDInternally( hwnd );
    CComQIPtr< IDispatch, &IID_IDispatch > pIDispToSend( pPeerToSend );
 
    // create rectangle structure
    CComPtr<IDispatch> pdispRectangle;
    HRESULT hr = GetUnoStruct( L"com.sun.star.awt.Rectangle", pdispRectangle );
    if( !SUCCEEDED( hr ) ) return hr;
 
    OLECHAR const * sRectMemberNames[4] = { L"X",
                                      L"Y",
                                      L"Width",
                                      L"Height" };
    CComVariant pRectVariant[4];
    pRectVariant[0] = pRectVariant[1] = pRectVariant[2] = pRectVariant[3] = CComVariant( 0 );
 
    hr = PutPropertiesToIDisp( pdispRectangle, sRectMemberNames, pRectVariant, 4 );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create WindowDescriptor structure
    CComPtr<IDispatch> pdispWinDescr;
    hr = GetUnoStruct( L"com.sun.star.awt.WindowDescriptor", pdispWinDescr );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // fill in descriptor with info
    OLECHAR const * sDescriptorMemberNames[6] = { L"Type",
                                 L"WindowServiceName",
                                 L"ParentIndex",
                                 L"Parent",
                                 L"Bounds",
                                 L"WindowAttributes" };
    CComVariant pDescriptorVar[6];
    pDescriptorVar[0] = CComVariant( 0 );
    pDescriptorVar[1] = CComVariant( L"workwindow" );
    pDescriptorVar[2] = CComVariant( 1 );
    pDescriptorVar[3] = CComVariant( pIDispToSend );
    pDescriptorVar[4] = CComVariant( pdispRectangle );
    pDescriptorVar[5] = CComVariant( 33 );
    hr = PutPropertiesToIDisp( pdispWinDescr, sDescriptorMemberNames, pDescriptorVar, 6 );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create XToolkit instance
    CComPtr<IDispatch> pdispToolkit;
    CComVariant aServiceName( L"com.sun.star.awt.Toolkit" );
    hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispToolkit );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create window with toolkit
    CComVariant aWinDescr( pdispWinDescr );
    hr = GetIDispByFunc( pdispToolkit, L"createWindow", &aWinDescr, 1, mpDispWin );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create frame
    aServiceName = CComVariant( L"com.sun.star.frame.Task" );
    hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
    if( !SUCCEEDED( hr ) || !mpDispFrame )
    {
        // the interface com.sun.star.frame.Task is removed in 6.1
        // but the interface com.sun.star.frame.Frame has some bugs in 6.0
        aServiceName = CComVariant( L"com.sun.star.frame.Frame" );
        hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
        if( !SUCCEEDED( hr ) ) return hr;
    }
 
    // initialize frame
    CComVariant dummyResult;
    CComVariant aDispWin( mpDispWin );
    hr = ExecuteFunc( mpDispFrame, L"initialize", &aDispWin, 1, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // set some properties to the layout manager, ignore errors for now
    SetLayoutManagerProps();
 
    // create desktop
    CComPtr<IDispatch> pdispDesktop;
    aServiceName = CComVariant( L"com.sun.star.frame.Desktop" );
    hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispDesktop );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create tree of frames
    CComPtr<IDispatch> pdispChildren;
    hr = GetIDispByFunc( pdispDesktop, L"getFrames", nullptr, 0, pdispChildren );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // insert new frame into desktop hierarchy
    CComVariant aDispFrame( mpDispFrame );
    hr = ExecuteFunc( pdispChildren, L"append", &aDispFrame, 1, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // initialize window
    CComVariant aTransparent( long(0xFFFFFFFF) );
    hr = ExecuteFunc( mpDispWin, L"setBackground", &aTransparent, 1, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComVariant aTrue( TRUE );
    hr = ExecuteFunc( mpDispWin, L"setVisible", &aTrue, 1, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComVariant aPosArgs[5];
    aPosArgs[4] = CComVariant( 0 );
    aPosArgs[3] = CComVariant( 0 );
    aPosArgs[2] = CComVariant( width );
    aPosArgs[1] = CComVariant( height );
    aPosArgs[0] = CComVariant( 12 );
    hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    // create frame locker if there is such service
    aServiceName = CComVariant( L"com.sun.star.embed.InstanceLocker" );
    hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpInstanceLocker );
    if( SUCCEEDED( hr ) && mpInstanceLocker )
    {
        SAFEARRAY FAR* pInitVals = SafeArrayCreateVector( VT_VARIANT, 0, 3 );
 
        // the first sequence element
        long nInitInd = 0;
        CComVariant pFrameVariant( mpDispFrame );
        SafeArrayPutElement( pInitVals, &nInitInd, &pFrameVariant );
 
        // the second sequence element
        nInitInd = 1;
        CComVariant pStrArr( 1 );
        SafeArrayPutElement( pInitVals, &nInitInd, &pStrArr );
 
        // the third sequence element
        nInitInd = 2;
        CComPtr<IDispatch> pdispValueObj;
        hr = GetIDispByFunc( mpDispFactory, L"Bridge_GetValueObject", nullptr, 0, pdispValueObj );
        if( !SUCCEEDED( hr ) || !pdispValueObj ) return hr;
 
        CComVariant aValueArgs[2];
        aValueArgs[1] = CComVariant( L"com.sun.star.embed.XActionsApproval" );
        CComPtr< CComObject< SOActionsApproval > > pApproval( new CComObject<SOActionsApproval>() );
        aValueArgs[0] = CComVariant ( pApproval );
 
        hr = ExecuteFunc( pdispValueObj, L"Set", aValueArgs, 2, &dummyResult );
        if( !SUCCEEDED( hr ) ) return hr;
 
        CComVariant aValueObj( pdispValueObj );
        SafeArrayPutElement( pInitVals, &nInitInd, &aValueObj );
 
        // execute initialize()
        CComVariant aVarInitVals;
        aVarInitVals.vt = VT_ARRAY | VT_VARIANT; aVarInitVals.parray = pInitVals;
        hr = ExecuteFunc( mpInstanceLocker, L"initialize", &aVarInitVals, 1, &dummyResult );
        if( !SUCCEEDED( hr ) ) return hr;
    }
 
    return S_OK;
}
 
HRESULT CSOActiveX::CallLoadComponentFromURL1PBool( OLECHAR const * sUrl, OLECHAR const * sArgName, BOOL sArgVal )
{
    SAFEARRAY FAR* pPropVals = SafeArrayCreateVector( VT_DISPATCH, 0, 1 );
    long ix = 0;
    CComPtr<IDispatch> pdispPropVal;
    HRESULT hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
    if( !SUCCEEDED( hr ) ) return hr;
 
    OLECHAR const * sPropMemberNames[2] = { L"Name", L"Value" };
    CComVariant pPropVar[2];
    pPropVar[0] = CComVariant( sArgName );
    pPropVar[1].vt = VT_BOOL; pPropVar[1].boolVal = sArgVal ? VARIANT_TRUE : VARIANT_FALSE ;
    hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
    if( !SUCCEEDED( hr ) ) return hr;
 
    SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
 
    CComVariant aDispArgs[4];
    aDispArgs[3] = CComVariant( sUrl );
    aDispArgs[2] = CComVariant( L"_self" );
    aDispArgs[1] = CComVariant( 0 );
    // aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
    aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;
 
    CComVariant dummyResult;
    hr = ExecuteFunc( mpDispFrame, L"loadComponentFromURL", aDispArgs, 4, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    return S_OK;
}
 
HRESULT CSOActiveX::CallDispatchMethod( OLECHAR const * sUrl,
                                        CComVariant* aArgNames,
                                        CComVariant* aArgVals,
                                        unsigned int count )
{
    CComPtr<IDispatch> pdispURL;
    HRESULT hr = GetUrlStruct( sUrl, pdispURL );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComPtr<IDispatch> pdispXDispatch;
    CComVariant aArgs[3];
    aArgs[2] = CComVariant( pdispURL );
    aArgs[1] = CComVariant( L"" );
    aArgs[0] = CComVariant( int(0) );
    hr = GetIDispByFunc( mpDispFrame,
                         L"queryDispatch",
                         aArgs,
                         3,
                         pdispXDispatch );
    if( !SUCCEEDED( hr ) ) return hr;
 
    SAFEARRAY FAR* pPropVals = SafeArrayCreateVector( VT_DISPATCH, 0, count );
    for( long ix = 0; ix < static_cast<long>(count); ix ++ )
    {
        CComPtr<IDispatch> pdispPropVal;
        hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
        if( !SUCCEEDED( hr ) ) return hr;
 
        OLECHAR const * sPropMemberNames[2] = { L"Name", L"Value" };
        CComVariant pPropVar[2];
        pPropVar[0] = aArgNames[ix];
        pPropVar[1] = aArgVals[ix];
        hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
        if( !SUCCEEDED( hr ) ) return hr;
 
        SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
    }
 
    CComVariant aDispArgs[2];
    aDispArgs[1] = CComVariant( pdispURL );
    // aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
    aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;
 
    CComVariant dummyResult;
    hr = ExecuteFunc( pdispXDispatch, L"dispatch", aDispArgs, 2, &dummyResult );
    if( !SUCCEEDED( hr ) ) return hr;
 
    return S_OK;
}
 
void CSOActiveX::CallbackCreateXInputStream( CBindStatusCallback<CSOActiveX>* /*pbsc*/, BYTE* pBytes, DWORD dwSize )
{
    if ( mbReadyForActivation )
        return;
 
    BOOL bSuccess = FALSE;
    BOOL bFinishDownload = FALSE;
    if ( !pBytes )
    {
        // means the download is finished, dwSize contains hresult
        bFinishDownload = TRUE;
        if ( SUCCEEDED( dwSize ) )
            bSuccess = TRUE;
    }
    else
    {
        HRESULT hr = S_OK;
 
        if ( !mpDispTempFile )
        {
            CComVariant aServiceName( L"com.sun.star.io.TempFile" );
            hr = GetIDispByFunc( mpDispFactory,
                                 L"createInstance",
                                 &aServiceName,
                                 1,
                                 mpDispTempFile );
        }
 
        if( SUCCEEDED( hr ) && mpDispTempFile )
        {
            SAFEARRAY FAR* pDataArray = SafeArrayCreateVector( VT_I1, 0, dwSize );
 
            if ( pDataArray )
            {
                hr = SafeArrayLock( pDataArray );
                if ( SUCCEEDED( hr ) )
                {
                    for( DWORD ix = 0; ix < dwSize; ix++ )
                        static_cast<BYTE*>(pDataArray->pvData)[ix] = pBytes[ix];
                    hr = SafeArrayUnlock( pDataArray );
                    if ( SUCCEEDED( hr ) )
                    {
                        CComVariant aArgs[1];
                        aArgs[0].vt = VT_ARRAY | VT_I1; aArgs[0].parray = pDataArray;
                        CComVariant dummyResult;
                        hr = ExecuteFunc( mpDispTempFile, L"writeBytes", aArgs, 1, &dummyResult );
                        if( SUCCEEDED( hr ) )
                            bSuccess = TRUE;
                    }
                }
            }
        }
    }
 
    if ( !bSuccess )
    {
        // the download failed, let StarOffice download
        bFinishDownload = TRUE;
        mpDispTempFile = CComPtr< IDispatch >();
    }
 
    if ( bFinishDownload )
    {
        // trigger the loading now
        mbLoad = TRUE;
        mbReadyForActivation = TRUE;
 
        Invalidate();
        UpdateWindow();
    }
}
 
HRESULT CSOActiveX::LoadURLToFrame( )
{
    CComVariant aArgNames[4] = { L"ReadOnly", L"ViewOnly", L"AsTemplate", L"InputStream" };
    CComVariant aArgVals[4];
    unsigned int nCount = 3; // the 4-th argument is used only if the stream can be retrieved
 
    aArgVals[0].vt = VT_BOOL; aArgVals[0].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
    aArgVals[1].vt = VT_BOOL; aArgVals[1].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
    aArgVals[2].vt = VT_BOOL; aArgVals[2].boolVal = VARIANT_FALSE;
 
    if ( mpDispTempFile )
    {
        aArgVals[3] = CComVariant( mpDispTempFile );
        nCount = 4;
    }
 
    HRESULT hr = CallDispatchMethod( mCurFileUrl, aArgNames, aArgVals, nCount );
    if( !SUCCEEDED( hr ) ) return hr;
 
    CComVariant aBarName( L"MenuBarVisible" );
    CComVariant aBarVis;
    aBarVis.vt = VT_BOOL; aBarVis.boolVal = VARIANT_FALSE;
    hr = CallDispatchMethod( L"slot:6661", &aBarName, &aBarVis, 1 );
    // does not work for some documents, but it is no error
    // if( !SUCCEEDED( hr ) ) return hr;
 
    // try to get the model and set the presetation specific property, the setting will fail for other document formats
    CComPtr<IDispatch> pdispController;
    hr = GetIDispByFunc( mpDispFrame, L"getController", nullptr, 0, pdispController );
    if ( SUCCEEDED( hr ) && pdispController )
    {
        CComPtr<IDispatch> pdispModel;
        hr = GetIDispByFunc( pdispController, L"getModel", nullptr, 0, pdispModel );
        if ( SUCCEEDED( hr ) && pdispModel )
        {
            CComPtr<IDispatch> pdispPres;
            hr = GetIDispByFunc( pdispModel, L"getPresentation", nullptr, 0, pdispPres );
            if ( SUCCEEDED( hr ) && pdispPres )
            {
                // this is a presentation
                // let the slide show be shown in the document window
                OLECHAR const * pPropName = L"IsFullScreen";
                CComVariant pPresProp;
                pPresProp.vt = VT_BOOL; pPresProp.boolVal = VARIANT_FALSE ;
                hr = PutPropertiesToIDisp( pdispPres, &pPropName, &pPresProp, 1 );
 
                // start the slide show
                if ( SUCCEEDED( hr ) )
                {
                    CComVariant dummyResult;
                    ExecuteFunc( pdispPres, L"Start", nullptr, 0, &dummyResult );
                }
            }
        }
    }
 
    // create dispatch interceptor
    mpDispatchInterceptor = new CComObject< SODispatchInterceptor >();
    mpDispatchInterceptor->AddRef();
    mpDispatchInterceptor->SetParent( this );
    CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );
 
    // register dispatch interceptor in the frame
        CComVariant aDispVariant( pIDispDispInter );
    CComVariant dummyResult;
    hr = ExecuteFunc( mpDispFrame,
                      L"registerDispatchProviderInterceptor",
                                          &aDispVariant,
                      1,
                      &dummyResult );
 
    if( !SUCCEEDED( hr ) ) return hr;
 
    return S_OK;
}
 
SOVersion CSOActiveX::GetVersionConnected()
{
    SOVersion bResult = SO_NOT_DETECTED;
    if( mpDispFactory )
    {
        // create ConfigurationProvider instance
        CComPtr<IDispatch> pdispConfProv;
        CComVariant aServiceName( L"com.sun.star.configuration.ConfigurationProvider" );
        HRESULT hr = GetIDispByFunc( mpDispFactory,
                             L"createInstance",
                             &aServiceName,
                             1,
                             pdispConfProv );
 
        if( SUCCEEDED( hr ) && pdispConfProv )
        {
            CComPtr<IDispatch> pdispConfAccess;
 
            SAFEARRAY* pInitParams = SafeArrayCreateVector( VT_VARIANT, 0, 1 );
 
            if( pInitParams )
            {
                long ix = 0;
                CComVariant aConfPath( L"org.openoffice.Setup" );
                SafeArrayPutElement( pInitParams, &ix, &aConfPath );
 
                CComVariant aArgs[2];
                aArgs[1] = CComVariant( L"com.sun.star.configuration.ConfigurationAccess" );
                aArgs[0].vt = VT_ARRAY | VT_VARIANT; aArgs[0].parray = pInitParams;
 
                hr = GetIDispByFunc( pdispConfProv,
                                     L"createInstanceWithArguments",
                                    aArgs,
                                     2,
                                     pdispConfAccess );
 
                if( SUCCEEDED( hr ) && pdispConfAccess )
                {
                    CComVariant aOfficeName;
 
                    CComVariant aProductName( L"Product/ooName" );
                    hr = ExecuteFunc( pdispConfAccess,
                                        L"getByHierarchicalName",
                                        &aProductName,
                                        1,
                                        &aOfficeName );
 
                    if( SUCCEEDED( hr ) && aOfficeName.vt == VT_BSTR )
                    {
                        CComVariant aOfficeVersion;
 
                        CComVariant aProductVersion( L"Product/ooSetupVersion" );
                        hr = ExecuteFunc( pdispConfAccess,
                                            L"getByHierarchicalName",
                                            &aProductVersion,
                                            1,
                                            &aOfficeVersion );
 
                        if( SUCCEEDED( hr ) && aOfficeVersion.vt == VT_BSTR )
                        {
                            USES_CONVERSION;
                            if( !strcmp( OLE2T( aOfficeName.bstrVal ), "StarOffice" ) )
                            {
                                if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "6.1", 3 ) )
                                    bResult = SO_61;
                                else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "6.0", 3 ) )
                                    bResult = SO_60;
                                else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "5.2", 3 ) )
                                    bResult = SO_52;
                                else
                                    bResult = SO_UNKNOWN;
                            }
                            else // OpenOffice
                            {
                                if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "1.1", 3 ) )
                                    bResult = OO_11;
                                else if( !strncmp( OLE2T( aOfficeVersion.bstrVal ), "1.0", 3 ) )
                                    bResult = OO_10;
                                else
                                    bResult = OO_UNKNOWN;
                            }
                        }
                    }
                }
            }
        }
    }
 
    return bResult;
}
 
class LockingGuard
{
    BOOL& mbLocked;
public:
    explicit LockingGuard( BOOL& bLocked )
    : mbLocked( bLocked )
    {
        mbLocked = TRUE;
    }
 
    ~LockingGuard()
    {
        mbLocked = FALSE;
    }
};
 
HRESULT CSOActiveX::OnDrawAdvanced( ATL_DRAWINFO& di )
{
    // This method is called only in main thread, no need to lock it
 
    // Get read of reentrance problems
    if ( mbDrawLocked )
        return S_OK;
    LockingGuard aGuard( mbDrawLocked );
 
    if( m_spInPlaceSite && mCurFileUrl && mbReadyForActivation )
    {
        HWND hwnd;
        HRESULT hr = m_spInPlaceSite->GetWindow( &hwnd );
        if( !SUCCEEDED( hr ) ) return hr;
 
        if( mParentWin != hwnd || !mOffWin )
        {
            if( mpDispFrame )
            {
                CComVariant dummyResult;
                CComVariant aPropVar;
                aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_FALSE;
                (void) ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult );
                mpDispFrame = CComPtr<IDispatch>();
            }
 
            mParentWin = hwnd;
            mOffWin = CreateWindowW(
                                STAROFFICE_WINDOWCLASS,
                                L"OfficeContainer",
                                WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,
                                di.prcBounds->left,
                                di.prcBounds->top,
                                di.prcBounds->right - di.prcBounds->left,
                                di.prcBounds->bottom - di.prcBounds->top,
                                mParentWin,
                                nullptr,
                                nullptr,
                                nullptr );
 
            ::ShowWindow( mOffWin, SW_SHOW );
        }
        else
        {
            RECT aRect;
            ::GetWindowRect( mOffWin, &aRect );
 
            if( aRect.left !=  di.prcBounds->left || aRect.top != di.prcBounds->top
             || aRect.right != di.prcBounds->right || aRect.bottom != di.prcBounds->bottom )
            {
                // on this state the office window should exist already
                ::SetWindowPos( mOffWin,
                              HWND_TOP,
                              di.prcBounds->left,
                              di.prcBounds->top,
                              di.prcBounds->right - di.prcBounds->left,
                              di.prcBounds->bottom - di.prcBounds->top,
                              SWP_NOZORDER );
 
                CComVariant aPosArgs[5];
                aPosArgs[4] = CComVariant( 0 );
                aPosArgs[3] = CComVariant( 0 );
                aPosArgs[2] = CComVariant( int(di.prcBounds->right - di.prcBounds->left) );
                aPosArgs[1] = CComVariant( int(di.prcBounds->bottom - di.prcBounds->top) );
                aPosArgs[0] = CComVariant( 12 );
                CComVariant dummyResult;
                hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
                if( !SUCCEEDED( hr ) ) return hr;
            }
        }
 
        if( !mnVersion )
        {
            OutputError_Impl( mOffWin, CS_E_INVALID_VERSION );
            return E_FAIL;
        }
 
        if( ! mpDispFrame )
        {
            hr = CreateFrameOldWay( mOffWin,
                            di.prcBounds->right - di.prcBounds->left,
                            di.prcBounds->bottom - di.prcBounds->top );
 
            if( !SUCCEEDED( hr ) )
            {
                // if the frame can not be opened do not try any more
                mbReadyForActivation = FALSE;
                OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );
                return hr;
            }
        }
 
        if( mbLoad )
        {
            hr = LoadURLToFrame();
            mbLoad = FALSE;
 
            if( !SUCCEEDED( hr ) )
            {
                // if the document can not be opened do not try any more
                mbReadyForActivation = FALSE;
 
                OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );
 
                return hr;
            }
        }
    }
    else
    {
        // activate the fallback
        CComControl<CSOActiveX>::OnDrawAdvanced( di );
    }
 
    return S_OK;
}
 
HRESULT CSOActiveX::OnDraw( ATL_DRAWINFO& di )
{
    // fallback that is activated by the parent class
    if ( di.hdcDraw )
        FillRect( di.hdcDraw, reinterpret_cast<RECT const *>(di.prcBounds), reinterpret_cast<HBRUSH>(COLOR_BACKGROUND) );
 
    return S_OK;
}
 
STDMETHODIMP CSOActiveX::SetClientSite( IOleClientSite* aClientSite )
{
    HRESULT hr = IOleObjectImpl<CSOActiveX>::SetClientSite( aClientSite );
 
    if( !aClientSite )
    {
        //ATLASSERT( mWebBrowser2 );
        if( mWebBrowser2 )
            AtlUnadvise( mWebBrowser2, DIID_DWebBrowserEvents2, mCookie );
        return hr;
    }
 
    CComPtr<IOleContainer> aContainer;
    m_spClientSite->GetContainer( &aContainer );
//  ATLASSERT( aContainer );
 
    if( SUCCEEDED( hr )  && aContainer )
    {
        CComQIPtr<IServiceProvider, &IID_IServiceProvider> aServiceProvider( aContainer );
        //ATLASSERT( aServiceProvider );
 
        if( aServiceProvider )
        {
            aServiceProvider->QueryService( SID_SInternetExplorer,
                                            IID_IWebBrowser,
                                            reinterpret_cast<void**>(&mWebBrowser2) );
//          ATLASSERT( mWebBrowser2 );
            if( mWebBrowser2 )
                AtlAdvise( mWebBrowser2, GetUnknown(), DIID_DWebBrowserEvents2, &mCookie );
        }
    }
 
    return hr;
}
 
STDMETHODIMP CSOActiveX::Invoke(DISPID dispidMember,
                                REFIID riid,
                                LCID lcid,
                                WORD wFlags,
                                DISPPARAMS* pDispParams,
                                VARIANT* pvarResult,
                                EXCEPINFO* pExcepInfo,
                                UINT* puArgErr)
{
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;
 
    if (!pDispParams)
        return DISP_E_PARAMNOTOPTIONAL;
 
    if ( dispidMember == DISPID_ONQUIT )
        Cleanup();
 
    IDispatchImpl<ISOActiveX, &IID_ISOActiveX,
                  &LIBID_SO_ACTIVEXLib>::Invoke(
             dispidMember, riid, lcid, wFlags, pDispParams,
             pvarResult, pExcepInfo, puArgErr);
 
    return S_OK;
}
 
HRESULT CSOActiveX::GetURL( const OLECHAR* url,
                              const OLECHAR* target )
{
    CComVariant aEmpty1, aEmpty2, aEmpty3;
    CComVariant aUrl( url );
    CComVariant aTarget;
    if ( target )
        aTarget = CComVariant( target );
 
    return mWebBrowser2->Navigate2( &aUrl,
                                  &aEmpty1,
                                  &aTarget,
                                  &aEmpty2,
                                  &aEmpty3 );
}
 
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V745 A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string. Consider using 'SysAllocString' function.

V768 The variable 'mnVersion' is of enum type. It is odd that it is used as a variable of a Boolean-type.

V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.

V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.

V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.