EDITED II код находится под WTFPL лицензии версии 2.
EDITED: Добавить параметр PID для обеспечения фильтрации, когда несколько процессов Excel в настоящее время работает, согласно комментарию предложения от @EricBrown.
Мне удалось получить рабочий IDispatch*
объект Excel «Приложение» без использования ROT. Хитрость заключается в использовании MSAA. Мой код работает как автономное консольное приложение, но я думаю, что если код выполняется в процессе Excel, через DLL Injection, он МОЖЕТ работать нормально. Возможно, вам придется быть в отдельной теме. Дайте мне знать, если вы хотите, чтобы я нажал expriment на уровень инъекции DLL.
Протестировано OK на Window7 64b, с сборками UNICODE (32 бит и 64 бит). Версия Excel 2010 64 бит (версия «14»)
Я получаю IDispatch с помощью свойства «application» из объекта «Worksheet». Следствие: должен быть открыт рабочий лист. Чтобы найти хорошее окно MSSA, мне нужно имя класса окна Excel Excel верхнего уровня. В Excel 2010 это «XLMAIN». Имя класса для рабочих листов - «EXCEL7», и это кажется «стандартным».
Я не смог напрямую получить рабочий IDispatch*
из главного окна Excel, но не очень старался. Это может включать в себя #import с DLL автоматизации из Excel, чтобы QueryInterface на IDispatch, что MSAA дает для главного окна (что IDispatch НЕ для объекта Application)
#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;
}
Вы [видели эту статью MSKB] (http://support.microsoft.com/kb/190985)? –
Это похоже на таблицу Running Object. –
Действительно, это так: так что я не буду использовать, я боюсь. – Bathsheba