Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

NEC 2690WUXi2

NEC 2690WUXi2 Купил себе это чудо. Таких цветов не видел еще никогда. На выходных буду играться плотнее, а до того времени - смотрю Отчаянных Домохозяек уже в ужаснейшем качестве :-).

Обзор здесь. Как обычно - хотел купить еще предыдущую модель с A-TW поляризатором, но она снята с производства и на складе у диллера закончилась пару месяцев назад.

How to kill a remote process

Я иногда работаю дома через удаленный рабочий стол. Visual Studio бывает при отладочном соединении к процессу вешает всю систему. Следующей командой я ее прибиваю (иначе - никик): taskkill /s MACHINENAME /U DOMAIN\USER /f /im devenv.exe

Label with vertical auto sizing

В проекте потребовалась лейба с автоподгоном высоты и фиксированной в пределах формы шириной, чтобы работал AutoEllipsis. Проблема в том, что AutoSize меняет также ширину лейбы. Сначала в голову пришла мысль считать высоту текста через Graphics или TextRenderer, но покопавшись в исходниках класса Label я выяснил, что его метод GetPreferredSize возвращает требуемый размер. Приятно то как! В итоге получился такой вот кусочек класса-наследника Label:

/// <summary>
/// See <see cref="Label.OnFontChanged"/>.
/// </summary>
protected override void OnFontChanged(EventArgs e)
{
    base.OnFontChanged(e);

    adjustHeight();
}

/// <summary>
/// See <see cref="Label.OnTextChanged"/>.
/// </summary>
protected override void OnTextChanged(EventArgs e)
{
    base.OnTextChanged(e);

    adjustHeight();
}

#endregion

#region Private methods

/// <summary>
/// Adjust <see cref="Control.Height"/> so that the text is fully visible vertically.
/// </summary>
private void adjustHeight()
{
    if (AutoSize)
    {
        return;
    }
    Height = PreferredSize.Height;
}

Focus and window activation in Win32

A better explanation. (actually, the focus saving feature is specific to dialog boxes created with CreateDialog/DialogBox).
At a lower level.

First, every thread (which represents a logical task on Win32... with its own message queue, and its own set of windows), has two window handles stored somewhere (probably some Thread Local Storage).
These two window handles are:


The active window (which is a popup window). This may be NULL if there is no active window in the current thread. For example, if the last active window has been destroyed, or accepted the deactivation (when recieving the WM_NCACTIVATE message) when switching to another task.
The focused window. It can be NULL (e.g. whenever the active window is NULL) or can be equal to the active window or can be a non-popup child window of the active window.
In some very special cases, very temporarily (e.g. during processing of the WM_ACTIVATE message) the focused window may be a child window of another window that's not currently the active window.


A few terms:

The foreground thread is the threads that currently recieves user input. e.g. keyboard input is sent to the message queue of the foreground thread.
The foreground window is the active window of the foreground thread. Every thread has an active window (NULL or non-NULL)... But there is a single foreground window.

User input is sent to the focused window of the foreground thread.

Note that the active window and focused window may be non-NULL while the current thread is not the foreground thread (i.e. when another task is the active task)...
But, most of the time, the active window and focused window are NULL, in all the threads that aren't the foreground thread.

Nevertheless, it's possible to get a scenario where GetActiveWindow()!=NULL && GetForegroundWindow()!=NULL && GetForegroundWindow()!=GetActiveWindow().


Assume that somebody clicks on an inactive popup window (which may have a non-NULL parent but has the WS_POPUP style) and that the current thread is the foreground thread and has an active window.

The WM_MOUSEACTIVATE message is sent to the clicked window.
This message indicates to the new window with which button the click has been done and in which area of the window (caption, scroll bar, border, menu, client area) the user clicked, and expects a return value indicating whether the window activation is accepted and whether the click must generate a click message on the window:

MA_ACTIVATE Activates the window, and does not discard the mouse message.
MA_ACTIVATEANDEAT Activates the window, and discards the mouse message.
MA_NOACTIVATE Does not activate the window, and does not discard the mouse message.
MA_NOACTIVATEANDEAT Does not activate the window, but discards the mouse message.


If WM_MOUSEACTIVATE returns MA_NOACTIVATE or MA_NOACTIVATEANDEAT, everything is canceled (but the click message is recieved by the inactive window in the case of MA_NOACTIVATEANDEAT). The old window remains active.

If WM_MOUSEACTIVATE returns MA_ACTIVATEANDEAT or MA_ACTIVATE, the processing continue:
The following processing is equivalent to a call to SetActiveWindow(HandleOfTheNewWindow)

