我有一個C++應用程序,有時需要將信息導出到電子表格。它旨在使用COM和ActiveX與Microsoft Excel和OpenOffice Calc進行集成。從單獨線程啓動時出現OpenOffice Automation問題
我注意到OpenOffice的一個較新版本,我的程序在任何時候嘗試導出時都會超時並失敗。
我做了相當多的研究找出了故障需要以下兩個事件之前:
1)用自定義程序(即使該程序沒有做任何事情更簡單的UI窗口的創建比傳遞到默認程序的所有內容)
2)一個單獨的線程中的代碼通過COM和ActiveX推出的OpenOffice()被執行
我要指出的創造,任何給定的時間,有隻有一個線程在做OpenOffice集成。它恰好是與處理UI的人不同的線程。
我也注意到一些其他的怪異。
如果窗口類不涉及自定義過程,則不會發生錯誤。但是,如果涉及任何自定義過程,它確實發生。即使自定義窗口過程絕對不會將所有消息傳遞給默認的窗口過程,也會發生錯誤。
如果沒有製作UI窗口,單獨線程中的代碼將完美無瑕地執行。
如果集成代碼是從與UI相同的線程啓動的,則不會發生錯誤。如果集成首先在與UI相同的線程中執行,則後續創建和執行單獨的線程將無誤地運行。
這是最奇怪的觀察:我正在使用Visual Studio 2005進行調試。如果我在調用「loadComponentFromURL」之前設置了斷點,則掛起不會發生。但是,如果我沒有設置中斷點,那麼當發生掛起時,我可以中斷執行,並且我會發現調用堆棧指示它停留在等待從WaitForMultipleObjectsEx(...)返回的RPC調用過程中的某處。 。
下面是一個完整的代碼示例。如果您使用最新版本的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;
}
謝謝你Vaugaus的信息。 OpenOffice自動化的文檔非常薄弱,但我認爲它是**公寓線程。但是,我不確定如何或在哪裏建立消息循環,因爲我的線程在調用堆棧爲空時從不等待任何事情。相反,每當我的線程調用OO啓動的一部分「loadComponentFromURL」時就會發生掛起。掛起發生在調用堆棧內部,它永遠等待WaitForMultipleObjectsEx(...)的返回。 – dshockey 2012-02-06 15:25:13
COM文檔指出,您不需要公寓線程的消息泵的唯一情況是,如果您只調用簡單的方法(即沒有回調等)。我可以想象這樣一個場景:你調用OO組件,它決定爲了什麼原因產生一個新的線程,並調用一個方法(在本身或另一個組件上,AFAIK,這並不重要);此時您確實需要消息泵(因爲OO正在穿過公寓組件的線程)。不幸的是,我不知道在哪裏介紹消息泵。 – Vagaus 2012-02-07 10:33:57
我發現了這個問題。問題在於主UI線程暫時等待完成處理線程的信號,並且沒有通過它的消息循環循環。通過設計消息傳遞方案並允許UI線程恢復消息泵,問題就消失了。顯然,COM對象正在等待其在UI線程中發佈的某條消息的結果。謝謝你讓我走上正確的軌道來搞清楚這一點。 – dshockey 2012-02-15 00:53:08