Автоматизация Word 2010: почему мое приложение обрабатывает sh, тогда как в Excel отображается ошибка 462 - PullRequest
0 голосов
/ 28 января 2020

Я использую C ++ для автоматизации Microsoft Word 2010. Когда пользователь закрывает приложение, и моя программа хочет использовать ранее полученный интерфейс IDispatch, программа падает (необработанное исключение). Код Simular VBA в Excel выдает ошибку «Ошибка 462: удаленный сервер не существует». Как я могу определить, что приложение закрыто пользователем, как это делает Excel.

#ifdef  __NO_PRECOMPILED_HEADERS__
#include        "generic/platformdefs.h"
#endif

#include    "test_word.h"

/*
 *  Include the right atlbase.h (depends on the compiler)
 */
#include    "compat/which_atlbase.h"

static  OLECHAR FAR *VISIBLE =
{
     OLESTR( "Visible" )    
} ;

static  OLECHAR FAR *QUIT =
{
     OLESTR( "quit" )   
} ;

static void
VarSetBool( VARIANT *v , BOOL value )
{
    V_VT( v ) = VT_BOOL ;
    V_BOOL( v ) = value ? VARIANT_TRUE : VARIANT_FALSE ;
}

static void
DispatchPropertyPut
(
    CComPtr<IDispatch>  dispatch    ,
    OLECHAR FAR     *property   ,
    VARIANT         *value  
)
{
    HRESULT     status      ;
    DISPID      dispid      ,
            propertyput ;
    DISPPARAMS  parameters  ;
    UINT        n_argument_error;
    VARIANT     result      ;

    /*
     *  Get the dispatch id of the method and arguments to invoke
     */
    status = dispatch->GetIDsOfNames( IID_NULL , 
                      &property , 
                      1 ,
                      LOCALE_USER_DEFAULT , 
                      &dispid ) ;

    if( !SUCCEEDED( status ) )
    {
        throw( 1 ) ;
    }

    /*
     *  Initialize result
     */
    VariantInit( &result ) ;

    /*
     *  need to be able to take the address of this
     */
    propertyput = DISPID_PROPERTYPUT ;

    /*
     *  Setup the parameters
     */
    parameters.cNamedArgs = 1 ;
    parameters.rgdispidNamedArgs = &propertyput ;
    parameters.cArgs = 1 ;
    parameters.rgvarg = value ;

    /*
     *  Get the object
     */
    status = dispatch->Invoke( dispid ,  
                   IID_NULL,              
                   LOCALE_USER_DEFAULT,                
                   DISPATCH_PROPERTYPUT ,
                   &parameters ,
                   &result ,
                   0,  
                   &n_argument_error ) ;

    /*
     *  Cleanup result if any
     */
    VariantClear( &result ) ;

    /*
     *  Success ?
     */
    if( !SUCCEEDED( status ) )
    {
        throw( 2 ) ;
    }
}

static void
DispatchInvoke
(
    CComPtr<IDispatch>  dispatch    ,
    OLECHAR FAR     *method
)
{
    DISPID      dispid      ;
    HRESULT     status      ;
    DISPPARAMS  parameters  ;

    status = dispatch->GetIDsOfNames( IID_NULL , 
                      &method , 
                      1 ,
                      LOCALE_USER_DEFAULT , 
                      &dispid ) ;

    if( !SUCCEEDED( status ) )
    {
        throw( 3 ) ;
    }

    parameters.cNamedArgs = 0 ;
    parameters.rgdispidNamedArgs = 0 ;
    parameters.cArgs = 0 ;
    parameters.rgvarg = 0 ;

    status = dispatch->Invoke( dispid ,  
                   IID_NULL,              
                   LOCALE_USER_DEFAULT,                
                   DISPATCH_METHOD ,
                   &parameters ,
                   0 ,
                   0 ,  
                   0 ) ;

    if( !SUCCEEDED( status ) )
    {
        throw( 4 ) ;
    }
}

