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.
Related
I develop a DAW application for Windows 10. It's a x64 application written in C++ and built by Visual Studio 2019.
The application uses a custom GUI that does not use any Windows APIs but it also has to load VST 2.4 plugins that do use standard Win32 GUI and I open them in modeless popup (non-child) windows.
The problem I've been trying to solve is a deadlock -- see below.
Disclaimer: I know the code isn't perfect and optimized -- it's a work in progress please.
======== main.cpp =============================
// ...
void winProcMsgRelay ()
{
MSG msg;
CLEAR_STRUCT (msg);
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
};
}
// ...
int CALLBACK WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdL, int nCmdShw)
{
// ...
}
=================================================
1) The WinMain function creates a new thread that will handle our custom GUI (which does not use any Windows API).
2) The WinMain thread uses the standard Windows GUI API and it handles all window messages delivered to our main application window.
The WinMain thread creates our main window by calling CreateWindowEx (with a WNDPROC window procedure callback):
{
WNDCLASSEX wc;
window_menu = CreateMenu ();
if (!window_menu)
{
// Handle error
// ...
}
wc.cbSize = sizeof (wc);
wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = mainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APP);
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = mainWinName;
wc.lpszClassName = mainWinName;
wc.hIconSm = LoadIcon (NULL, IDI_APP);
RegisterClassEx (&wc);
mainHwnd = CreateWindowEx (WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW | WS_EX_CONTEXTHELP,
mainWinName, mainWinTitle,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, 0,
0, 0,
NULL, NULL, hInst, NULL);
// ...
// Then the WinMain thread keeps executing a standard window message processing loop
// ...
while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE) != 0
&& ! requestQuit)
{
if (GetMessage (&msg, NULL, 0, 0) == 0)
{
requestQuit = true;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (! requestQuit)
{
WaitMessage ();
}
}
// ...
}
3) Our custom-GUI thread (spawned above), in addition to its other functions, does the following:
a) Loads a VST audio plugin from a DLL file by calling LoadLibrary.
b) Creates a new thread for the DLL plugin (let's call it "plugin thread") to create a new instance of it (there may be multiple instances of a loaded DLL plugin):
vst_instance_thread_handle = (HANDLE) _beginthreadex (NULL, _stack_size, redirect, (void *) this, 0, NULL);
c) After some time that the plugin instance has been running on its own thread, our custom-GUI thread (in response to a user action in our custom GUI) creates a new thread for the plugin GUI window:
vst_gui_thread_handle = (HANDLE) _beginthreadex (NULL, _stack_size, redirect, (void *) this, 0, NULL);
(Note that the DLL plugin uses standard Win32 GUI.)
When the new plugin GUI thread is being spawned, the function VSTGUI_open_vst_gui is called on the plugin instance thread -- see below:
============ vst_gui.cpp: ====================
// ...
struct VSTGUI_DLGTEMPLATE: DLGTEMPLATE
{
WORD e[3];
VSTGUI_DLGTEMPLATE ()
{
memset (this, 0, sizeof (*this));
};
};
static INT_PTR CALLBACK VSTGUI_editor_proc_callback (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
thread_local AEffect * volatile Vst_instance_ptr = 0;
thread_local volatile int Vst_instance_index = -1;
thread_local volatile UINT_PTR Vst_timer_id_ptr = 0;
thread_local volatile HWND Vst_gui_handle = NULL;
void VSTGUI_open_vst_gui (int vst_instance_index)
{
AEffect *vst_instance = VST_instances [vst_instance_index].vst->pEffect;
Vst_instance_index = vst_instance_index;
Vst_instance_ptr = vst_instance;
VSTGUI_DLGTEMPLATE t;
t.style = WS_POPUPWINDOW | WS_MINIMIZEBOX | WS_DLGFRAME | WS_VISIBLE |
DS_MODALFRAME | DS_CENTER;
t.cx = 100; // We will set an appropriate size later
t.cy = 100;
VST_instances [vst_instance_index].vst_gui_open_flag = false;
Vst_gui_handle = CreateDialogIndirectParam (GetModuleHandle (0), &t, 0, (DLGPROC) VSTGUI_editor_proc_callback, (LPARAM) vst_instance);
if (Vst_gui_handle == NULL)
{
// Handle error
// ...
}
else
{
// Wait for the window to actually open and initialize -- that will set the vst_gui_open_flag to true
while (!VST_instances [vst_instance_index].vst_gui_open_flag)
{
winProcMsgRelay ();
Sleep (1);
}
// Loop here processing window messages (if any), because otherwise (1) VST GUI window would freeze and (2) the GUI thread would immediately terminate.
while (VST_instances [vst_instance_index].vst_gui_open_flag)
{
winProcMsgRelay ();
Sleep (1);
}
}
// The VST GUI thread is about to terminate here -- let's clean up after ourselves
// ...
return;
}
// The plugin GUI window messages are handled by this function:
INT_PTR CALLBACK VSTGUI_editor_proc_callback (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
AEffect* vst_instance = Vst_instance_ptr;
int instance_index = Vst_instance_index;
if (VST_instances [instance_index].vst_gui_window_handle == (HWND) INVALID_HANDLE_VALUE)
{
VST_instances [instance_index].vst_gui_window_handle = hwnd;
}
switch(msg)
{
case WM_INITDIALOG:
{
SetWindowText (hwnd, String (tmp_str) + VST_get_best_vst_name (instance_index, false));
if (vst_instance)
{
ERect* eRect = 0;
vst_instance->dispatcher (vst_instance, effEditGetRect, 0, 0, &eRect, 0);
if (eRect)
{
// ...
SetWindowPos (hwnd, HWND_TOP, x, y, width, height, SWP_SHOWWINDOW);
}
vst_instance->dispatcher (vst_instance, effEditOpen, 0, 0, hwnd, 0);
}
}
VST_instances [instance_index].vst_gui_open_flag = true;
if (SetTimer (hwnd, (UINT_PTR) Vst_instance_ptr, 1, 0) == 0)
{
logf ("Error: Could not obtain a timer object for external VST GUI editor window.\n");
}
return 1;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint (hwnd, &ps);
EndPaint (hwnd, &ps);
}
return 0;
case WM_MOVE:
if (Vst_instance_index >= 0)
{
VST_instances [Vst_instance_index].vst_gui_win_pos_x = VST_get_vst_gui_win_pos_x (Vst_instance_index);
VST_instances [Vst_instance_index].vst_gui_win_pos_y = VST_get_vst_gui_win_pos_y (Vst_instance_index);
}
return 0;
case WM_SIZE:
if (Vst_instance_index >= 0)
{
VST_instances [Vst_instance_index].vst_gui_win_width = VST_get_vst_gui_win_width (Vst_instance_index);
VST_instances [Vst_instance_index].vst_gui_win_height = VST_get_vst_gui_win_height (Vst_instance_index);
}
return 0;
case WM_TIMER:
if (vst_instance != NULL)
{
vst_instance->dispatcher (vst_instance, effEditIdle, 0, 0, 0, 0);
}
return 0;
case WM_CLOSE:
// ...
return 0;
case WM_NCCALCSIZE:
return 0;
default:
return (DefWindowProc (hwnd, msg, wParam, lParam));
}
return 0;
=================================================
Our custom-GUI thread, too, periodically calls winProcMsgRelay (); Sleep (1); in a loop.
Why multi-threaded? Because: 1) this is a real-time audio-processing application where near-zero latencies are required, and 2) we need to set CPU priorities and stack sizes independently for each thread, based on their real needs. Also, 3) having multi-threaded GUI allows our DAW app to remain responsive when the plugin or its GUI becomes unresponsive and 4) we make us of multi-core CPUs.
Everything is working well. I can open multiple instances of multiple plugins. Their GUI windows can even spawn other windows showing progress bars, all that without any deadlock.
However, the problem is that I get a deadlock when I click the app logo in a plugin GUI window (Absynth 5 and Kontakt 6 by Native Instruments), which apparently creates a child modal window, which, by the way, displays correctly and fully.
But both this modal window and the parent GUI window stop responding to user actions and window messages -- they "hang" (our custom GUI keeps working well, though). The same thing happens when the plugin GUI displays a standard Windows modal MessageBox on error, where the MessageBox is completely "frozen".
When I set a debugger breakpoint in VSTGUI_open_vst_gui in the second loop that calls winProcMsgRelay, I can determine that this is the place where it hangs, because when I get the deadlock state, that breakpoint is never triggered.
I know that modal dialogs have their own message loop that might block ours, but how should I redesign my code to accommodate for that?
I also know that SendMessage and the like are blocking until they get response. That's why I use the asynchronous PostMessage, instead.
I confirmed that the deadlock occurs in 32-bit builds of the application, too.
I've been trying to trace the cause for several weeks. I believe I've done all my homework and I honestly don't know what else to try. Any help would be greatly appreciated.
There is a lot of code not appearing here (e.g. winProcMsgRelay) and I will admit I'm finding it difficult to get a mental picture of how this works, but let me offer you some general advice and some things to keep in mind.
First of all, modal dialogs have their own message loop. As long as they are up, your message loop will not run.
Second of all, windows functions like SetWindowPos SetWindowText actually send a message to the window. Are you calling those from the thread that that created the window? Because if not, that means that the calling thread will block while the OS sends the message to the window and waits for a response. If the thread that created those windows is busy, the sending thread will remain blocked until it is not.
If I were attempting to debug this, I would simply wait until it deadlocks, then break into the debugger and bring up the threads and call stacks windows next to each other. Switch context among the threads in the threads windows (double click on them) and look at the resulting thread call stacks. You should be able to spot the problem.
Ok, I was able to resolve the deadlock myself. The solution was to rewrite the code so as to unify the window proc handlers (VST GUI messages are handled by the same callback function as the main window messages). Moreover, unlike the official VST SDK, which uses DialogBoxIndirectParam to create the plugin window, I now use CreateWindowEx, instead (not sure if this contributed to solving the deadlock issue, though). Thanks for the comments.
I want to modify a DirectX-Application's behavior (namely I want to implement a program similar to the Statman-Application by OrfeasZ [https://github.com/OrfeasZ/Statman/releases] as Onscreen-Info for Hitman 2) by injecting code (as DLL) into it and hooking the DirectX DeviceInterface VMT.
Since there are very limited resources on how to do this for DirectX11-Applications, I first wanted to learn how to do this in DX9 by creating a program that gets the DeviceInterface pointer of any DirectX9-Application. I wrote this code in the DllMain() function of my DLL (which is almost a 1:1 copy/paste of the third answer to this thread Hooking DirectX EndScene from an injected DLL):
HMODULE hDLL = GetModuleHandleA("d3d9");
LPDIRECT3D9(__stdcall*pDirect3DCreate9)(UINT) = (LPDIRECT3D9(__stdcall*)(UINT))GetProcAddress(hDLL, "Direct3DCreate9");
LPDIRECT3D9 pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
HRESULT hRes = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
//WNDPROC TempWndProc;
WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC, WndProc,0L,0L,GetModuleHandle(NULL),NULL,NULL,NULL,NULL,TEXT("1"),NULL };
RegisterClassEx(&wc);
HWND hWnd = CreateWindow(TEXT("1"), NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
IDirect3DDevice9 * ppReturnedDeviceInterface;
hRes = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &ppReturnedDeviceInterface);
pD3D->Release();
DestroyWindow(hWnd);
if (pD3D == NULL) {
//printf ("WARNING: D3D FAILED");
return false;
}
unsigned long* pInterface = (unsigned long*)*((unsigned long*)ppReturnedDeviceInterface);
When I inject the DLL into a DirectX9-Application (I've tried this with Civilization V and Total War: Shogun 2), it opens a window, so it actually is able to get the Direct3DCreate9 function from the d3d9.dll within the game, but pD3D->CreateDevice() always returns `D3DERR_INVALIDCALL. I don't really get what could be the reason for this, especially since the rest of this program works flawlessly. Does anybody have any idea what is missing/wrong?
D3DERR_INVALIDCALL
The method call is invalid. For example, a method's parameter may not
be a valid pointer.
Based on the error information this issue may caused by invalid parameter of IDirect3D9::CreateDevice method. You need initialize the pointer:
IDirect3DDevice9 *pReturnedDeviceInterface = NULL;
Also check if hWnd is a valid window handle and d3ddm.Format etc.
I am embedding Windows Media Player in a C program. I found the WMP Host example in C++ in the WMP SDK. It contains an even dispatcher. But when I receive an event, how do I know who sent the event and how do I access that class object's variables? For example, I want to set a class member (variable) or call a method.
The CWMPHost object creates the window containing the WMP object and creates the event object. The minimal code is:
LRESULT CWMPHost::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
AtlAxWinInit();
CComPtr<IAxWinHostWindow> spHost;
CComPtr<IConnectionPointContainer> spConnectionContainer;
CComWMPEventDispatch *pEventListener = NULL;
CComPtr<IWMPEvents> spEventListener;
HRESULT hr;
RECT rcClient;
m_dwAdviseCookie = 0;
// create window
GetClientRect(&rcClient);
m_wndView.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0);
// load OCX in window
hr = m_wndView.QueryHost(&spHost);
hr = spHost->CreateControl(CComBSTR(_T("{6BF52A52-394A-11d3-B153-00C04F79FAA6}")), m_wndView, 0);
hr = m_wndView.QueryControl(&m_spWMPPlayer);
// start listening to events
hr = CComWMPEventDispatch::CreateInstance(&pEventListener);
spEventListener = pEventListener;
hr = m_spWMPPlayer->QueryInterface(&spConnectionContainer);
// See if OCX supports the IWMPEvents interface
hr = spConnectionContainer->FindConnectionPoint(__uuidof(IWMPEvents), &m_spConnectionPoint);
if (FAILED(hr))
{
// If not, try the _WMPOCXEvents interface, which will use IDispatch
hr = spConnectionContainer->FindConnectionPoint(__uuidof(_WMPOCXEvents), &m_spConnectionPoint);
}
hr = m_spConnectionPoint->Advise(spEventListener, &m_dwAdviseCookie);
return 0;
}
The full sample code can be found at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/WMP/cpp/WMPHost
EDIT: My improved solution that meets my needs:
First I untangled the include dependencies and applied include guards against circular includes. The CWMPHost class definition doesn't need to know about the CWMPEventDispatch class; however its implementation does, so the CWMPEventDispatch.h class definition is included in the CWMPHost.cpp file but not in the CWMPHost.h file.
This allows to define a member of CWMPEventDispatch to be a pointer to the owning CWMPHost object:
CWMPHost *pCWMPHost;
It is set in the CWMPHost::Create method:
hr = CComWMPEventDispatch::CreateInstance(&pEventListener);
pEventListener->pCWMPHost= this;
Now the event dispatcher can access the methods and members of the CWMPHost object that created the dispatcher.
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;
}
I've got a function:
HWND createMainWindow(P2p_Socket_Machine * toSend){
HWND hMainWnd = CreateWindow(
L"Class",/*(LPCWSTR) nameOfConference.c_str()*/L"Chat", WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, 0, 600,400,
(HWND)NULL, (HMENU)NULL,
/*(HINSTANCE)hlnstance*/NULL, NULL
);
if (!hMainWnd) {
MessageBox(NULL, L"Cannot create main window", L"Error", MB_OK);
return 0;
}
CreateWindowA("LISTBOX",NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY|LBS_MULTIPLESEL,310,30,255,275,hMainWnd,(HMENU)List_Box,NULL,NULL);
CreateWindowExA(NULL,"BUTTON", "Refresh", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,310,100,24,hMainWnd,(HMENU)Button_Refresh, NULL ,NULL);
CreateWindowExA(NULL,"BUTTON", "Send", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,334,100,24,hMainWnd,(HMENU)Button_Send, NULL ,NULL);
CreateWindowExA(NULL,"BUTTON", "New", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,354,100,24,hMainWnd,(HMENU)Button_New, NULL ,NULL);
CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL|WS_DISABLED,
10,30,265,275,hMainWnd,(HMENU)Text_Box_Get,NULL,NULL);
CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL,
10,320,265,45,hMainWnd,(HMENU)Text_Box_Send,NULL,NULL);
SetWindowLongPtr(hMainWnd,GWLP_USERDATA,(LONG_PTR)toSend);
ShowWindow(hMainWnd, SW_SHOW);
//UpdateWindow(hMainWnd);
return hMainWnd;
}
And this is main part of my program:
int WINAPI WinMain(HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int
nCmdShow)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MyFunc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hlnstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Class";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
HWND toSend = createMainWindow(P2pSocket);
//some code
hThread = CreateThread(NULL, 0, ClientThread,
Message2, 0, &dwThreadId);
if (hThread == NULL)
{
cout<<"Create thread filed";
exit(10);
}
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
When i call function createMainWindow() in main part of my program
it works as it should, but when i run it in
my thread(ClientThread) it doesn't work. i've read that i should create windows only in main thread. Is it true? And if it's true, what is the simplest way to call this function from another thead to be done in main thread?
Thanks everyone. Now i know the problem, but i'm stuck with solution.
My client thread code is:
while(1){
vector<HWND> AllHandlers;
pair<string,string> Answer = Pointer->receiveMsgByUdp();
if(!Pointer->isMyLocalAddress(Answer.first)){
int type = messageUdpContentType(Answer.second);
switch(type){
case 0 :
Pointer->sendMsgToIpUdp(Answer.first,"<?xml version='1.0'?><accepted/>");
AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR)
SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str());
break;
case 1 :
AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR)
SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str());
break;
case 2 :
AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box);
for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++)
if((i = SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str()))!=LB_ERR)
SendMessageA(*j,LB_DELETESTRING, 0, (LPARAM)Answer.first.c_str());
break;
case 3 :
userReply = MessageBoxW(NULL, L"Принять приглашение на конференцию?",
L"", MB_YESNO | MB_ICONQUESTION);
if (userReply==IDYES){
//todo: Проверка на создание встречи, в которой уже состоишь
string nameOfConf = fetchNameOfInviteConf(Answer.second);
Pointer->createConference(nameOfConf);
HWND toSendTo = createMainWindow(Pointer);
Pointer->setHandlerInfo(nameOfConf,toSendTo);
Pointer->addNewMemberToConference_ServerType(nameOfConf,Answer.first);
string toSend = string("<?xml version='1.0'?><inviteAccepted>") + nameOfConf + string("</inviteAccepted>");
Pointer->sendMsgToIpUdp(Answer.first,toSend);
}
break;
case 4 :
string nameOfConf = fetchNameOfInviteAcceptConf(Answer.second);
toSend.clear();
Participants.clear();
Participants = Pointer->getCurrentParticipants(nameOfConf);
toSend+="<?xml version='1.0'?>";
toSend+="<conference>";
toSend+="<nameOfConference>";
toSend+=nameOfConf;
toSend+="</nameOfConference>";
for(vector<string>::iterator i = Participants.begin();i!=Participants.end();i++){
toSend+="<participant>" + *i + "</participant>";
}
toSend+="</conference>";
Pointer->addNewMemberToConference_ClientType(nameOfConf,Answer.first);
Pointer->sendToIpTcp(Answer.first,toSend);
break;
}
the function receiveMsgByUdp() stops this thread until receives message. I apologize for lack of knowledge, but what functions can i use or another stuff to solve this. Should i rewrite my method receiveMsgByUdp() to be asynchronous or how can i call function createMainWindow() to be run on main thread?About the last variant : how can i do this in pure winapi, i couldn't found any simple examples. Can someone give code snippet. Thanks one more time)
You can indeed create windows in threads other than the main UI thread. However, those windows will have affinity to the thread that created them and you will need to run a message pump in each and every thread that creates windows.
So whilst you can do what you ask, Win32 is really designed to work with all windows in a process having affinity to the same thread. There's really nothing to be gained from creating multiple UI threads. All you will succeed in doing is making your life extraordinarily and needlessly complex.
You can create windows on "non-main" threads but be aware that those windows are attached to the creation thread, and you need to make sure to implement a message loop there and keep dispatching messages posted on the queue. If you don't do this, your windows are going to freeze.
See:
Using Messages and Message Queues
Message Handling -- About Messages and Message Queues
The system does not automatically create a message queue for each
thread. Instead, the system creates a message queue only for threads
that perform operations which require a message queue. If the thread
creates one or more windows, a message loop must be provided; this
message loop retrieves messages from the thread's message queue and
dispatches them to the appropriate window procedures.
If you create a window in another thread, you will also need to implement a message loop on that thread since queued messages are posted to the message queue of thread which owns the window.