I have a application which uses socket connection to send and receive data from another application. While creating socket it uses the port 4998 .
That is where my problem lie. Once I start my application the socket starts using port 4998. So if I want to execute the application again then I get socket binding error.
So I want to limit my application instance to one. That means if the application is already running and some one tries to run the application again by clicking the exe or shortcut icon it shouldn't run the program, instead it should bring the existing application to the Top.
You may used named mutex.
Code sample from the article:
WINAPI WinMain(
HINSTANCE, HINSTANCE, LPSTR, int)
{
try {
// Try to open the mutex.
HANDLE hMutex = OpenMutex(
MUTEX_ALL_ACCESS, 0, "MyApp1.0");
if (!hMutex)
// Mutex doesn’t exist. This is
// the first instance so create
// the mutex.
hMutex =
CreateMutex(0, 0, "MyApp1.0");
else
// The mutex exists so this is the
// the second instance so return.
return 0;
Application->Initialize();
Application->CreateForm(
__classid(TForm1), &Form1);
Application->Run();
// The app is closing so release
// the mutex.
ReleaseMutex(hMutex);
}
catch (Exception &exception) {
Application->
ShowException(&exception);
}
return 0;
}
When your application initializes, create a mutex. If it already exists, find the existing application and bring it to the foreground. If the application has a fixed title for its main window, it is easy to find with FindWindow.
m_singleInstanceMutex = CreateMutex(NULL, TRUE, L"Some unique string for your app");
if (m_singleInstanceMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
HWND existingApp = FindWindow(0, L"Your app's window title");
if (existingApp) SetForegroundWindow(existingApp);
return FALSE; // Exit the app. For MFC, return false from InitInstance.
}
/*
I have found the necessary editing to be done. Added some extra code and edits that are needed. The present one is working perfectly for me. Thank you, Kirill V. Lyadvinsky and Remy Lebeau for the help!!
*/
bool CheckOneInstance()
{
HANDLE m_hStartEvent = CreateEventW( NULL, FALSE, FALSE, L"Global\\CSAPP" );
if(m_hStartEvent == NULL)
{
CloseHandle( m_hStartEvent );
return false;
}
if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
CloseHandle( m_hStartEvent );
m_hStartEvent = NULL;
// already exist
// send message from here to existing copy of the application
return false;
}
// the only instance, start in a usual way
return true;
}
/*
The above code works even when one tries to open up second instance FROM A DIFFERENT LOGIN LEAVING THE FIRST LOGIN OPEN with ITS INSTANCE RUNNING.
*/
Create named event on the start and check the result. Close application if the event is already exist.
BOOL CheckOneInstance()
{
m_hStartEvent = CreateEventW( NULL, TRUE, FALSE, L"EVENT_NAME_HERE" );
if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
CloseHandle( m_hStartEvent );
m_hStartEvent = NULL;
// already exist
// send message from here to existing copy of the application
return FALSE;
}
// the only instance, start in a usual way
return TRUE;
}
Close m_hStartEvent on the app exit.
You can achieve this in your WindowMain function by calling the FindWindow function with the class name and the title of your main window at the very beginning. If the window exists you can either show a message to the user or simply show the window and then return:
HWND hWnd = FindWindow(lzClassName, lzWindowText);
if(hWnd!=NULL)
{
ShowWindow(hWnd, SW_SHOW);
return 0;
}
WNDCLASSEX wcex;
/* register window class */
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC;
wcex.lpfnWndProc = WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
...
Don't you already have a way to check if your application is running? Who needs a Mutex, if the port is already taken, you know that the app is running!
Related
I have a 32-bit MFC application that uses a custom library that would be a nightmare to re-compile into x64. In general the app doesn't really need to run as 64-bit, except in one case -- and that is to render contents to display in a dialog window, which can benefit from a larger addressing space.
So my goal is to "imitate" CDialog::DoModal method but for a dialog in another process.
I built that dialog window as a standalone x64 MFC dialog-based application. It takes a file path as it's input parameter, does all the work internally, and returns simple user selection: OK, Cancel.
So I do the following from my main parent process:
//Error checks omitted for brevity
CString strCmd = L"D:\\C++\\MyDialogBasedApp.exe";
HWND hParWnd = this->GetSafeHwnd();
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
sei.nShow = SW_SHOW;
sei.lpVerb = _T("open");
sei.lpFile = strCmd.GetBuffer();
sei.hwnd = hParWnd;
BOOL bInitted = SUCCEEDED(::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
ShellExecuteEx(&sei);
DWORD dwProcID = ::GetProcessId(sei.hProcess);
//Try to get main Wnd handle for the child process
HWND hMainChildWnd = NULL;
for(;; ::Sleep(100))
{
hMainChildWnd = getHwndFromProcID(dwProcID);
if(hMainChildWnd)
break;
}
HWND hPrevParWnd = ::SetParent(hMainChildWnd, hParWnd);
if(hPrevParWnd)
{
//Wait for child process to close
::WaitForSingleObject(sei.hProcess, INFINITE);
//Reset parent back
::SetParent(hMainChildWnd, hPrevParWnd);
}
::CloseHandle(sei.hProcess);
if(bInitted)
::CoUninitialize();
where getHwndFromProcID is taken from here.
This kinda works, except of the following:
(1) There are two icons on the taskbar: one for my main app, and one for the child app. Is there a way not to show a child icon?
(2) I can switch focus from child window to parent and vice versa. In actual modal dialog window one cannot switch back to parent while child is open. Is there a way to do that?
(3) If I start interacting with the parent, it appears to be "hung up" and the OS will even show it on its title bar.
So I was curious if there's a way to resolve all these?
you need pass pointer of own window to child process
you need process windows messages, while you wait on child process
exit. WaitForSingleObject unacceptably here - need use
MsgWaitForMultipleObjectsEx
child process must set your window as self owner window at create
time - you not need call SetParent
with this all will be worked perfect. in your 32-bit MFC application you need use next code :
BOOL DoExternalModal(HWND hwnd, PCWSTR ApplicationName)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
WCHAR CommandLine[32];
swprintf(CommandLine, L"*%p", hwnd);
if (CreateProcessW(ApplicationName, CommandLine, 0, 0, 0, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
MSG msg;
for (;;)
{
switch (MsgWaitForMultipleObjectsEx(1, &pi.hProcess, INFINITE, QS_ALLINPUT, 0))
{
case WAIT_OBJECT_0:
CloseHandle(pi.hProcess);
return TRUE;
case WAIT_OBJECT_0 + 1:
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
continue;
default: __debugbreak();
}
}
}
return FALSE;
}
in MyDialogBasedApp.exe let use MessageBox as demo dialog. we will be use your MFC window as first argument to it.
void ExeEntry()
{
int ec = -1;
if (PWSTR CommandLine = GetCommandLine())
{
if (CommandLine = wcschr(CommandLine, '*'))
{
HWND hwnd = (HWND)(ULONG_PTR)_wcstoi64(CommandLine + 1, &CommandLine, 16);
if (hwnd && !*CommandLine && IsWindow(hwnd))
{
ec = MessageBoxW(hwnd, L"aaa", L"bbb", MB_OK);
}
}
}
ExitProcess(ec);
}
with this code:
(1) There are only one icon on the taskbar for your main app
(2) You can not switch focus from child window to parent and vice versa. all work as actual modal dialog window
(3) parent not "hung up" because it processing windows messages (MsgWaitForMultipleObjectsEx) - your code is "hung up" because you not do this, but wait in WaitForSingleObject
A modal dialog box does two things that make it "modal":
The dialog's "owner" is set to the parent window.
The parent window is disabled.
I played around with this a little bit and while you can do these manually, the easiest way is to just pass the parent window handle to the DialogBox function (or the CDialog constructor in MFC).
Instead of doing all the work after ShellExecuteEx in the parent process, your child process can use FindWindow (or a similar mechanism) to get the parent window handle and use that to display the dialog.
What you are trying to do cannot safely be done. The blog entry Is it legal to have a cross-process parent/child or owner/owned window relationship? explains, that installing a cross-process owner/owned window relationship causes the system to call AttachThreadInput, and - as we all know - AttachThreadInput is like taking two threads and pooling their money into a joint bank account, where both parties need to be present in order to withdraw any money. This creates a very real potential for a dead lock. You can only safely prevent this from happening, if you control both participating threads. Since at least one thread uses a 3rd party application framework (MFC in this case), this is off limits.
Since we have established, that your proposed solution cannot safely be implemented, you need to look into alternatives. One solution might be to delegate the work to a 64-bit process for computation, and have the results passed back to your 32-bit process for display.
I have a simple (windows) application that launches another application using the CreateProcess function. It then gets the correct hwnd by using EnumWindows and the process id of the newly created process.
After the hwnd has been gained, the 'main loop' of my applications begins. This loop continues until the application started by CreateProcess is no longer running.
Everything works perfectly, until I try to use PeekMessage to peek at the messages being sent to the application I have launched - It seems that no messages at all are being recognized by my application, but the program that was launched (via CreateProcess) is running as normal, doing everything it should..
What I am trying to achieve, is to remove certain messages from being sent to the application, mainly various F-keys (F1, F2..F12 keys), but also, if possible, I would like to change the menu shown in the application (I dont know the technical name for the menu I mean, its the one you see what you click on the application icon in the top right corner) - I want to add a small 'about' option.
If anyone could point out what I am doing wrong within my code, or to a better alternative for stopping certain keypresses from reaching the launched application, I would be very grateful.
Many thanks in advance. :)
Here is the code I currently have:
VOID PerformLaunch(LPWSTR lpAppName, LPTSTR lpCmdLine) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwLoopExitCode = NULL;
BOOL cpBool = FALSE;
BOOL finishedLoop = FALSE;
MSG message = { 0 };
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
cpBool = CreateProcess(lpAppName, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!cpBool) {
MessageBox(Game_HWND, L"Could not start process!", L"Error:", MB_OK | MB_ICONERROR);
}
//-- Enumerate windows until Game_HWND && Game_Hook are not NULL.
while (Game_Hook == NULL) {
EnumWindows(MainEnumGameWindProc, pi.dwProcessId);
}
while (!finishedLoop) {
DWORD dwWC = WaitForSingleObject(pi.hProcess, 0);
if ((dwWC == WAIT_FAILED) || (dwWC == WAIT_OBJECT_0)|| (dwWC == WAIT_ABANDONED)) {
DWORD dwExitCode;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
dwLoopExitCode = dwExitCode;
finishedLoop = TRUE;
}
else {
if (PeekMessage(&message, Game_HWND, 0, 0, PM_REMOVE)) {
TranslateMessage(&message);
DispatchMessage(&message);
if (WM_QUIT == message.message) {
finishedLoop = TRUE;
break;
}
}
}
}
}
You can't intercept another message loop like that. Remember, that process will be doing its own message pump - it's not okay for you to be dispatching its messages, and I expect things would go very weird if you do.
The correct way is to set a hook. Use the SetWindowsHookEx function to install a hook on that window.
Reference for SetWindowsHookEx
PeekMessage retrieves messages only from the message queue, associated with the its calling thread. Window, which messages you are trying to peek belongs to the different thread, so PeekMessage with its handle will never give you anything. You could either install a hook, or subclass its window procedure.
I'm using ShellExecuteEx to run external application:
SHELLEXECUTEINFO shExInfo = { 0 };
shExInfo.cbSize = sizeof(shExInfo);
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shExInfo.hwnd = 0;
shExInfo.lpVerb = L"runas"; // Operation to perform
shExInfo.lpFile = windowStringContainingAppPath.c_str(); // Application to start
shExInfo.lpParameters = windowStringContainingAppParameters.c_str(); // Additional parameters
shExInfo.lpDirectory = 0;
shExInfo.nShow = SW_SHOW;
shExInfo.hInstApp = 0;
if(ShellExecuteEx(&shExInfo))
{
WaitForSingleObject(shExInfo.hProcess, INFINITE);
DeleteFile(wsMesh3dx64Parameter.c_str());
CloseHandle(shExInfo.hProcess);
}
Everything works perfectly but there exist an unplanned behaviour of this external app that after closing its main window its process is still active.
This prevents WaitForSingleObject(shExInfo.hProcess, INFINITE); from returning and I have to terminate the process manualy.
Instead I'm looking for a way to replace WaitForSingleObject(shExInfo.hProcess, INFINITE);with a loop that checks if external process owns a window and if not terminate it.
This is what I thought of but if there is a better way please point it out for me.
UPDATE:
Thanks to Robson answer I managed to do what I intended to:
struct Porcess_ID_HWND
{
DWORD processID;
HWND processhWnd;
};
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
Porcess_ID_HWND*info = (Porcess_ID_HWND*)lParam;
DWORD processID;
GetWindowThreadProcessId(hWnd, &processID);
if (processID == info->processID){
info->processhWnd = hWnd;
return FALSE;
}
return TRUE;
}
And my loop:
if(ShellExecuteEx(&shExInfo))
{
DWORD dwProcessID = GetProcessId(shExInfo.hProcess);
Porcess_ID_HWND info;
info.processID = dwProcessID;
// wait for window to appear
do
{
info.processhWnd = NULL;
EnumWindows(EnumWindowsProc, (LPARAM)&info);
} while (!info.processhWnd);
// wait for window to close
do
{
info.processhWnd = NULL;
EnumWindows(EnumWindowsProc, (LPARAM)&info);
} while (info.processhWnd);
//WaitForSingleObject(shExInfo.hProcess, INFINITE);
DeleteFile(wsMesh3dx64Parameter.c_str());
CloseHandle(shExInfo.hProcess);
}
found a good answer at http://forums.codeguru.com/showthread.php?392273-RESOLVED-How-to-get-window-s-HWND-from-it-s-process-handle
1)
HAVE: Process ID, NEED: Process handle
Solution: OpenProcess()
2)
HAVE: Process handle, NEED: Process ID
Solution: GetProcessId()
3)
HAVE: Window handle, NEED: Process ID
Solution: GetWindowThreadProcessId()
4)
HAVE: Window handle, NEED: Process handle
Solution: Use 3) and then 1)
5)
HAVE: Process ID, NEED: Window handle
Solution: EnumWindows(), then in the callback function do 3) and check if it matches your process ID.
6)
HAVE: Process handle, NEED: Window handle
Solution: 2) and then 5)
so you are in case 6. then if no window handle's process ID is matched with your shExInfo.hProcess's process ID then shExInfo.hProcess owns no window
Hello my fellow colleagues from StackOverflow!
I will try to be brief, so I will cut to the point:
I work on Windows XP, in C++, using pure Win32 to create a dialog box.
That dialog box has some edit controls, and OK button, which activates a thread when pressed.
Thread then gathers text from edit controls and writes them into MS Word document, using OLE Automation.
Everything works fine,when I press OK button, and wait for thread to show filled Word document.
However, when I push the OK button and then close the dialog, while thread is in the middle of the work, a blank Word document pops up.
To further illustrate my problem here are some code snippets:
This is snippet for thread function:
DWORD WINAPI TabelaSvihObjekata( LPVOID hWnd ) // hWnd is handle of the Dialog box
{
// obtain dialogs window handle
HWND hwnd = (HWND)hWnd;
// Initialize COM for this thread...
CoInitialize(NULL);
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Word.Application", &clsid);
// do other Automation stuff and clean afterwards
}
In dialog box, this is the snippet for button handler:
case IDOK:
{
// create thread
DWORD threadID;
HANDLE threadHandle = CreateThread( NULL , 0 ,
(LPTHREAD_START_ROUTINE)TabelaSvihObjekata ,
(void*)hwnd , 0 , &threadID );
if( !threadHandle )
{
MessageBox( hwnd, L"Error", L"Error", MB_ICONERROR );
EndDialog( hwnd, IDCANCEL );
}
CloseHandle( threadHandle );
}
And this is the problematic handler:
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
I have looked on MSDN for a clue, and have found only ExitThread as a solution, but I don't know how to use it properly since I am inexperienced with threads.
Browsing through SO archive, I have found some examples in C# where people introduce boolean variable and test it's value in while loop, so they can determine whether to abort the thread or let it work.The other way was suggested, where thread is placed in separate process and then killed.
My question is:
What should I add or change, so when I close the dialog box, Word application closes along with threads destruction ?
If there is anything else that I can do to help, ask and I will gladly do it.
Thanks to everybody who tries to help.
If you use WinApi you have to make threadhandle accesible by other part of code.
Then to terminate your thread you can use ExitThread - this is preferred option by MSDN. I show you how you can use it:
DWORD threadID;
HANDLE hthread;
void TerminateYourThread()
{
DWORD exitCode;
if(GetExitCodeThread(hThread,&exitCode) != 0) // check if your thread is active
{
ExitThread(exitCode); // terminating thread
if(CloseHandle(hThread)) // closing handle
{
//
}
}
}
void CreateYourThread()
{
hThread = CreateThread( NULL , 0 ,
(LPTHREAD_START_ROUTINE)TabelaSvihObjekata ,
(void*)hwnd , 0 , &threadID );
}
Now when you want to terminate the thread just call TerminateYourThread function. It waits until thread is closed. This is only suggestion not finally solution so you can refactor it in the future.
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.