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

发布于 2024-07-08 16:18:39 字数 2187 浏览 8 评论 0原文

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

问题是,当我调用 DSOFramer 的 OPEN 函数时,如果我在另一个窗口中打开 Excel,它会关闭 Excel 文档(但使 Excel 保持运行)。 如果它尝试关闭的文档有未保存的数据,我会在另一个窗口中看到一个关闭 Excel 文档的对话框。 如果文件中未保存数据,dsoframer 将无法打开并显示消息框:尝试访问无效地址

我构建了源代码,并逐步执行,并在其 CDsoDocObject::CreateFromFile 函数中进行调用,在类 IMoniker 的对象上调用 BindToObjectHR0x8001010a 消息过滤器指示应用程序正忙。 发生该故障时,它会尝试通过 CLSID Microsoft Excel 工作表的 classidInstantiateDocObjectServer...此操作失败并显示 HRESULT > 0x80040154 类未注册InstantiateDocObjectServer 只需在 classid 上调用 CoCreateInstance,首先使用 CLSCTX_LOCAL_SERVER,然后(如果失败)使用 CLSCTX_INPROC_SERVER

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

更新 1:我尝试更改传递给 Excel.Applicationclassid (我知道该类会解析),但这不起作用。 在 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) 消息过滤器表明应用程序正忙。 我尝试使用传递给 BindToObject 的标志(通过 SetBindOptions),但似乎没有任何区别。

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

I'm using Microsoft's DSOFramer control to allow me to embed an Excel file in my dialog so the user can choose his sheet, then select his range of cells; it's used with an import button on my dialog.

The problem is that when I call the DSOFramer's OPEN function, if I have Excel open in another window, it closes the Excel document (but leaves Excel running). If the document it tries to close has unsaved data, I get a dialog boxclosing Excel doc in another window. If unsaved data in file, dsoframer fails to open with a messagebox: Attempt to access invalid address.

I built the source, and stepped through, and its making a call in its CDsoDocObject::CreateFromFile function, calling BindToObject on an object of class IMoniker. The HR is 0x8001010a The message filter indicated that the application is busy. On that failure, it tries to InstantiateDocObjectServer by classid of CLSID Microsoft Excel Worksheet... this fails with an HRESULT of 0x80040154 Class not registered. The InstantiateDocObjectServer just calls CoCreateInstance on the classid, first with CLSCTX_LOCAL_SERVER, then (if that fails) with CLSCTX_INPROC_SERVER.

I know DSOFramer is a popular sample project for embedding Office apps in various dialog and forms. I'm hoping someone else has had this problem and might have some insight on how I can solve this. I really don't want it to close any other open Excel documents, and I really don't want it to error-out if it can't close the document due to unsaved data.

Update 1: I've tried changing the classid that's passed in to Excel.Application (I know that class will resolve), but that didn't work. In CDsoDocObject, it tries to open key HKEY_CLASSES_ROOT\CLSID\{00024500-0000-0000-C000-000000000046}\DocObject, but fails. I've visually confirmed that the key is not present in my registry; The key is present for the guide, but there's no DocObject subkey. It then produces an error message box: The associated COM server does not support ActiveX document embedding. I get similar (different key, of course) results when I try to use the Excel.Workbook programid.

Update 2: I tried starting a 2nd instance of Excel, hoping that my automation would bind to it (being the most recently invoked) instead of the problem Excel instance, but it didn't seem to do that. Results were the same. My problem seems to have boiled down to this: I'm calling the BindToObject on an object of class IMoniker, and receiving 0x8001010A (RPC_E_SERVERCALL_RETRYLATER) The message filter indicated that the application is busy. I've tried playing with the flags passed to the BindToObject (via the SetBindOptions), but nothing seems to make any difference.

Update 3: It first tries to bind using an IMoniker class. If that fails, it calls CoCreateInstance for the clsid as a fallback method. This may work for other MS Office objects, but when it's Excel, the class is for the Worksheet. I modified the sample to CoCreateInstance _Application, then got the workbooks, then called the Workbooks::Open for the target file, which returns a Worksheet object. I then returned that pointer and merged back with the original sample code path. All working now.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

桜花祭 2024-07-15 16:18:39

@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. 由于雇主的限制,我恐怕无法做到这一点。 但是,如果您采用“stock”dsoframer 项目,进行本文第 1 部分中描述的更改以及我在之前的文章中描述的更改,那么您几乎完全重新创建了我所拥有的内容。

@Jinjin

  1. You can use the #import directive to import your Excel's OLB file. this should generate (and automatically include an Excel .tlh file which contains the structures for _Application (and the rest you need)). Ideally, you should find an OLB file that matches the earliest Excel version that you wish to support. The one on your local system is probably in c:\Program Files\Microsoft Office\Office12 (presuming you have Office 2007 installed). It may be named Excel.olb, or XL5EN32.OLB (different, obviously if you haven't installed the US English verion of Excel.
    So, copy the .olb file to your project source directory, then at the top of the source file, add a line for #import "XL5EN32.olb".

  2. Yes, opens older versions. Best way to guarantee that this will be the case is to find an OLB file (mentioned in item 1 above) that is from an installation of Excel that is the earliest version you wish to support. I use an Excel9.olb from Office 2000. Works fine with my testing of Excel versions all the way to the latest from Office 2007.

  3. Yes, you should use dsoframer normally after making these changes.

  4. I'm afraid I probably can't do that due to restrictions of my employer. However, if you take the "stock" dsoframer project, make the changes described in part 1 of this post, and the changes I described in my earlier post, you have pretty much recreated exactly what I have.

千纸鹤 2024-07-15 16:18:39

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

将 _Application 重命名为 Application (以及其他)并不是正确的方法。 _Application 结构是具有映射的结构。 这就是为什么您找不到应用程序->get_Workbooks。

您在查找哪个文件,找到了 Application 但没有找到 _Application?

@Jinjin: did you put the import statement (#import "XL5EN32.olb") in the cpp file where you are using the Excel::_Application? If not, do that... can't just add it to the project. If you have already done that, try also adding this statement to the cpp file where you are using those mappings #import "Debug\XL5EN32.tlh". The tlh file is a header that is generated by running the #import; you should find it in your Debug directory (presuming you're performing a Debug build).

Renaming _Application to Application (and the others) is not the right way to go. The _Application structure is the one that has the mappings. That is why you are not finding the app->get_Workbooks.

What file are you looking in that you are finding Application but not _Application?

玻璃人 2024-07-15 16:18:39

假设您使用的是 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;
}

Assuming you are using the DSOFRAMER project, you need to add this code to dsofdocobj.cpp in the CreateFromFile function, at around line 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>
}

Then, define the following new member function in 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文