void
test_word( int argc , char *argv[] , void *data )
{
    CComPtr<IDispatch>  word        ;
    VARIANT         v       ;
    HRESULT         hr      ;

    OleInitialize( NULL ) ;

    try
    {
        /*
         *  The metrowerks compiler doesn't handle __uuidof() 
         */
#       ifdef __MWERKS__

        hr = word.CoCreateInstance( OLESTR( "Word.Application" ) ,
                        IID_IDispatch , 
                        0 , 
                        CLSCTX_SERVER ) ;

#       else

        hr = word.CoCreateInstance( OLESTR( "Word.Application" ) , 
                        0 , 
                        CLSCTX_SERVER ) ;

#       endif

        if( !SUCCEEDED( hr ) )
        {
            throw( 6 ) ;
        }

        VariantInit( &v ) ;

        VarSetBool( &v , TRUE ) ;

        DispatchPropertyPut( word , VISIBLE , &v ) ;

        DispatchInvoke( word , QUIT ) ;

        VarSetBool( &v , FALSE ) ;

        /*
         *  This will crash the application
         */
        DispatchPropertyPut( word , VISIBLE , &v ) ;
    }
    catch( int where )
    {
        fprintf( stderr , "Exception caught %d\n" , where ) ;
    }

    word.Release() ;

    OleUninitialize() ;
}

Макрос Excel vba выглядит следующим образом:

Sub test_word()

    Dim word As Object

    Set word = CreateObject("word.application")

    word.Visible = True

    word.quit()

    '
    '   Quit the word application before the next statement
    '   and you will get Error 462: The remote server does not exist
    '
    word.Visible = False

    Set word = Nothing

End Sub

Ответы [ 2 ]

0 голосов
/ 29 января 2020

Интересно. Word 2010 все исправлено? Я запустил следующее против Word 2016 (что я установил). Он также использует Visual Studio 2017, но должен компилироваться на любом Visual C ++ за последние 10 лет:

#include <comdef.h>
#include <atlbase.h>

class COleInitialize
{
    public:
    COleInitialize()
    {
        OleInitialize(NULL);
    }
    ~COleInitialize()
    {
        OleUninitialize();
    }
};

int main(int argc, char* argv[])
{
    COleInitialize _oleinit;

    IUnknownPtr lpUnk;

    lpUnk.CreateInstance(L"Word.Application");

    CComDispatchDriver disp(lpUnk);
    disp.PutPropertyByName(L"Visible", &_variant_t(VARIANT_TRUE));

    MessageBox(NULL, "Close Word", "Prompt", MB_OK);

    return 0;
}
0 голосов
/ 29 января 2020

Это скорее комментарий, но комментарии ограничены ... он может работать как решение ...

Ну, в общем, вы не будете делать то, что делаете. Вы должны установить точку останова до того, как второе свойство будет установлено, и в этот момент Word закроет вручную. Обычно вы регистрируете события из Word, а затем, когда Word закрывается, вы получаете уведомление, а затем узнаете, что указатель интерфейса больше не используется. Для защиты от перехвата событий я мог бы предложить сначала создать приложение, чтобы сначала получить указатель IUnknown. Затем, когда вы захотите позвонить в IDispatch, запросите IDispatch, а затем сделайте вызов, а затем отпустите IDispatch.

При первом создании с использованием IUnknown будет создан обработчик, для которого IUnknown вы. При первом вызове запроса для IDispatch в этот момент фактически запустится Word. При последующих вызовах в QI для IDispatch и последующем вызове обработчик может быть достаточно умным, чтобы просто изящно завершиться с ошибкой, если Word был закрыт - или нет. Но я бы начал там ... если вы не хотите перехватывать события.

Реальный правильный способ получения событий из Word и поиска событий Closing или Closed.

Ух ты ... только что посмотрел на события Word. Похоже, что есть событие Quit, но нет события Closing или BeforeClosing или чего-либо подобного. Итак, вы хотите поймать событие Quit. После этого установите флаг или отпустите интерфейс IDispatch и никогда не используйте его снова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...