The WM_NCACTIVATE message is sent to the old window to indicate that it will be deactivated (fActive is FALSE).
If this message is handled, the deactivation can be canceled if the window procedure returns FALSE. In that case, the window remains active, and the new one is not activated.
(We'll see later what happens if the user clicks from/to a different thread).

The DefWindowProc returns TRUE (to indicate that the processing should continue) after having re-drawn the window frame to indicate that the window is inactive.
At this point, the action cannot be canceled anymore.

WM_ACTIVATE with fActive=WA_INACTIVE is sent to the old window. It indicates to the window that it's deactivated. The return value is not used.

Then, the active window handle is set to the new window.
The handle of the focused window is not changed. It's still currently the old active window or a child window of it.

Then, WM_NCACTIVATE with fActive=TRUE is recieved by the new window. DefWindowProc draws the title bar. The return value is not used.
Then, WM_ACTIVATE with fActive=WA_CLICKACTIVE is recieved by the new window. The return value is not used. DefWindowProc call SetFocus to sets the focus to the new window itself (not a child window of it).
If you handle this message instead of calling DefWindowProc, you can sets the focus with SetFocus to a child window of this new active window.
If you handle this message but don't change the focus (so the focus is still a child window of the old active window or the old active window itself), the focus will be automatically set to the new active window, just after WM_ACTIVATE is processed.

I tested to see what happens when SetFocus is called on a child window of another window during the processing of WM_ACTIVATE.
The call to SetFocus (which internally calls SetActiveWindow() if the new focused control is not in the currently active window, and then, sets the focused window to the hWnd passed to SetFocus) is immediately treated (messages are pushed on the stack) and activates the parent popup window of the new window with all the messages I described above, then, it sets the focus to the child window, and returns... Then, the old WM_ACTIVATE message processing window is exited... and... the focus is not modified, it remains set to the new child window of the new active window.
So, I think that the activation routine simply checks that, after the WM_ACTIVATE message is processed, if the focused window is not a child window of the active window. If it isn't, it sets the focus to the active window.

So, SetFocus will be called at some time. Either inside WM_ACTIVATE (by user code or by DefWindowProc) or just after it.
SetFocus generates too messages:
WM_KILLFOCUS on the old focused window.
WM_SETFOCUS on the new one.
These two messages are handled by buttons, text boxes, and other basic controls in order to show that the button or text box is selected (dot rectangle for buttons, caret for the text box), but these messages can't be used to cancel the focus change, and, not treating it doesn't change the fact that the new window is focused.

About controls & focus: text boxes and buttons handle the WM_LBUTTONDOWN and WM_LBUTTONDBLCLK messages and, internally call SetFocus(WindowHandleOfTheControl) when they're processed.
Otherwise, DefWindowProc doesn't treat these messages, so that, clicking on a user-defined control doesn't change set the focus on it unless the WM_LBUTTONDOWN message is specially handled.
That's in contrast with window activation which is automatic when the user clicks on the window, unless WM_MOUSEACTIVATE is specially handle to prevents the activation.

Another thing: When returning WM_NOACTIVATE (which doesn't eat the click message) from the WM_MOUSEACTIVATE message processing routine, if a left mouse click is done on the caption, the window is activated... By the default handling (by DefWindowProc) of WM_NCLBUTTONDOWN.
Note also that ALT+TAB or a call to SetActiveWindow() bypass the WM_MOUSEACTIVATE message, but it doesn't bypass the WM_NCACTIVATE deactivation message on the old window.

Second scenario: Mouse click on a window that doesn't belong to the foreground thread while there's a currently active foreground window.

WM_NCACTIVATE with fActive=FALSE is sent to the old window in the old thread.
If FALSE is returned for the WM_NCACTIVATE message processing (DefWindowProc returns TRUE), the active window and focused window of the old thread are not modified, but the window becomes "inactive" from a user point of view as it's not the foreground window anymore, and user input is not sent to it.
Of course, if TRUE is returned for this message, the window becomes inactive too, and additionally the active window and the focused window for this thread become NULL... Moreover, WM_ACTIVATE with fActive==WA_INACTIVE is sent and processed (it's not sent if WM_NCACTIVATE returns FALSE).
Then, if TRUE was returned for WM_NCACTIVATE, WM_ACTIVATEAPP with fActive==FALSE is sent to the old thread, to ALL the popup windows of the thread, including the window that recieved WM_NCACTIVATE.

Now, back to the new thread (the old thread and the new thread are actually running at the same time and recieve messages at the same time):
If this thread has an active window (usually, it won't have any), it recieves the WM_NCACTIVATE message with fActive=TRUE. The returned value is ignored.
If this thread has no active window, this message is not sent.

Then, if the mouse click was done on the active window, no additionnal message is sent.
Otherwise, if there was no active window or if the click was done on another window, a sequence of messages are sent.
This sequence of messages is almost the same as the one for a switch from one window of a task to another window of the same task, with a difference:
WM_MOUSEACTIVATE is sent to the new window of the new task. It can cancel the operation. In that case, the old window of the new task (and it's focused control) remains active. If there was no active window, there is no foreground window in the system... Any keyboard input is dicarded, and GetForegroundWindow() returns NULL.

Then, if the operation has not been cancelled, WM_ACTIVATEAPP with fActive==TRUE is sent to all the popup windows of the new thread, if and only if there was no active window.

Then, WM_NCACTIVATE with fActive==FALSE is sent to the old window of the new task (if there is one). It can cancel the operation. In that case, this window remains the active window (and thus, becomes the foreground window).
If WM_NCACTIVATE succeeds, WM_ACTIVATE with fActive==WA_INACTIVE is sent.
Then, If WM_NCACTIVATE succeeds or if there was no active window, WM_NCACTIVATE with fActive=TRUE and WM_ACTIVATE with fActive=WA_CLICKACTIVE are sent to the new window.
Eventually, if the new window is activated, the focus is changed (either by the processing of WM_ACTIVATE or immediately after it), to the new window or a child window of it. In that case, WM_KILLFOCUS and WM_SETFOCUS messages are sent.

Ok, that's a bit complex.
Here is a simplified description, that explains everything.

Each task (thread) must be seen as independent, and ignores the existence of other tasks (and of the windows of other tasks)...
In that case, things are simplier.
There are four states for a task:
1) There is an active window (and a focused control/window) in the task, but the user keyboard is elsewhere (i.e. the thread is not the foreground thread).
2) There is an active window and the thread is the foreground thread.
3) There is no active window and the thread is not the foreground thread.
4) There is no active window and the thread is the foreground thread.

States 2 and 3 are the most common.

The WM_ACTIVATEAPP message is sent whenever the task goes to/from state 1/2 to state 3/4 through a window activation/deactivation. In other words, when GetActiveWindow() becomes NULL or non-NULL.
The WM_NCACTIVATE message with fActive==TRUE is sent when a task goes from state 1/3 to 2 (i.e. when the task becomes the foreground task and there was an active window in this task).
The WM_MOUSEACTIVATE message is sent whenever an inactive window is clicked on.
The WM_NCACTIVATE message is also recieved when switching between windows of the same task.

With DefWindowProc, when an inactive window is activated, the focus is set to the window itself.

That is not the behavior of dialog boxes created by DialogBox/CreateDialog.
Dialog boxes are normal windows with a particular classes (#32770 on my computer).
Dialog boxes handle a number of messages, such as the ones for managing the "default push button".
Good news. All the dialog box API, and the dialog box window class could be programmed by anybody from the raw Win32 API.
So, there is nothing new to learn, but the high level behavior of the message handling by the WindowProc of dialog boxes (named DegDlgProc).

Simply checking the manual, it's easy to find "Dialog Box Default Message Processing":

WM_ACTIVATE Restores the input focus to the control identified by the previously saved handle if the dialog box is activated. Otherwise, the procedure saves the handle of the control having the input focus.
WM_SETFOCUS Sets the input focus to the control identified by a previously saved control window handle. If no such handle exists, the procedure sets the input focus to the first control in the dialog box template that is visible, not disabled, and has the WS_TABSTOP style. If no such control exists, the procedure sets the input focus to the first control in the template.
WM_SHOWWINDOW Saves the handle of the control having the input focus if the dialog box is being hidden, then calls DefWindowProc to complete the default action.

http://msdn2.microsoft.com/en-us/library/ms644995.aspx#default_messages

Where is it saved?
I listed properties of the dialog box window with EnumProps... There are none.
So, I guess that it's stored in the cbWndExtra extra bytes of memory bound to every window. This area is known (it's named DLGWINDOWEXTRA) and is useful when deriving classes from the dialog boxes class.

Somewhere in DefDlgProc there is probably something like:

case WM_ACTIVATE:
if (LOWORD(wParam)==0) SetWindowLong(hDlg, FOCUS_OFFSET, (LONG)GetFocus());
else SetFocus(GetWindowLong(hDlg, FOCUS_OFFSET));

There is probably a level of indirection...
Ok, here is the thing in Wine (Thanks, Google):
http://source.winehq.org/source/dlls/user32/defdlg.c#L252

Note that, a dialog box window itself, cannot have the focus (if it has at least one control)... Because whenever it's activated it sets the focus to a child control, and, if SetFocus is called on it... It handles the WM_SETFOCUS message to activate a child control.

Can this data being read out?

I didn't find any documented way to read it.

The issue for MDI interfaces is similar.
There is a window class with a window proc which handles a number of messages.
Fortunately, MDI windows supports many messages to manipulate children windows:

WM_MDIACTIVATE
WM_MDICASCADE
WM_MDICREATE
WM_MDIDESTROY
WM_MDIGETACTIVE
WM_MDIICONARRANGE
WM_MDIMAXIMIZE
WM_MDINEXT
WM_MDIREFRESHMENU
WM_MDIRESTORE
WM_MDISETMENU
WM_MDITILE


There is a message to get the currently saved active window:

WM_MDIGETACTIVE

wParam = 0; // not used; must be zero
lParam = (LPBOOL) lpfMaximized; // optional pointer to maximized state flag


An application sends the WM_MDIGETACTIVE message to a multiple document interface (MDI) client window to retrieve the handle of the active MDI child window.

Rollback book

Слушаю бомбовую аудиокнигу Откатчики. Настоятельно рекомендую всем. Book title

Copyright 2007-2011 Chabster