4

我正在使用Microsoft's DSOFramer控件允许我在对话框中嵌入 Excel 文件,以便用户可以选择他的工作表,然后选择他的单元格范围;它与我对话框上的导入按钮一起使用。

问题是当我调用该DSOFramer's OPEN函数时,如果我在另一个窗口中打开 Excel,它会关闭 Excel 文档(但让 Excel 继续运行)。如果它试图关闭的文档有未保存的数据,我会在另一个窗口中看到一个关闭 Excel 文档的对话框。如果文件中未保存数据,dsoframer则无法打开并显示消息框:Attempt to access invalid address.

我构建了源代码,并逐步完成,并在其CDsoDocObject::CreateFromFile函数中进行了调用,调用BindToObject了 IMoniker 类的对象。HR0x8001010a The message filter indicated that the application is busy。_ 失败时,它会尝试InstantiateDocObjectServer通过Microsoft Excel 工作表...失败并显示. 只需调用,首先使用,然后(如果失败)使用.classidCLSIDHRESULT0x80040154 Class not registeredInstantiateDocObjectServerCoCreateInstanceclassidCLSCTX_LOCAL_SERVERCLSCTX_INPROC_SERVER

我知道DSOFramer是一个流行的示例项目,用于将 Office 应用程序嵌入到各种对话框和表单中。我希望其他人遇到过这个问题,并且可能对我如何解决这个问题有一些见解。我真的不希望它关闭任何其他打开的 Excel 文档,如果由于未保存的数据而无法关闭文档,我真的不希望它出错。

更新 1:我尝试更改classid传入的内容Excel.Application(我知道该类会解析),但这没有用。在CDsoDocObject中,它尝试打开 key 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 实例,但它似乎没有这样做。结果是一样的。我的问题似乎归结为:我正在调用BindToObjectclass 的对象IMoniker,并接收0x8001010A (RPC_E_SERVERCALL_RETRYLATER) The message filter indicated that the application is busy. 我尝试过使用传递给BindToObject(通过SetBindOptions)的标志,但似乎没有任何区别。

更新 3:它首先尝试使用 IMoniker 类进行绑定。如果失败,它会调用asCoCreateInstance方法。这可能适用于其他 MS Office 对象,但当它是 Excel 时,该类适用于工作表。我将示例修改为,然后获取工作簿,然后为目标文件调用 ,该文件返回一个 Worksheet 对象。然后我返回该指针并与原始示例代码路径合并。现在都在工作。clsidfallbackCoCreateInstance _ApplicationWorkbooks::Open

4

3 回答 3

0

假设您使用的是 DSOFRAMER 项目,您需要将此代码添加到 CreateFromFile 函数中的 dsofdocobj.cpp 中,大约在第 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;
}
于 2008-11-14T21:22:28.283 回答
0

@Jinjin:您是否将导入语句(#import“XL5EN32.olb”)放在使用 Excel::_Application 的 cpp 文件中?如果没有,那就这样做……不能只是将其添加到项目中。如果您已经这样做了,请尝试将此语句添加到使用这些映射的 cpp 文件中#import "Debug\XL5EN32.tlh"。tlh 文件是通过运行#import 生成的头文件;您应该在 Debug 目录中找到它(假设您正在执行 Debug 构建)。

将 _Application 重命名为 Application(和其他)不是正确的方法。_Application 结构是具有映射的结构。这就是您没有找到 app->get_Workbooks 的原因。

您正在查找哪个文件,您正在查找 Application 但不是 _Application?

于 2008-11-18T17:43:05.200 回答
0

@金进

  1. 您可以使用#import 指令来导入 Excel 的 OLB 文件。这应该生成(并自动包含一个 Excel .tlh 文件,其中包含 _Application 的结构(以及您需要的其余部分))。理想情况下,您应该找到与您希望支持的最早 Excel 版本相匹配的 OLB 文件。您本地系统上的那个可能在 c:\Program Files\Microsoft Office\Office12 中(假设您安装了 Office 2007)。它可能被命名为 Excel.olb 或 XL5EN32.OLB(不同,显然如果您没有安装 Excel 的美国英语版本。
    因此,将 .olb 文件复制到您的项目源目录,然后在源文件的顶部,为#import "XL5EN32.olb" 添加一行。

  2. 是的,打开旧版本。保证这种情况的最佳方法是从您希望支持的最早版本的 Excel 安装中找到一个 OLB 文件(在上面的第 1 项中提到)。我使用 Office 2000 中的 Excel9.olb。从 Office 2007 到最新版本的 Excel 版本都可以正常工作。

  3. 是的,您应该在进行这些更改后正常使用 dsoframer。

  4. 恐怕由于雇主的限制,我可能无法做到这一点。但是,如果您采用“库存” dsoframer 项目,进行本文第 1 部分中描述的更改,以及我在之前的文章中描述的更改,那么您几乎完全重新创建了我所拥有的。

于 2008-11-17T17:28:07.477 回答