COM 自动化 Excel 2010 冻结

Posted

技术标签:

【中文标题】COM 自动化 Excel 2010 冻结【英文标题】:COM Automation Excel 2010 freezes 【发布时间】:2013-01-02 14:03:08 【问题描述】:

我正在以编程方式启动 Excel 应用程序。当我不使用事件侦听器(事件接收器)时,一切正常。我的问题是,当我通过连接点注册接收器时,Excel UI 冻结。更准确地说,当我在工作表上再次单击时,Excel 会冻结。如果没有注册的事件接收器,则不会发生此问题。这是接收器的代码。要将 Sink 注册到连接点,请调用 AttachToSource() 方法。我的代码是用 C++ 编写的,不使用 ATL 或 MFC。任何帮助表示赞赏!

编辑: Word 2010 中出现同样的问题。那里显示了等待圆圈。我从“Ocidl.h”获得 COM 定义。

#include "AppEventListener.h"

#include <iostream>

using namespace std;

//Constructor.
CAppEventListener::CAppEventListener() :
m_pConnectionPoint(NULL),
m_dwConnection(0)

   m_refCount = 0;


//Destructor.
CAppEventListener::~CAppEventListener()


/******************************************************************************
*   IUnknown Interfaces -- All COM objects must implement, either 
*  directly or indirectly, the IUnknown interface.
******************************************************************************/ 

/******************************************************************************
*  QueryInterface -- Determines if this component supports the 
*  requested interface, places a pointer to that interface in ppvObj if it is 
*  available, and returns S_OK.  If not, sets ppvObj to NULL and returns 
*  E_NOINTERFACE.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::QueryInterface(REFIID riid, void ** ppvObj)

   if (riid == IID_IUnknown)
      *ppvObj = static_cast<IUnknown*>(this);
   

   else if (riid == IID_IDispatch)
      *ppvObj = static_cast<IDispatch*>(this);
   

   else if (riid == IID_ApplicationEvents)
      *ppvObj = static_cast<IDispatch*>(this);
   

   else
      *ppvObj = NULL;
      return E_NOINTERFACE;
   

   static_cast<IUnknown*>(*ppvObj)->AddRef();
   return S_OK;


/******************************************************************************
*  AddRef() -- In order to allow an object to delete itself when 
*  it is no longer needed, it is necessary to maintain a count of all 
*  references to this object. When a new reference is created, this function 
*  increments the count.
******************************************************************************/ 
STDMETHODIMP_(ULONG) CAppEventListener::AddRef()

   return ++m_refCount;


/******************************************************************************
*  Release() -- When a reference to this object is removed, this 
*  function decrements the reference count. If the reference count is 0, then 
*  this function deletes this object and returns 0.
******************************************************************************/ 
STDMETHODIMP_(ULONG) CAppEventListener::Release()

   m_refCount--;

   if (m_refCount == 0)
   
      delete this;
      return 0;
   
   return m_refCount;


/******************************************************************************
*   IDispatch Interface -- This interface allows this class to be used as an
*   automation server, allowing its functions to be called by other COM
*   objects.
******************************************************************************/ 

/******************************************************************************
*   GetTypeInfoCount -- This function determines if the class supports type 
*   information interfaces or not. It places 1 in iTInfo if the class supports
*   type information and 0 if it does not.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::GetTypeInfoCount(UINT *iTInfo)

   *iTInfo = 0;
   return S_OK;


/******************************************************************************
*   GetTypeInfo -- Returns the type information for the class. For classes 
*   that do not support type information, this function returns E_NOTIMPL;
******************************************************************************/ 
STDMETHODIMP CAppEventListener::GetTypeInfo(UINT iTInfo, LCID lcid, 
                                       ITypeInfo **ppTInfo)

   return E_NOTIMPL;


