Получение Excel.Application IDispatch* в dll, загруженной в Excel.

StackOverflow https://stackoverflow.com//questions/20002811

  •  20-12-2019
  •  | 
  •  

Вопрос

Кто-нибудь знает, как заполучить Excel.Application IDispatch* указатель, связанный с процессом Excel, в котором dll был загружен?

Ключевым моментом здесь является то, что этот процесс excel.exe, и указатель, который мне нужен должен принадлежат этому процессу.Использование таблицы выполняемых объектов не будет работать, поскольку Excel регистрирует с ней только свой первый экземпляр.

Я надеюсь, что это какой-то низкоуровневый трюк с COM, но я не эксперт в этой области.

Это было полезно?

Решение

РЕДАКТИРОВАНО II Код находится под ВТФПЛ лицензия версии 2.

ОТРЕДАКТИРОВАНО:Добавьте параметр PID, чтобы разрешить фильтрацию, когда в данный момент запущено несколько процессов Excel, согласно предложению комментария от @EricBrown.

мне удалось получить рабочий IDispatch* в объект Excel «Приложение» без использования ROT.Хитрость заключается в использовании MSAA.Мой код работает как отдельное консольное приложение, но я думаю, что если код выполняется в процессе Excel через внедрение DLL, он МОЖЕТ работать нормально.Возможно, вам придется быть в отдельной теме.Дайте мне знать, если вы хотите, чтобы я перевел эксперимент на уровень внедрения DLL.

Протестировано нормально на Window7 64b со сборками UNICODE (32 бита и 64 бита).Excel версия 2010 64 бита (версия «14»)

Я получаю IDispatch через свойство «application» из объекта «Worksheet».Последствие:должен быть открытый рабочий лист.Чтобы найти подходящее окно MSSA, мне нужно имя класса окна фрейма Excel верхнего уровня.В Excel 2010 это «XLMAIN».Имя класса для рабочих листов — «EXCEL7», и это кажется «стандартом».

Мне не удалось напрямую получить работающую IDispatch* из главного окна Excel, но особо не старался.Это может включать #import с DLL автоматизации из Excel, чтобы QueryInterface IDispatch, который MSAA предоставляет для главного окна (этот IDispatch НЕ предназначен для объекта приложения).

#include <atlbase.h>

#pragma comment( lib, "Oleacc.lib" )

HRESULT GetExcelAppDispatch( CComPtr<IDispatch> & spIDispatchExcelApp, DWORD dwExcelPID ) {

   struct ew {
      struct ep {
         _TCHAR* pszClassName;
         DWORD dwPID;
         HWND hWnd;
      };
      static BOOL CALLBACK ewp( HWND hWnd, LPARAM lParam ) {
         TCHAR szClassName[ 64 ];
         if ( GetClassName( hWnd, szClassName, 64 ) ) {
            ep* pep = reinterpret_cast<ep*>( lParam );
            if ( _tcscmp( szClassName, pep->pszClassName ) == 0 ) {
               if ( pep->dwPID == 0 ) {
                  pep->hWnd = hWnd;
                  return FALSE;
               } else {
                  DWORD dwPID;
                  if ( GetWindowThreadProcessId( hWnd, &dwPID ) ) {
                     if ( dwPID == pep->dwPID ) {
                        pep->hWnd = hWnd;
                        return FALSE;
                     }
                  }
               }
            }
         }
         return TRUE;
      }
   };

   ew::ep ep;

   ep.pszClassName = _TEXT( "XLMAIN" );
   ep.dwPID = dwExcelPID;
   ep.hWnd = NULL;
   EnumWindows( ew::ewp, reinterpret_cast<LPARAM>( &ep ) );
   HWND hWndExcel = ep.hWnd;
   if ( ep.hWnd == NULL ) {
      printf( "Can't Find Main Excel Window with EnumWindows\n" );
      return -1;
   }

   ep.pszClassName = _TEXT( "EXCEL7" );
   ep.dwPID = 0;
   ep.hWnd = NULL;
   EnumChildWindows( hWndExcel, ew::ewp, reinterpret_cast<LPARAM>( &ep ) );
   HWND hWndWorkSheet = ep.hWnd;
   if ( hWndWorkSheet == NULL ) {
      printf( "Can't Find a WorkSheet with EnumChildWindows\n" );
      return -1;
   }

   CComPtr<IDispatch> spIDispatchWorkSheet;
   HRESULT hr = AccessibleObjectFromWindow( hWndWorkSheet, OBJID_NATIVEOM, IID_IDispatch,
                                            reinterpret_cast<void**>( &spIDispatchWorkSheet ) );
   if ( FAILED( hr ) || ( spIDispatchWorkSheet == 0 ) ) {
      printf( "AccessibleObjectFromWindow Failed\n" );
      return hr;
   }
   CComVariant vExcelApp;
   hr = spIDispatchWorkSheet.GetPropertyByName( CComBSTR( "Application" ), &vExcelApp );
   if ( SUCCEEDED( hr ) && ( vExcelApp.vt == VT_DISPATCH ) ) {
      spIDispatchExcelApp = vExcelApp.pdispVal;
      return S_OK;
   }
   return hr;

}
int _tmain(int argc, _TCHAR* argv[])
{

   DWORD dwExcelPID = 0;
   if ( argc > 1 ) dwExcelPID = _ttol( argv[ 1 ] );

   HRESULT hr = CoInitialize( NULL );
   bool bCoUnInitializeTodo = false;
   if ( SUCCEEDED( hr ) ) {
      bCoUnInitializeTodo = true;
      CComPtr<IDispatch> spDispatchExcelApp;
      hr = GetExcelAppDispatch( spDispatchExcelApp, dwExcelPID );
      if ( SUCCEEDED( hr ) && spDispatchExcelApp ) {
         CComVariant vExcelVer;
         hr = spDispatchExcelApp.GetPropertyByName( CComBSTR( "Version" ), &vExcelVer );
         if ( SUCCEEDED( hr ) && ( vExcelVer.vt == VT_BSTR ) ) {
            wprintf( L"Excel Version is %s\n", vExcelVer.bstrVal );
         }
      }
   }
   if ( bCoUnInitializeTodo ) CoUninitialize();
   return 0;
}

