从单独线程启动时出现 OpenOffice Automation 问题
我有一个 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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
自从日常使用 COM 以来已经有很长一段时间了,但对我来说,这看起来像是在公寓线程中泵送消息的典型失败。
检查以下内容:
希望这有帮助。
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:
Hope this helps.