DSOFramer закрывает Excel документ в другом окне. Если несохраненные данные в файле, dsoframer не открывается с "Попытка получить доступ к неверному адресу"
Я использую Microsoft's DSOFramer
контроль, позволяющий мне встраивать файл Excel в мой диалог, чтобы пользователь мог выбрать свой лист, а затем выбрать диапазон ячеек; это используется с кнопкой импорта в моем диалоге.
Проблема в том, что когда я звоню DSOFramer's OPEN
Функция, если у меня Excel открыт в другом окне, он закрывает документ Excel (но оставляет его в рабочем состоянии). Если документ, который он пытается закрыть, содержит несохраненные данные, я получаю диалоговое окно, закрывающее документ Excel в другом окне. Если несохраненные данные в файле, dsoframer
не удается открыть с окном сообщения: Attempt to access invalid address
,
Я построил источник, и прошел, и он делает вызов в его CDsoDocObject::CreateFromFile
функция, вызывающая BindToObject
на объекте класса IMoniker. HR
является 0x8001010a
The message filter indicated that the application is busy
, На этот провал, он пытается InstantiateDocObjectServer
от classid
из CLSID
Microsoft Excel Worksheet... это не удается с HRESULT
из 0x80040154
Class not registered
, InstantiateDocObjectServer
просто звонки CoCreateInstance
на classid
сначала с CLSCTX_LOCAL_SERVER
затем (если это не удается) с CLSCTX_INPROC_SERVER
,
я знаю DSOFramer
это популярный пример проекта для встраивания приложений Office в различные диалоги и формы. Я надеюсь, что у кого-то еще была эта проблема и, возможно, есть некоторое представление о том, как я могу решить эту проблему. Я действительно не хочу, чтобы он закрывал любые другие открытые документы Excel, и я действительно не хочу, чтобы он выводил ошибку, если он не может закрыть документ из-за несохраненных данных.
Обновление 1: я пытался изменить classid
это передано в Excel.Application
(Я знаю, что класс разрешится), но это не сработало. В CDsoDocObject
Пытается открыть ключ HKEY_CLASSES_ROOT\CLSID\{00024500-0000-0000-C000-000000000046}\DocObject
, но не удается. Я визуально подтвердил, что ключ отсутствует в моем реестре; Ключ присутствует для руководства, но нет DocObject
подключ. Затем он выдает сообщение об ошибке: The associated COM server does not support ActiveX document embedding
, Я получаю похожие (разные ключи, конечно) результаты, когда пытаюсь использовать Excel.Workbook programid
,
Обновление 2: я попытался запустить второй экземпляр Excel, надеясь, что моя автоматизация свяжется с ним (будучи вызванным последним) вместо проблемного экземпляра Excel, но, похоже, он этого не сделал. Результаты были одинаковыми. Моя проблема, кажется, сводилась к этому: я звоню BindToObject
на объекте класса IMoniker
и получение 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)
The message filter indicated that the application is busy
, Я пытался играть с флагами, переданными BindToObject
(через SetBindOptions
), но, кажется, ничего не имеет значения.
Обновление 3: Сначала он пытается выполнить привязку с помощью класса IMoniker. Если это не удается, он вызывает CoCreateInstance
для clsid
как fallback
метод. Это может работать для других объектов MS Office, но когда это Excel, класс предназначен для рабочего листа. Я изменил образец на CoCreateInstance _Application
, затем получил рабочие тетради, затем назвал Workbooks::Open
для целевого файла, который возвращает объект рабочего листа. Затем я вернул этот указатель и слил обратно с исходным образцом пути кода. Все работает сейчас.
3 ответа
Предполагая, что вы используете проект DSOFRAMER, вам нужно добавить этот код в dsofdocobj.cpp в функции CreateFromFile, примерно в строке 348:
CLSID clsidExcelWS;
hr = CLSIDFromProgID(OLESTR("Excel.Sheet"),clsidExcelWS);
if (FAILED(hr)) return hr;
if (clsid == clsidExcelWS)
{
hr = InstantiateAndLoadExcel(pwszFile, &pole);
if (FAILED(hr)) return hr;
}
else
{
<the IMoniker::BindToObject call and it's failure handling from the "stock" sample goes here>
}
Затем определите следующую новую функцию-член в CDsoDocObject:
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::InstantiateAndLoadExcel (protected)
//
// Create an instance of Excel and load the target file into its worksheet
//
STDMETHODIMP CDsoDocObject::InstantiateAndLoadExcel(LPWSTR pwszFile, IOleObject **ppole)
{
IUnknown *punkApp=NULL;
Excel::_Application *app=NULL;
Excel::Workbooks *wbList=NULL;
Excel::_Workbook *wb;
CLSID clsidExcel;
HRESULT hr = CLSIDFromProgID(OLESTR("Excel.Application"), &clsidExcel);
if (FAILED(hr))
return hr;
hr = CoCreateInstance(clsidExcel, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&punkApp);
if (SUCCEEDED(hr))
{
hr = punkApp->QueryInterface(__uuidof(Excel::_Application),(LPVOID *)&app);
if (SUCCEEDED(hr))
{
hr = app->get_Workbooks(&wbList);
VARIANT vNoParam;
VariantInit(&vNoParam);
V_VT(&vNoParam) = VT_ERROR;
V_ERROR(&vNoParam) = DISP_E_PARAMNOTFOUND;
VARIANT vReadOnly;
VariantInit(&vReadOnly);
V_VT(&vReadOnly) = VT_BOOL;
V_BOOL(&vReadOnly) = VARIANT_TRUE;
BSTR bstrFilename = SysAllocString(pwszFile);
hr = wbList->Open(bstrFilename, vNoParam,vNoParam,vNoParam,vNoParam,vReadOnly,vNoParam,vNoParam,vNoParam,vNoParam,vNoParam,vNoParam,vNoParam,0,&wb);
if (SUCCEEDED(hr))
hr = wb->QueryInterface(IID_IOleObject, (void**)ppole);
VariantClear(&vReadOnly);
VariantClear(&vNoParam);
SysFreeString(bstrFilename);
}
}
if (wb != NULL) wb->Release();
if (wbList != NULL) wbList->Release();
if (app != NULL) app->Release();
if (punkApp != NULL) punkApp->Release();
return hr;
}
@Jinjin: вы поместили оператор импорта (#import "XL5EN32.olb") в файл cpp, где вы используете приложение Excel::_? Если нет, сделайте это... не можете просто добавить его в проект. Если вы уже сделали это, попробуйте также добавить этот оператор в файл cpp, где вы используете эти сопоставления #import "Debug\XL5EN32.tlh". Файл tlh - это заголовок, который генерируется запуском #import; вы должны найти его в своем каталоге Debug (при условии, что вы выполняете сборку Debug).
Переименование _Application в приложение (и другие) не правильный путь. Структура _Application - это та, которая имеет сопоставления. Вот почему вы не можете найти приложение->get_Workbooks.
Какой файл вы ищете, что вы находите приложение, но не _Application?
@Jinjin
Вы можете использовать директиву #import для импорта файла Excel вашего OLB. это должно сгенерировать (и автоматически включить файл Excel .tlh, который содержит структуры для _Application (и все остальное, что вам нужно)). В идеале вы должны найти файл OLB, который соответствует самой ранней версии Excel, которую вы хотите поддерживать. Вероятно, файл в вашей локальной системе находится в каталоге c:\Program Files\Microsoft Office\Office12 (предполагается, что у вас установлен Office 2007). Он может называться Excel.olb или XL5EN32.OLB (иначе, очевидно, если вы не установили версию Excel на английском языке для США).
Итак, скопируйте файл.olb в исходный каталог вашего проекта, затем в верхней части исходного файла добавьте строку для #import "XL5EN32.olb".Да, открывает старые версии. Лучший способ гарантировать, что это будет так, - это найти файл OLB (упомянутый в пункте 1 выше), который находится в установке Excel, самой ранней версии, которую вы хотите поддерживать. Я использую Excel9.olb из Office 2000. Прекрасно работает с моим тестированием версий Excel вплоть до последних версий Office 2007.
Да, вы должны использовать dsoframer обычно после внесения этих изменений.
Боюсь, что, вероятно, я не смогу сделать это из-за ограничений моего работодателя. Однако, если вы возьмете "стандартный" проект dsoframer, внесете изменения, описанные в части 1 этого поста, и изменения, которые я описал в моем предыдущем посте, вы в значительной степени воссоздали именно то, что у меня есть.