الحصول على Excel.Application IDispatch* داخل ملف dll الذي تم تحميله في Excel

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

  •  20-12-2019
  •  | 
  •  

سؤال

هل يعرف أحد كيفية الحصول على Excel.Application IDispatch* المؤشر المرتبط بعملية Excel التي فيها dll وقد تم تحميلها؟

الشيء الرئيسي هنا هو أن هذه العملية excel.exe, والمؤشر الذي أحتاجه يجب تنتمي إلى تلك العملية.لن يتم استخدام جدول الكائنات الجاري تشغيله نظرًا لأن Excel يسجل المثيل الأول له فقط مع ذلك.

آمل أن يكون هناك بعض الخداع COM منخفض المستوى، لكنني لست خبيرًا في هذا المجال.

هل كانت مفيدة؟

المحلول

تم تحريره II الكود موجود تحت WTFPL نسخة الترخيص 2.

تم التعديل:أضف معلمة PID للسماح بالتصفية عند تشغيل العديد من عمليات Excel حاليًا، وفقًا لاقتراح التعليق من @EricBrown.

تمكنت من الحصول على عمل IDispatch* إلى كائن "تطبيق" Excel دون استخدام ROT.الحيلة هي استخدام MSAA.يعمل الكود الخاص بي كتطبيق وحدة تحكم مستقل، ولكن أعتقد أنه إذا تم تنفيذ الكود في عملية Excel، عبر DLL حقن، فقد يعمل بشكل جيد.ربما عليك أن تكون في موضوع مخصص.اسمحوا لي أن أعرف إذا كنت تريد مني أن أدفع التجربة إلى مستوى حقن DLL.

تم اختباره بشكل جيد على Window7 64b، مع إصدار UNICODE (32 بت و64 بت).إصدار Excel 2010 64 بت (الإصدار "14")

أحصل على IDispatch عبر خاصية "التطبيق" من كائن "ورقة العمل".عاقبة:يجب أن تكون هناك ورقة عمل مفتوحة.من أجل العثور على نافذة 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 من مكتبة الملحق.من المحتمل أن يكون الرمز أكثر توضيحا تحتاجه، لكنه سينفذ المرجع الذي تحتاجه.

هذه هي الطريقة التي أفعل ذلك:(أعترف @ مانويل). 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() إرجاع الأحرف الصغيرة.

لأن تطبيقات Office سجلت مستنداتها في التعفن، يمكنك إرفاق مثيلات بجانب أول واحد (الموجود بالفعل في التعفن) بواسطة الحصول على IDispatch للمستندات في تعفن ، ثم يمكنك استخدام المستند.Application.hwnd (هذا هو VBA، تحتاج إلى

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top