У меня есть приложение на C ++, которое иногда требует экспортировать информацию в электронную таблицу.Он предназначен для этого с использованием интеграции COM и ActiveX с Microsoft Excel и OpenOffice Calc.
В одной из более новых версий OpenOffice я заметил, что моя программа будет зависать по таймауту и завершаться ошибкой всякий раз, когда я пытаюсь выполнить экспорт.
Я провел небольшое исследование, прежде чем выяснил, что для сбоя потребовались следующие два события:
1.) Создание простого окна пользовательского интерфейса с пользовательской процедурой (даже если эта процедура не выполнялась).сделать что-то большее, чем передать все в процедуру по умолчанию)
2.) Создание отдельного потока, в котором выполняется код для запуска OpenOffice (через COM и ActiveX)
Я должен отметить,что в любой момент времени только ОДИН поток выполняет интеграцию OpenOffice.Просто случается, что это поток, отличный от потока, обрабатывающего пользовательский интерфейс.
Я также заметил некоторые другие странности.
Если класс окна НЕ включает пользовательскую процедуру, ошибки не возникает.Однако, если ЛЮБАЯ пользовательская процедура вовлечена, это действительно происходит.Даже если пользовательская оконная процедура абсолютно ничего не делает, а передает все сообщения оконной процедуре по умолчанию, возникает ошибка.
Если окно пользовательского интерфейса не создано, код в отдельном потоке выполняется без ошибок.
Если код интеграции запускается из того же потока, что и пользовательский интерфейс, ошибки не возникает.Если интеграция сначала выполняется в том же потоке, что и пользовательский интерфейс, последующее создание и выполнение отдельного потока выполняется без ошибок.
И это самое странное наблюдение: я использую Visual Studio 2005 для отладки.Если я установлю точку останова непосредственно перед вызовом «loadComponentFromURL», зависание НЕ произойдет.Однако, если я НЕ установлю точку останова, когда произойдет зависание, я могу прервать выполнение и обнаружу, что стек вызовов указывает, что он застрял где-то в процессе вызова RPC, ожидая возврата из WaitForMultipleObjectsEx (...).
Ниже приведен полный пример кода.Если вы скомпилируете и запустите это на машине с последней версией OpenOffice, она зависнет.В функции WinMain (...) есть закомментированный вызов TestOOCalc.Если вы раскомментируете его, вы обнаружите, что программа теперь прекрасно запускает OpenOffice Calc.
Учитывая, что несколько потоков не пытаются одновременно получить доступ к OpenOffice, это не должно быть многопоточностью.проблема вообще.
Я нигде не могу найти ничего об этом явлении или его коренной причине.Я действительно не хочу прибегать к тому, чтобы поместить всю работу в один и тот же поток с пользовательским интерфейсом, поскольку это сделало бы пользовательский интерфейс невосприимчивым к длительным операциям.
Мысли?Идеи?
#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;
}