从单独线程启动时出现 OpenOffice Automation 问题

发布于 2025-01-02 03:56:09 字数 5795 浏览 0 评论 0 原文

我有一个 C++ 应用程序,有时需要将信息导出到电子表格。它旨在使用 COM 和 ActiveX 与 Microsoft Excel 和 OpenOffice Calc 集成来实现此目的。

我注意到,在 OpenOffice 的较新版本之一中,每当我尝试进行导出时,我的程序都会超时并失败。

在弄清楚失败需要以下两个事件之前,我做了相当多的研究:

1.)使用自定义过程创建一个简单的 UI 窗口(即使该过程除了将所有内容传递给默认过程之外没有做任何事情) )

2.) 创建一个单独的线程,在该线程中执行启动 OpenOffice(通过 COM 和 ActiveX)的代码

我应该注意到,任何给定时间,都只有一个线程进行 OpenOffice 集成。它只是恰好与处理 UI 的线程不同。

我还注意到其他一些奇怪的现象。

如果窗口类不涉及自定义过程,则不会发生错误。但是,如果涉及任何自定义程序,它就会发生。即使自定义窗口过程什么也不做,只是将所有消息传递给默认窗口过程,也会发生错误。

如果没有创建 UI 窗口,则单独线程中的代码将完美执行。

如果集成代码从与 UI 相同的线程启动,则不会发生错误。如果首先在与 UI 相同的线程中执行集成,则后续创建和执行单独线程时不会出现错误。

这是最奇怪的观察结果:我使用 Visual Studio 2005 进行调试。如果我在调用“loadComponentFromURL”之前设置断点,则不会发生挂起。但是,如果我不设置断点,当发生挂起时,我可以中断执行,并且我会发现调用堆栈表明它卡在 RPC 调用过程中的某个位置,等待从 WaitForMultipleObjectsEx(...) 返回。

下面是一个完整的代码示例。如果您在装有最新版本 OpenOffice 的计算机上编译并运行它,它将挂起。在 WinMain(...) 函数中,有一个对 TestOOCalc 的调用已被注释掉。如果取消注释,您会发现该程序现在可以完美启动 OpenOffice Calc。

鉴于没有多个线程尝试同时访问 OpenOffice,这似乎根本不应该是线程问题。

我找不到任何有关此现象或根本原因的信息。我真的不想将所有工作都放在与 UI 相同的线程中,因为这会使 UI 在长时间操作期间无响应。

想法?有想法吗?

#include <windows.h>
#include <atlbase.h>
#include <process.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hwnd, message, wParam, lParam);
}

BOOL MakeUIWindow(HINSTANCE hInstance)
{
    // Class definition for Main Window
    WNDCLASS wndclass;
    ZeroMemory(&wndclass, sizeof(wndclass));

    wndclass.style            = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc      = WndProc;
    wndclass.hInstance        = hInstance;
    wndclass.lpszClassName    = TEXT("Problem Window Class");

    // Register the Main Window class
    if (!RegisterClass(&wndclass))
        return FALSE;

    HWND hwnd = CreateWindowEx(0, TEXT("Problem Window Class"), 
                                        TEXT("Problem"), WS_OVERLAPPEDWINDOW,
                                       10, 10, 500, 500, 
                                        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, SW_NORMAL);

    return TRUE;
}

BOOL ActiveX_MethodCall(CComPtr<IDispatch> &rcpPropInterface, const WCHAR *wszMethod, const UINT uiArgs, VARIANTARG *pArgs, CComPtr<IDispatch> &rcpResult)
{
    DISPID dispid;
    HRESULT hr = rcpPropInterface.GetIDOfName(wszMethod, &dispid);
    if (FAILED(hr))
        return FALSE;

    DISPPARAMS dp;
    EXCEPINFO ei;
    VARIANT varReturn;
    ZeroMemory(&varReturn, sizeof(varReturn));
    ZeroMemory(&dp, sizeof(dp));
    ZeroMemory(&ei, sizeof(ei));

    varReturn.vt = VT_EMPTY;

    dp.cArgs = uiArgs;
    dp.rgvarg = pArgs;

    hr = rcpPropInterface->Invoke(dispid, IID_NULL, NULL, DISPATCH_METHOD, &dp, &varReturn, NULL, NULL);

    if (FAILED(hr))
        return FALSE;

    rcpResult.Attach(varReturn.pdispVal);
    return TRUE;
}

