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.
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 am attempting to disable a window under certain conditions using EnableWindow(hWnd, false);
According to the documentation, this should "disable mouse and keyboard input to the specified window".
The problem I am seeing is that it does, in fact, disable like it says, except if the cursor is currently inside of a text box in the window and the window has the focus that does not get disabled. I was thinking of doing some sort of code to take the focus off of the window as well.
Is there a better way to go about this?
Note: The window being disabled is a binary ran via _spawnl().
I'm not sure if this is a Windows feature or a bug. Either way, disabling the foreground window is not a good idea.
If you are able to modify the program you start with _spawnl() then that is a better solution. You could make it respond to WM_APP or something like that when you need to control it.
If it is a 3rd-party application then you are left with hacks.
You could try to change the foreground window with SetForegroundWindow but this will only work if you do it very soon after _spawnl() before your thread loses the foreground lock. Using LockSetForegroundWindow before _spawnl() might be able to help you keep the lock longer. There are also various other hacks to change the foreground with AttachThreadInput etc.
If you don't want to change the foreground I was able to come up with a workaround:
ShellExecute(NULL, NULL, TEXT("Notepad"), NULL, NULL, SW_SHOW);
Sleep(2000);
HWND hNP = FindWindow(TEXT("Notepad"), NULL);
Sleep(2000); // Start typing in Notepad now...
if (hNP)
{
DWORD tid = GetWindowThreadProcessId(hNP, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(gti);
if (tid && GetGUIThreadInfo(tid, >i))
{
HWND hChild = NULL;
if (gti.hwndFocus != hNP && gti.hwndFocus)
{
EnableWindow(hChild = gti.hwndFocus, false);
}
if (GetForegroundWindow() == hNP)
{
SendNotifyMessage(hNP, WM_ACTIVATE, WA_INACTIVE, NULL);
SendNotifyMessage(hNP, WM_ACTIVATE, WA_ACTIVE, NULL);
SendNotifyMessage(hNP, WM_SETFOCUS, NULL, NULL);
// SendNotifyMessage(hNP, WM_NCACTIVATE, false, NULL); // Uncomment to make it look like it is inactive
}
EnableWindow(hNP, false);
if (hChild)
{
EnableWindow(hChild, true);
}
}
MessageBox(NULL, TEXT("Done?"), NULL, MB_TOPMOST);
SetForegroundWindow(hNP);
PostMessage(hNP, WM_CLOSE, 0, 0);
}
This is certainly not optimal, it leaves Notepad in a state where it looks like it is enabled but it is really not. The idea is to disable the focused child window and trigger a fake activation change and forcing the focus to change. It might not work with other applications, who knows.
If you are willing to risk a deadlock you can do this instead:
DWORD tid = GetWindowThreadProcessId(hNP, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(gti);
if (tid && GetGUIThreadInfo(tid, >i))
{
if (GetForegroundWindow() == hNP)
{
if (AttachThreadInput(GetCurrentThreadId(), tid, true))
{
SetFocus(NULL);
AttachThreadInput(GetCurrentThreadId(), tid, false);
}
}
EnableWindow(hNP, false);
}
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 am just learning to create a gui using the winapi, but i have run into an issue. I can create a window like this
#include "stdafx.h"
#include <windows.h>
int main()
{
HWND hwnd = CreateWindow(L"STATIC",NULL,WS_VISIBLE|WS_SYSMENU|WS_CAPTION,0,0,600,600,NULL,NULL,NULL,NULL);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_gettch();
}
But the windows will not close when the close button is clicked, and the window can not be dragged around or moved. I was wondering how i would enable these features of the window.
Static windows are not there for normal windows, you should try and look up how to register and handle your own class with RegisterWindowEx then use the same class name to create a window. You have to have your own window procedure in order to handle messages.
All window classes registered by the system run their own default window procudure and as far as I know none of them handle WM_CLOSE ( that is the close button ) this is why you can't close it.
For you main windows always use something like WS_OVERLAPPEDWINDOW so it'll be clear if it's okay or not and from that eliminate the flags you don't need.
How you set it up :
WNDCLASSEX wndcls;
HWND hMainWnd;
// Register your own window class
ZeroMemory(&wndcls,sizeof(WNDCLASSEX));
wndcls.cbSize=sizeof(WNDCLASSEX);
wndcls.style=CS_VREDRAW+CS_HREDRAW;
wndcls.lpfnWndProc=&appWndFunc;
wndcls.hInstance=hInstance;
wndcls.hIcon=hMainIcon; // or just LoadIcon(hInstance,MAKEINTRESOURCE(IDI_MAIN_ICON))
wndcls.hIconSm=hMainIcon;
wndcls.hCursor=LoadCursor((HINSTANCE)NULL,IDC_ARROW);
wndcls.hbrBackground=(HBRUSH)COLOR_APPWORKSPACE;
wndcls.lpszClassName="myWndClass";
if (RegisterClassEx(&wndcls)==0)
{
// failed to register class name
return false;
}
// Create window with your own class
hMainWnd=CreateWindowEx(0,\
"myWndClass","widnow title",\
WS_OVERLAPPEDWINDOW|WS_VISIBLE,\
0,\
0,\
250,\
250,\
hMainWnd,NULL,hInstance,NULL);
if (hMainWnd==(HWND)NULL)
{
// failed to create main window
return false;
}
Then your main loop :
bool bAppMainLoop=false
while(!bAppMainLoop)
{
WaitMessage();
while(PeekMessage(&emsg,NULL,0,0,PM_NOREMOVE))
{
if(GetMessage(&emsg,NULL,0,0)==0)
{
bAppMainLoop=true;
break;
}
TranslateMessage(&emsg);
DispatchMessage(&emsg);
}
}
This is a bit more than usual setup, so let me explain , in order to not burn CPU, you wait for a message with WaitMessage, it'll block until something happens, like move window, click, paint etc. PeekMessage will return true if there is a message so calling it in a while loop will make sure it drains the message quene, GetMessage will obtain the message if it returns 0 it means that your app called the PostQuitMessage(0) so a WM_QUIT arrived was found in the message loop that means it's time to break out from the message loop. The rest Translate and Dispatch does what it name says.
Finally you need your own window procedure :
LRESULT CALLBACK appWndFunc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg==WM_CLOSE)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
DefWindowProc is essential that handles all commonly occurring messages from the system, thus you don't need to handle those here. You simply respond to the WM_CLOSE message which is sent when you want to close the window and post a quit message into the message loop that you will catch and exit.
Additional info :
It's not required to release your stuff since windows does that for you so it wont be locked the next time you start your program , but it's a good practice to at least Unregister your window class after your main loop.
Btw that is the wrong main function : WinMain that is the correct one. Plus to avoid even more bugs make sure you compile a windows GUI application.
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.