/******************************************************************************
*   GetIDsOfNames -- Takes an array of strings and returns an array of DISPIDs
*   that correspond to the methods or properties indicated. If the name is not 
*   recognized, returns DISP_E_UNKNOWNNAME.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::GetIDsOfNames(REFIID riid,  
                                         OLECHAR **rgszNames, 
                                         UINT cNames,  LCID lcid,
                                         DISPID *rgDispId)

   return E_NOTIMPL;


/******************************************************************************
*   Invoke -- Takes a dispid and uses it to call another of this class's 
*   methods. Returns S_OK if the call was successful.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
                                  WORD wFlags, DISPPARAMS* pDispParams,
                                  VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
                                  UINT* puArgErr)

switch(dispIdMember)
   case 0x00622:
      if(pDispParams->cArgs !=2)
         return E_INVALIDARG;
      else
      
         if(pDispParams->rgvarg[1].vt & VT_BYREF)
         
            HandleBeforeWorkbookClose( // Call the function.
               *(pDispParams->rgvarg[1].ppdispVal),
               pDispParams->rgvarg[0].pboolVal);
         
         else
         
            HandleBeforeWorkbookClose(  // Call the function.
               (pDispParams->rgvarg[1].pdispVal),
               pDispParams->rgvarg[0].pboolVal);
         
      

   case 0x0061c:
      
         if(pDispParams->rgvarg[1].vt & VT_BYREF)
         
            HandleSheetChange( // Call the function.
               *(pDispParams->rgvarg[1].ppdispVal),
               *(pDispParams->rgvarg[0].ppdispVal));
         
         else
         
            HandleSheetChange(  // Call the function.
               pDispParams->rgvarg[1].pdispVal,
               pDispParams->rgvarg[0].pdispVal);
         
      
      break;
   
   return S_OK;


/******************************************************************************
*  HandleBeforeWorkbookClose -- This method processes the BeforeWorkbookClose
*  event for the application attached to this event handler.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::HandleBeforeWorkbookClose( IDispatch* xlBook, 
                                                  VARIANT_BOOL* fCancel )

  cout << "HandleBeforeWorkbookClose\n" << endl;
   HRESULT hr = S_OK;
   return hr;


/******************************************************************************
*  HandleSheetChange -- This method processes the SheetChange event for the 
*  application attached to this event handler.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::HandleSheetChange( IDispatch* xlSheet, 
                                                  IDispatch* xlRange)

   cout << "HandleSheetChange\n" << endl;
   HRESULT hr = S_OK;
   return hr;


/******************************************************************************
*  AttachToSource -- This method attaches to an event source.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::AttachToSource( IUnknown* pEventSource )

   HRESULT hr = S_OK;

   IConnectionPointContainer* pCPC = NULL;
   hr = pEventSource->QueryInterface( IID_IConnectionPointContainer, 
      (void**)&pCPC );
   if (SUCCEEDED(hr))

      hr = pCPC->FindConnectionPoint( IID_ApplicationEvents, 
         &m_pConnectionPoint );
      if (SUCCEEDED(hr))

         hr = m_pConnectionPoint->Advise( this, &m_dwConnection );
      
      pCPC->Release();
   

   return hr;


/******************************************************************************
*  DetachFromSource -- This method detaches from an event source.
******************************************************************************/ 
STDMETHODIMP CAppEventListener::DetachFromSource()

   HRESULT hr = S_OK;
   if (m_pConnectionPoint != NULL)
      m_pConnectionPoint->Unadvise( m_dwConnection );
      m_pConnectionPoint = NULL;
   
   return hr;

【问题讨论】:

【参考方案1】:

使用 CoInitializeEx(NULL, COINIT_MULTITHREADED) 并添加 _WIN32_DCOM 作为预处理器指令解决了问题。

【讨论】:

以上是关于COM 自动化 Excel 2010 冻结的主要内容,如果未能解决你的问题,请参考以下文章

表格里的第一行怎么固定

怎么样为Excel2010表格设置自动换行

python处理Excel实现自动化办公教学(含实战)

python处理Excel实现自动化办公教学(含实战)

Python自动化办公--Pandas玩转Excel数据分析

Excel里冻结窗口是否只能冻结首行或首列?