// Performs an initialization of OpenOffice
BOOL TestOOCalc()
{
    if (FAILED(CoInitialize(NULL)))
        return FALSE;

    // Get class IDs for the ActiveX object specified
    CLSID clsid;
    if (FAILED(CLSIDFromProgID(L"com.sun.star.ServiceManager", &clsid)))
        return FALSE;

    CComPtr<IDispatch> cpSvcMgr;
    if (FAILED(cpSvcMgr.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER)))
        return FALSE;

    CComPtr<IDispatch> cpDesktop;
    { // context change for local variants
        VARIANTARG varArg;
        ZeroMemory(&varArg, sizeof(varArg));

        varArg.scode = DISP_E_PARAMNOTFOUND;
        varArg.vt = VT_BSTR;
        varArg.bstrVal = SysAllocString(L"com.sun.star.frame.Desktop");

        if (!ActiveX_MethodCall(cpSvcMgr, L"createInstance", 1, &varArg, cpDesktop))
        {
            VariantClear(&varArg);
            return FALSE;
        }

        VariantClear(&varArg);
    }

    // Call Desktop.loadComponentFromURL Method
    CComPtr<IDispatch> cpWorkbook;
    { // context change for local variants
        VARIANTARG pvarArgs[4];
        ZeroMemory(&pvarArgs, sizeof(pvarArgs));

        pvarArgs[3].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[3].vt = VT_BSTR;
        pvarArgs[3].bstrVal = SysAllocString(L"private:factory/scalc");

        pvarArgs[2].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[2].vt = VT_BSTR;
        pvarArgs[2].bstrVal = SysAllocString(L"_blank");

        pvarArgs[1].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[1].vt = VT_I4;
        pvarArgs[1].lVal = 0;

        SAFEARRAYBOUND saBound;
        saBound.lLbound = 0;
        saBound.cElements = 0;
        SAFEARRAY *psaArgs = SafeArrayCreate(VT_VARIANT, 1, &saBound);
        pvarArgs[0].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[0].vt = VT_ARRAY | VT_VARIANT;
        pvarArgs[0].parray = psaArgs;

        if (!ActiveX_MethodCall(cpDesktop, L"loadComponentFromURL", 4, pvarArgs, cpWorkbook))
        {
            SafeArrayDestroy(psaArgs);
            VariantClear(&pvarArgs[3]);
            VariantClear(&pvarArgs[2]);
            VariantClear(&pvarArgs[1]);
            VariantClear(&pvarArgs[0]);

            return FALSE;
        }

        SafeArrayDestroy(psaArgs);
        VariantClear(&pvarArgs[3]);
        VariantClear(&pvarArgs[2]);
        VariantClear(&pvarArgs[1]);
        VariantClear(&pvarArgs[0]);
    }

    return TRUE;
}

unsigned int __stdcall thrTestOOCalc(void *vShare)
{
    TestOOCalc();
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    if (!MakeUIWindow(hInstance))
        return 0;

    //TestOOCalc();

    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, thrTestOOCalc, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    return 0;
}

I have a C++ application that sometimes needs to export information to a spreadsheet. It is designed to do so using COM and ActiveX integration with Microsoft Excel and OpenOffice Calc.

I noticed with one of the newer versions of OpenOffice that my program would timeout and fail any time I tried doing the export.

I did quite a bit of research before figuring out that the failure required the following two events:

1.) Creation of a simple UI window with a custom procedure (even if that procedure did not do anything more than pass everything on to the default procedure)

2.) Creation of a separate thread in which the code to launch OpenOffice (via COM and ActiveX) is executed

I should note that any given time, there is only ONE thread doing OpenOffice integration. It just happens to be a different thread from the one handling the UI.

I also noticed some other oddities.

If the window class does NOT involve a custom procedure, no error occurs. However, if ANY custom procedure is involved it does occur. Even if the custom window procedure does absolutely nothing but pass all messages to the default window procedure, the error occurs.

If no UI window is made, the code in the separate thread executes flawlessly.

If the integration code is launched from the same thread as the UI, no error occurs. If the integration is first carried out within the same thread as the UI, subsequent creation and execution of a separate thread runs without error.

And this is the weirdest observation: I'm using Visual Studio 2005 for debugging. If I set a breakpoint just prior to the invocation of "loadComponentFromURL", the hang will NOT occur. However, if I do NOT set a break point, when the hang occurs I can break execution and I'll find that the call stack indicates that it is stuck somewhere within the process of RPC invocation awaiting a return from WaitForMultipleObjectsEx(...).

Below is a complete code example. If you compile and run this on a machine with the newest version of OpenOffice, it will hang. Within the WinMain(...) function, there is a call to TestOOCalc that is commented out. If you uncomment it, you'll find the program now launches OpenOffice Calc perfectly.

Given that there are NOT multiple threads attempting to access OpenOffice at the same time, this doesn't seem like it should be a threading issue at all.

I can't find anything anywhere about this phenomenon or what the root cause is. I really don't want to resort to putting all of the work in the same thread as the UI as this would make the UI unresponsive during lengthy operations.

Thoughts? Ideas?

#include <windows.h>
#include <atlbase.h>
#include <process.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hwnd, message, wParam, lParam);
}

BOOL MakeUIWindow(HINSTANCE hInstance)
{
    // Class definition for Main Window
    WNDCLASS wndclass;
    ZeroMemory(&wndclass, sizeof(wndclass));

    wndclass.style            = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc      = WndProc;
    wndclass.hInstance        = hInstance;
    wndclass.lpszClassName    = TEXT("Problem Window Class");

    // Register the Main Window class
    if (!RegisterClass(&wndclass))
        return FALSE;

    HWND hwnd = CreateWindowEx(0, TEXT("Problem Window Class"), 
                                        TEXT("Problem"), WS_OVERLAPPEDWINDOW,
                                       10, 10, 500, 500, 
                                        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, SW_NORMAL);

    return TRUE;
}

