Well I want to minimize the window after starting the window but it doesn't take effect and I don't know what I'm doing wrong. Can you please point out the mistake in the code below?
Nothing happens other than that a window is opened.
HWND g_hwnd;
int g_nFound;
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);
HWND GetHwndFromPID(DWORD dwProcessId)
{
g_hwnd = NULL;
g_nFound = 0;
EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);
if (g_hwnd) // we found one...
return (g_hwnd);
// nothing found :-(
return (NULL);
}
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam)
{
DWORD dwPID2Find = (DWORD)lParam;
DWORD dwPID = 0;
if (GetWindowThreadProcessId(hwnd, &dwPID))
{
if (dwPID == dwPID2Find)
{
g_hwnd = hwnd;
return (FALSE);
}
}
return (TRUE);
}
int main
{
..../
if (!CreateProcessA(NULL, // No module name (use command line)
command_line.GetBuffer(),
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
//... error handling
return 0;
}
WaitForInputIdle(pi.hProcess, 1000);
HWND hwnds = GetHwndFromPID(pi.dwProcessId);
printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);
CloseWindow(hwnds);
That code is supposed to minimize the window but I don't know why it doesn't.
You are going about this the wrong way. The official and documented way to ask a spawned process to run initially minimized is to use the STARTUPINFO structure when calling CreateProcess(), eg:
int main()
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_MINIMIZE;
//...
if (!CreateProcessA(..., &si, ...))
{
//... error handling
return 0;
}
//...
return 0;
}
STARTUPINFO structure
wShowWindow
If dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the values that can be specified in the nCmdShow parameter for the ShowWindow function, except for SW_SHOWDEFAULT. Otherwise, this member is ignored.
For GUI processes, the first time ShowWindow is called, its nCmdShow parameter is ignored wShowWindow specifies the default value. In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is set to SW_SHOWDEFAULT.
ShowWindow function
nCmdShow [in]
Type: int
Controls how the window is to be shown. This parameter is ignored the first time an application calls ShowWindow, if the program that launched the application provides a STARTUPINFO structure. Otherwise, the first time ShowWindow is called, the value should be the value obtained by the WinMain function in its nCmdShow parameter.
...
The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter.
As noted in the discussion of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the structure. In this case, ShowWindow uses the information specified in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup information provided by the program that launched the application. This behavior is designed for the following situations:
• Applications create their main window by calling CreateWindow with the WS_VISIBLE flag set.
• Applications create their main window by calling CreateWindow with the WS_VISIBLE flag cleared, and later call ShowWindow with the SW_SHOW flag set to make it visible.
because the application window may not belong to the started process, but instead it belongs to its child process, you have to go deeper in FindHwndFromPID by including the parent process in the comparison.
also we shall not count on WaitForInputIdle() by it self, you have to give the created process enough time to fully initialize.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tlhelp32.h>
HWND g_hwnd;
int g_nFound;
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);
/*___________________________________________________________________________________________________
*/
HWND GetHwndFromPID(DWORD dwProcessId)
{
g_hwnd = NULL;
g_nFound = 0;
EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);
if (g_hwnd) // we found one...
return (g_hwnd);
// nothing found :-(
return (NULL);
}
/*___________________________________________________________________________________________________
*/
DWORD GetParentProcess(DWORD pid){
PROCESSENTRY32 p32={sizeof(PROCESSENTRY32)};
DWORD ParentPID=0;
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 );
if(Process32First(hSnapShot,&p32)){
do{
if(p32.th32ProcessID==pid){
ParentPID = p32.th32ParentProcessID;
break;
}
}while(Process32Next(hSnapShot,&p32));
}
CloseHandle(hSnapShot);
return ParentPID;
}
/*___________________________________________________________________________________________________
*/
int __stdcall WindowText(HWND hWnd,LPSTR lpString,int nMaxCount){
int ret;
ret=SendMessage(hWnd,WM_GETTEXTLENGTH,0,0);
if(ret){
ret=SendMessage(hWnd,WM_GETTEXT,nMaxCount,(LPARAM)lpString);
}
return ret;
}
/*___________________________________________________________________________________________________
*/
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam){
DWORD dwPID2Find = (DWORD)lParam;
DWORD dwPID = 0;
if(GetWindowLong(hwnd,GWLP_HWNDPARENT) || !IsWindowVisible(hwnd))
return 1;
if (GetWindowThreadProcessId(hwnd, &dwPID)){
if ((dwPID == dwPID2Find) || ( GetParentProcess(dwPID) == dwPID2Find)){
g_hwnd = hwnd;
return (FALSE);
}
}
return (TRUE);
}
/*___________________________________________________________________________________________________
*/
int main(){
PROCESS_INFORMATION pi;
STARTUPINFO si={sizeof(si)};
TCHAR exename[]=TEXT("write.exe"); // writable buffer (for Unicode bug.)
if (!CreateProcess(NULL,exename,NULL, NULL,FALSE, 0, NULL,NULL,&si, &pi)){
return 0;
}
//WaitForInputIdle(pi.hProcess, 1000); // this alown will not always work (process may have children)
// give enough time to process to fully initialize
// put all in a loop until you
// get the window handle or timeout.
HWND hwnds = GetHwndFromPID(pi.dwProcessId);
for( int i=0 ;i<1000;i++ ){
if(hwnds)
break;
Sleep(10);
hwnds = GetHwndFromPID(pi.dwProcessId);
}
printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);
CloseWindow(hwnds);
return 0;
}
Related
I have a dll that gets called by a process and now I would like to implement an input check in the dll to react on certain inputs that occur in the application.
SetWindowsHookEx() with a KeyboardProc function seemed like a possible solution so I implemented it.
This is roughly how the code in the dll looks like:
static HHOOK hhk = NULL;
LRESULT CALLBACK keyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if(code == HC_ACTION && ((DWORD)lParam & 0x80000000) == 0) // if there is an incoming action and a key was pressed
{
switch(wParam)
{
case VK_SPACE:
printf("Space was pressed\n");
break;
}
}
return CallNextHookEx(hhk, code, wParam, lParam);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if(ul_reason_for_call == DLL_PROCESS_ATTACH)
{
if(AllocConsole()){
freopen("CONOUT$", "w", stdout); // redirect output to console for debugging
}
printf("Dll loaded, lastError = %i\n", GetLastError());
printf("lastError = %i\n", GetLastError());
// sidenote: for some reason the first GetLastError() returns 0 while the second one returns 6 (invalid handle)
hhk = SetWindowsHookEx(WH_KEYBOARD, keyboardProc, hModule, GetCurrentThreadId());
}
else if (ul_reason_for_call == DLL_PROCESS_DETACH)
{
printf("\nCleaning up...");
FreeConsole();
UnhookWindowsHookEx(hhk);
}
return TRUE;
}
However nothing happens (or gets printed) in the Console window when I press any key. It doesn't even seem like the keyboardProc function is accessed at any time.
It does work though when I pass NULL instead of GetCurrentThreadId() to SetWindowsHookEx().
But this causes the hook to work globally meaning that whenever I press a key in another application, a Console window pops up (because the dll gets called again) and he checks for key inputs there.
Obviously this is not desired and I would like to make this work with only the process that originally called the dll.
I already checked if GetCurrentThreadId() returns a valid ID and it seems to be indeed the main thread ID of the process that initially called the dll (checked with Process Explorer).
So now my question is what could be the problem and more importantly, what can I do to make it working?
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
uint process_id;
uint thread_id = GetWindowThreadProcessId(windowHandle, out process_id);
hhook = SetWindowsHookEx(WH_KEYBOARD, a_KeyboardProc, hInstance, 0);
I have used the code above to get the main thread_ID for a certain process. The good part is, the SetWindowsHookEx function gives a logical output. Unfortunately, the bad part is, if a key is pressed in the thread that has been hooked, the thread stops working.
In specific, the idHook parameter of SetWindowsHoookEx function was set to 2 (instead of 13) in my case for non-low-level keyboard events. It seems, at least to me, that LL corresponds to low-level, where keyboardProc should come with a WH_KEYBOARD instead of WH_KEYBOARD_LL.
I am not sure at this point how my response would be related to your question. Hopefully, we get what we need through discussion.
Basically, I want to convert a process ID to a HWND. I am using this code:
DWORD dwWindowId;
CHAR pszClassName[200];
HWND hWnd;
hWnd = GetTopWindow (NULL);
while ( hWnd != NULL )
{
if ( GetClassName (hWnd, pszClassName, 200) > 0)
if ( lstrcmpiA (lpcszWindowClass, pszClassName) == 0)
if ( GetWindowThreadProcessId (hWnd, &dwWindowId) )
if ( dwWindowId == dwProcessId )
return hWnd;
hWnd = GetNextWindow ( hWnd, GW_HWNDNEXT );
}
return NULL;
This worked fine until I tried with a process was created by CreateProcess. What should I do in this case? I have the process info, such as its ID and thread ID from CreateProcess, but I still do not know how to get its hwnd. I did read this:
After you call CreateProcess(), examine the PROCESS_INFORMATION
struct pointed to by lpProcessInformation argument.
PROCESS_INFORMATION contains a handle to the Process you just
started and a Thread ID. With this information call the
GetGUIThreadInfo()function and then examine the GUITHREADINFO
struct pointed to by lpgui. GUITHREADINFO has several HWNDs. Start
with hwndActive, and call GetParent() or GetAncestor() untill the
Main window is found.
By bug_crusher
I have tried EnumChildWindows() and EnumWindows(), and they did not work.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
DWORD PID =0;
GetWindowThreadProcessId(hwnd,&PID);
if(PID == 1)
{
//,,,,,,,,
}
return TRUE;
}
But I don’t get it, can anyone explain that?
I'm a bit confused by what you're actually trying to do, but this function will build a vector of all the top-level windows belonging to the specified process.
void GetWindowsOfProcess(DWORD dwId, std::vector<HWND>& vecWindows)
{
struct WindowsOfProcess
{
std::vector<HWND>* pvecWindows;
DWORD dwProcId;
static BOOL CALLBACK EnumProc(HWND hWnd, LPARAM lParam)
{
DWORD dwProcId;
GetWindowThreadProcessId(hWnd, &dwProcId);
if (dwProcId == reinterpret_cast<WindowsOfProcess*>(lParam)->dwProcId)
reinterpret_cast<WindowsOfProcess*>(lParam)->pvecWindows->push_back(hWnd);
return TRUE;
}
WindowsOfProcess(DWORD dwId, std::vector<HWND>* pvec)
: dwProcId(dwId)
, pvecWindows(pvec)
{
EnumWindows(EnumProc, reinterpret_cast<LPARAM>(this));
}
};
WindowsOfProcess wop(dwId, &vecWindows);
}
How can i find the lpClassName string of the FindWindow API call
if i start application with CreateProcess API function
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
STARTUPINFO StartupInfo; //This is an [in] parameter
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
if(CreateProcess("c:\\temp\\application1.exe", NULL,
NULL,NULL,FALSE,0,NULL,
NULL,&StartupInfo,&ProcessInfo))
{
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
}
else
{
MessageBox("The process could not be started...");
}
also can i some how set only part of the name in the FindWindow ?
for example if i know that allays the app name is "application< some version number>.exe"
so it can be :
application1.exe
application1.1.exe
application1.2.1.exe
my final goal is to use Windows Message system and the SendMessage API method to send messages to the application.
It sounds like what you really want is the handle to the window created by your launched application.
The problem is, your process might create many windows. You can use the SetWindowsHookEx function to be notified whenever the process creates a new window.
Untested code incoming:
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
if(nCode == HCBT_CREATEWND) {
// wParam is a handle to a window your app just created.
}
return 0;
}
CreateProcess("c:\\temp\\application1.exe", NULL,
NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,
NULL,&StartupInfo,&ProcessInfo);
SetWindowsHookEx(WH_CBT, procHook, NULL, ProcessInfo.hThread);
ResumeThread(ProcessInfo.hTread);
I'm working on a sporadic production issue that's occurring within our 32 bit MFC VC2010 application. The application is running on Windows Server 2008 R2 Standard SP1 64-bit.
The issue is caused by a failure to create a CWnd derived class. When the failure occurs the AfxUnhookWindowCreate method returns false within CWnd::CreateEx. This is because the pThreadState->m_pWndInit variable is not NULL. It looks like _AfxCbtFilterHook should be setting this to NULL when HCBT_CREATEWND is hooked, but it appears this is not occurring. I've logged out the CREATESTRUCT and compared it to when the failure occurs vs. doesn't occur and the parameters are essentially the same.
Does anyone have ideas on what could cause this or how I could identify the cause? Thanks!
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
...
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
...
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
...
BOOL AFXAPI AfxUnhookWindowCreate()
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
#ifndef _AFXDLL
if (afxContextIsDLL && pThreadState->m_hHookOldCbtFilter != NULL)
{
::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
pThreadState->m_hHookOldCbtFilter = NULL;
}
#endif
if (pThreadState->m_pWndInit != NULL)
{
pThreadState->m_pWndInit = NULL;
return FALSE; // was not successfully hooked
}
return TRUE;
}
LRESULT CALLBACK
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (code != HCBT_CREATEWND)
{
// wait for HCBT_CREATEWND just pass others on...
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
}
...
pThreadState->m_pWndInit = NULL;
I tracked the problem down to a window procedure hook that shouldn't have been executing at this time.
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;
}
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:
Are OpenOffice component declared as apartment threaded ?
If not, try to initialize your thread in MTA using CoInitializeEx.
If OO components are declared as apartment thread, you'll need to pump messages on your newly created thread.
Hope this helps.