从单独的线程启动时的OpenOffice自动化问题

时间:2012-02-05 04:11:45

标签: c++ multithreading com activex

我有一个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;
}

1 个答案:

答案 0 :(得分:1)

自从每天与COM一起工作已经很长时间了,但对我而言,这看起来像是在APARTMENT线程中抽取消息的经典失败。

检查以下内容:

  1. OpenOffice组件是否声明为公寓线程
  2. 如果没有,请尝试使用CoInitializeEx在MTA中初始化您的主题。
  3. 如果OO组件 声明为公寓线程,则需要在新创建的线程上提取消息。
  4. 希望这有帮助。