BOOL ActiveX_MethodCall(CComPtr<IDispatch> &rcpPropInterface, const WCHAR *wszMethod, const UINT uiArgs, VARIANTARG *pArgs, CComPtr<IDispatch> &rcpResult)
{
    DISPID dispid;
    HRESULT hr = rcpPropInterface.GetIDOfName(wszMethod, &dispid);
    if (FAILED(hr))
        return FALSE;

    DISPPARAMS dp;
    EXCEPINFO ei;
    VARIANT varReturn;
    ZeroMemory(&varReturn, sizeof(varReturn));
    ZeroMemory(&dp, sizeof(dp));
    ZeroMemory(&ei, sizeof(ei));

    varReturn.vt = VT_EMPTY;

    dp.cArgs = uiArgs;
    dp.rgvarg = pArgs;

    hr = rcpPropInterface->Invoke(dispid, IID_NULL, NULL, DISPATCH_METHOD, &dp, &varReturn, NULL, NULL);

    if (FAILED(hr))
        return FALSE;

    rcpResult.Attach(varReturn.pdispVal);
    return TRUE;
}

// Performs an initialization of OpenOffice
BOOL TestOOCalc()
{
    if (FAILED(CoInitialize(NULL)))
        return FALSE;

    // Get class IDs for the ActiveX object specified
    CLSID clsid;
    if (FAILED(CLSIDFromProgID(L"com.sun.star.ServiceManager", &clsid)))
        return FALSE;

    CComPtr<IDispatch> cpSvcMgr;
    if (FAILED(cpSvcMgr.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER)))
        return FALSE;

    CComPtr<IDispatch> cpDesktop;
    { // context change for local variants
        VARIANTARG varArg;
        ZeroMemory(&varArg, sizeof(varArg));

        varArg.scode = DISP_E_PARAMNOTFOUND;
        varArg.vt = VT_BSTR;
        varArg.bstrVal = SysAllocString(L"com.sun.star.frame.Desktop");

        if (!ActiveX_MethodCall(cpSvcMgr, L"createInstance", 1, &varArg, cpDesktop))
        {
            VariantClear(&varArg);
            return FALSE;
        }

        VariantClear(&varArg);
    }

    // Call Desktop.loadComponentFromURL Method
    CComPtr<IDispatch> cpWorkbook;
    { // context change for local variants
        VARIANTARG pvarArgs[4];
        ZeroMemory(&pvarArgs, sizeof(pvarArgs));

        pvarArgs[3].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[3].vt = VT_BSTR;
        pvarArgs[3].bstrVal = SysAllocString(L"private:factory/scalc");

        pvarArgs[2].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[2].vt = VT_BSTR;
        pvarArgs[2].bstrVal = SysAllocString(L"_blank");

        pvarArgs[1].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[1].vt = VT_I4;
        pvarArgs[1].lVal = 0;

        SAFEARRAYBOUND saBound;
        saBound.lLbound = 0;
        saBound.cElements = 0;
        SAFEARRAY *psaArgs = SafeArrayCreate(VT_VARIANT, 1, &saBound);
        pvarArgs[0].scode = DISP_E_PARAMNOTFOUND;
        pvarArgs[0].vt = VT_ARRAY | VT_VARIANT;
        pvarArgs[0].parray = psaArgs;

        if (!ActiveX_MethodCall(cpDesktop, L"loadComponentFromURL", 4, pvarArgs, cpWorkbook))
        {
            SafeArrayDestroy(psaArgs);
            VariantClear(&pvarArgs[3]);
            VariantClear(&pvarArgs[2]);
            VariantClear(&pvarArgs[1]);
            VariantClear(&pvarArgs[0]);

            return FALSE;
        }

        SafeArrayDestroy(psaArgs);
        VariantClear(&pvarArgs[3]);
        VariantClear(&pvarArgs[2]);
        VariantClear(&pvarArgs[1]);
        VariantClear(&pvarArgs[0]);
    }

    return TRUE;
}

unsigned int __stdcall thrTestOOCalc(void *vShare)
{
    TestOOCalc();
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    if (!MakeUIWindow(hInstance))
        return 0;

    //TestOOCalc();

    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, thrTestOOCalc, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    return 0;
}

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

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

发布评论

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

评论(1

日暮斜阳 2025-01-09 03:56:09

自从日常使用 COM 以来已经有很长一段时间了,但对我来说,这看起来像是在公寓线程中泵送消息的典型失败。

检查以下内容:

  1. OpenOffice 组件是否声明为公寓线程
  2. 如果没有,请尝试使用 CoInitializeEx
  3. 如果 OO 组件被声明为单元线程,则您需要在新创建的线程上发送消息。

希望这有帮助。

It has been a long time since a worked in a daily basis with COM, but to me this looks like the classic failure of pumping messages in an APARTMENT thread.

Check the following:

  1. Are OpenOffice component declared as apartment threaded ?
  2. If not, try to initialize your thread in MTA using CoInitializeEx.
  3. If OO components are declared as apartment thread, you'll need to pump messages on your newly created thread.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文