Другие советы

Вы должны быть в состоянии узнать, как это сделать, просмотрев код в exceldna .Этот проект содержит код, который подключается к Excel из библиотеки расширения.Кодекс, вероятно, будет более сложным, что вам нужно, но реализуйте ссылку, необходимую вам.

Вот как я это делаю: (подтвердить @manuell).dispatch_wrapper - это класс, вот конструктор для установки m_disp_application:

dispatch_wrapper(void)
{
    DWORD target_process_id = ::GetProcessId(::GetCurrentProcess());

    if (getProcessName() == "excel.exe"){
        HWND hwnd = ::FindWindowEx(0, 0, "XLMAIN", NULL);
        while (hwnd){
            DWORD process_id;
            ::GetWindowThreadProcessId(hwnd, &process_id);
            if (process_id == target_process_id){
                HWND hwnd_desk = ::FindWindowEx(hwnd, 0, "XLDESK", NULL);
                HWND hwnd_7 = ::FindWindowEx(hwnd_desk, 0, "EXCEL7", NULL);
                IDispatch* p = nullptr;
                if (SUCCEEDED(::AccessibleObjectFromWindow(hwnd_7, OBJID_NATIVEOM, IID_IDispatch, (void**)&p))){
                    LPOLESTR name[1] = {L"Application"};
                    DISPID dispid;
                    if (SUCCEEDED(p->GetIDsOfNames(IID_NULL, name, 1U, LOCALE_SYSTEM_DEFAULT, &dispid))){
                        CComVariant v;
                        DISPPARAMS dp;
                        ::memset(&dp, NULL, sizeof(DISPPARAMS));
                        EXCEPINFO ei;
                        ::memset(&ei, NULL, sizeof(EXCEPINFO));
                        if (SUCCEEDED(p->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dp, &v, &ei, NULL))){
                            if (v.vt == VT_DISPATCH){
                                m_disp_application = v.pdispVal;
                                m_disp_application->AddRef();
                                return;
                            }
                        }
                    }
                }
            }
            hwnd = ::FindWindowEx(0, hwnd, "XLMAIN", NULL);
        }
    }
    m_disp_application = nullptr;
}
.

getProcessName() возвращает нижний регистр.

Поскольку офисные приложения регистрируют свои документы в гниле, вы можете прикрепить к экземплярам рядом с первым (который уже находится в гниле) через Получение IDSPatch для документов в ROTT , затем вы можете использовать документ. Аппликация.hwnd (это VBA, вам нужно Перевести на IDispatch :: GetIDSOFNAMES и IDISPATCH :: Отзываются с Dispatch_Propertyget ), чтобы получить оконные ручки всех Excel экземпляры.

Теперь у вас есть сопоставление между IDispatch и Windows ручек всех экземпляров Excel, пришло время найти свой собственный экземпляр Excel. Вы можете назвать GetWindowTreadProcessID в оконных ручках, чтобы получить идентификаторы процесса, затем сравниваются с вашим собственным идентификатором процесса, возвращаемыми GetCurrentProcessID, чтобы увидеть, какое окно Excel принадлежит вашему текущему процессу, и посмотрите вверх в HWND для отображения iDispatch, чтобы найти текущее приложение Excel IDispatch интерфейс.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top