DSOFramer закрывает Excel документ в другом окне. Если несохраненные данные в файле, dsoframer не открывается с "Попытка получить доступ к неверному адресу"

Я использую Microsoft's DSOFramer контроль, позволяющий мне встраивать файл Excel в мой диалог, чтобы пользователь мог выбрать свой лист, а затем выбрать диапазон ячеек; это используется с кнопкой импорта в моем диалоге.

Проблема в том, что когда я звоню DSOFramer's OPEN Функция, если у меня Excel открыт в другом окне, он закрывает документ Excel (но оставляет его в рабочем состоянии). Если документ, который он пытается закрыть, содержит несохраненные данные, я получаю диалоговое окно, закрывающее документ Excel в другом окне. Если несохраненные данные в файле, dsoframer не удается открыть с окном сообщения: Attempt to access invalid address,

Я построил источник, и прошел, и он делает вызов в его CDsoDocObject::CreateFromFile функция, вызывающая BindToObject на объекте класса IMoniker. HR является 0x8001010aThe message filter indicated that the application is busy, На этот провал, он пытается InstantiateDocObjectServer от classid из CLSID Microsoft Excel Worksheet... это не удается с HRESULT из 0x80040154Class 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

  1. Вы можете использовать директиву #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".

  2. Да, открывает старые версии. Лучший способ гарантировать, что это будет так, - это найти файл OLB (упомянутый в пункте 1 выше), который находится в установке Excel, самой ранней версии, которую вы хотите поддерживать. Я использую Excel9.olb из Office 2000. Прекрасно работает с моим тестированием версий Excel вплоть до последних версий Office 2007.

  3. Да, вы должны использовать dsoframer обычно после внесения этих изменений.

  4. Боюсь, что, вероятно, я не смогу сделать это из-за ограничений моего работодателя. Однако, если вы возьмете "стандартный" проект dsoframer, внесете изменения, описанные в части 1 этого поста, и изменения, которые я описал в моем предыдущем посте, вы в значительной степени воссоздали именно то, что у меня есть.

Другие вопросы по тегам