DSOFramer关闭另一个窗口中的Excel文档。如果文件中有未保存的数据,则dsoframer无法打开,并显示“尝试访问无效地址”的错误信息。

4
我正在使用Microsoft的DSOFramer控件,以便在我的对话框中嵌入Excel文件,以便用户可以选择工作表,然后选择单元格范围;它与我的对话框上的导入按钮一起使用。
问题在于,当我调用DSOFramer的OPEN函数时,如果我已经在另一个窗口中打开了Excel,则会关闭Excel文档(但保留Excel运行)。如果要关闭的文档有未保存的数据,则会弹出一个对话框来关闭另一个窗口中的Excel文档。如果文件中有未保存的数据,则dsoframer无法打开并显示消息框:“尝试访问无效地址”。
我构建了源代码,并进行了步骤处理,在其CDsoDocObject :: CreateFromFile函数中进行了调用,调用类IMoniker的BindToObject。 HR为0x8001010a,“消息过滤器指示应用程序正忙”。在失败时,它尝试通过CLSID Microsoft Excel Worksheet的classid来InstantiateDocObjectServer…这将以HRESULT 0x80040154“未注册的类”失败。 InstantiateDocObjectServer只是在classid上调用CoCreateInstance,首先使用CLSCTX_LOCAL_SERVER,然后(如果失败)使用CLSCTX_INPROC_SERVER。
我知道DSOFramer是一个流行的示例项目,用于在各种对话框和表单中嵌入Office应用程序。我希望其他人也遇到了这个问题,并可能对如何解决它有一些见解。我真的不希望它关闭任何其他打开的Excel文档,而且如果由于未保存的数据而无法关闭文档,我真的不希望它出现错误。
更新1:我已尝试更改传递给Excel.Application的classid(我知道该类将解析),但是没有成功。在CDsoDocObject中,它尝试打开键HKEY_CLASSES_ROOT \ CLSID \ {00024500-0000-0000-C000-000000000046} \ DocObject,但失败了。我已经视觉确认该键不在我的注册表中;该键存在于指南中,但没有DocObject子键。然后它会产生一个错误消息框:“关联的COM服务器不支持ActiveX文档嵌入”。当我尝试使用Excel.Workbook programid时,我会得到类似的结果(当然是不同的键)。
更新2:我尝试启动第二个Excel实例,希望我的自动化可以绑定到它(成为最近调用的实例),而不是问题Excel实例,但似乎没有这样做。结果是一样的。我的问题似乎归结为:我正在调用IMoniker类的BindToObject,并收到0x8001010A(RPC_E_SERVERCALL_RETRYLATER)消息过滤器指示应用程序正忙。我尝试使用通过SetBindOptions传递给BindToObject的标志进行操作,但似乎没有任何区别。
更新3:它首先尝试使用IMoniker类进行绑定。如果失败,则调用CoCreateInstance作为fallback方法来获取clsid。这可能适用于其他MS Office对象,但对于Excel而言,该类是工作表。我修改了示例以CoCreateInstance _Application,然后获取了工作簿,接着调用目标文件的Workbooks::Open,返回一个工作表对象。然后我将该指针合并回原始示例代码路径。现在一切正常。
3个回答

0
假设您正在使用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;
}

0

@Jinjin

  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. 是的,打开旧版本。保证这种情况的最佳方法是找到一个OLB文件(如上述第1项中提到的),该文件来自您希望支持的最早版本的Excel的安装。我使用来自Office 2000的Excel9.olb。与我测试的Excel版本一直到Office 2007的最新版本都可以正常工作。

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

  4. 恐怕由于我的雇主的限制,我可能无法做到这一点。但是,如果您采用“标准”的dsoframer项目,进行本帖第1部分中描述的更改以及我在早期帖子中描述的更改,则几乎完全重新创建了我所拥有的内容。


0
@Jinjin:你是否在使用Excel::_Application的cpp文件中放置了导入语句(#import "XL5EN32.olb")?如果没有,请这样做...不能只是将其添加到项目中。如果你已经这样做了,请尝试在使用这些映射的cpp文件中添加此语句 #import "Debug\XL5EN32.tlh"。tlh文件是通过运行#import生成的头文件;你应该在Debug目录中找到它(假设你正在执行Debug构建)。
将_Application重命名为Application(以及其他名称)不是正确的方法。_Application结构体是具有映射的结构体。这就是为什么你找不到app->get_Workbooks的原因。
你在查找Application而不是_Application的哪个文件?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接