Previously, I asked this question about returning the user to the previous window they were using, but I have found the solution, but it has created another problem. The function that determines the previous window will sometimes work (if a certain set of events happen that I cannot recreate at will, but happens randomly). At some point it goes 2 windows back, sometimes 1 window back. It does this because the taskbar is in the way first, and then there is the window.
So, in order to fix this, I have determined that I should keep going through the previous windows until I get to a window that is not the taskbar, however, I do not know how to determine the IntPtr of the taskbar.
I have tried:
IntPtr taskBarWnd = FindWindow("Shell_TrayWnd", null);
But it doesn't seem to work. If I call MessageBox.Show(taskBarWnd.ToInt64().ToString()), I get 131258, but when I do:
IntPtr thisWnd = GetForegroundWindow();
IntPtr lastWnd = GetWindow(thisWnd, 2);
It is referencing the taskbar, but when I call the same MessageBox function above, it returns 131260 (65774 if the icon is within the hidden icons area).
Does anyone know if there is anything else I can try to determine if the lastWnd I have calculated is the taskbar? I would prefer not to have something that checks if the IntPtr's are close to each other instead of being equal.
I can't check right now since this is my iPad, but I'm pretty sure the taskbar windows all use custom window classes so you should be able to call GetClassName and figure out from the result what your handle refers to.
Related
In my C# application, I am trying to prevent a crash in my application, basically, I am using a Console window as a log display window. I already solved the "Close Button" issue by disabling the close window, and I show/hide the menu with Show and Hide calls, all of that is working just fine.
My final hurtle is if Text Selection is active and the window attempting to be hidden.
I either need to:
A. Kick the window out of select mode. (not sure how I would do this, since selection pauses all output.)
B. Disable the "Edit" menu in the same way I disabled the Close menu, in a hope that it would also disable the mouse selection, but I have yet to find any way to remove the "Edit" menu, and I am not even sure that would prevent mouse selection.
C. This seems the most obvious, disable mouse selection, and this is the one I have currently in my code, but it's not working, so I am not sure what I am missing.
The Code in Question:
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
....
[DllImport("Kernel32.dll", ExactSpelling = true)]
private static extern int SetConsoleMode(IntPtr hWnd, int wFlag);
....
public static void HideConsoleLog () {
if (handle != IntPtr.Zero) {
SetConsoleMode(handle, 0x0080); // ENABLE_EXTENDED_FLAGS 0x0080
ShowWindow(handle, (int)WinCntrlOpt.SW_HIDE);
LogVisible = false;
}
}
According to the Documentation:
ENABLE_EXTENDED_FLAGS 0x0080 Required to enable or disable extended
flags. See ENABLE_INSERT_MODE and ENABLE_QUICK_EDIT_MODE.
And...
ENABLE_QUICK_EDIT_MODE 0x0040 This flag enables the user to use the
mouse to select and edit text.
To enable this mode, use ENABLE_QUICK_EDIT_MODE |
ENABLE_EXTENDED_FLAGS. To disable this mode, use ENABLE_EXTENDED_FLAGS
without this flag.
So, it seems I am doing this correctly, and it does run, but it does not prevent mouse selection. I do know that not to long ago, Microsoft tinkered with Consoles, and I don't know if that's broke this or not.
There is a similar question on stack exchange, but that nether gives a working solution, and is in-fact the reverse to what I am trying to do and in C or C++. I can interpolate, but it's basically what I am already trying here.
It seems to be the same suggestion, use ENABLE_EXTENDED_FLAGS without any other flags to disable the option, and that's what I have. But it doesn't work.
So, I am either scratching at the wrong solution or am not doing something else correctly.
The effect is, while the console window is open, you can use the mouse to select text which automatically puts the console in that "select mode" that Microsoft's tinkering added. That's not a problem, but it's when it's left IN that mode, and you select "Show Log" (a check-marked toggle menu option in the main application window) The console window does indeed hide, but then the main window locks up and the application crashes. (my guess is because the window is blocked by that select mode.) It doesn't crash when the 'select mode' is not active.
I would also like to try this from the same documentation:
ENABLE_MOUSE_INPUT 0x0010 If the mouse pointer is within the borders
of the console window and the window has the keyboard focus, mouse
events generated by mouse movement and button presses are placed in
the input buffer. These events are discarded by ReadFile or
ReadConsole, even when this mode is enabled.
Problem is, it says:
When a console is created, all input modes except ENABLE_WINDOW_INPUT
are enabled by default.
So, how do I disable it?
I am fine with any solution that either kicks it out of select mode, or prevents it to begin with, and I have here what I have tried.
When the "Show Log" menu option is selected, the window is going to minimize.
Before the window minimizes, you have to check whether the user has selected any input to prevent the crash. You can check whether the user is selecting anything by using GetConsoleSelectionInfo.
The CONSOLE_SELECTION_INFO out parameter should be equal to 0x00, and if it's not, you need to process the selection. As the documentation from GetConsoleMode/SetConsoleMode shows, you either need to call ReadFile or ReadConsole to discard the selection event that might be going on.
When you've implemented this, it shouldn't crash anymore.
I have a C# application that I am kicking off a separate win32 window from. I'm marshaling Win32 api calls into my C# application and trying to control the win32 from that. I'm toggling the window from a button press to be either maximized on top of all windows always or minimized.
It will show maximized, then ignore the first button press to hide, it then stays showing when I press the button again, then finally hides once I press the button a 3rd time with the same code. It behaves as desired from here on out. I'm sure it must be some Windows flags that aren't set properly until it goes through a maximize/minimize cycle first.
Here is the life cycle of the window:
Create window maximized
A:
Hide:
target_hwnd = FindWindowHandleByCaption()
ShowWindow(target_hwnd, SW_SHOWMINIMIZED);
Show:
target_hwnd = FindWindowHandleByCaption()
ShowWindow(target_hwnd, SW_SHOWMAXIMIZED);
Goto A:
What am I doing wrong? I've been at this for hours trying various code snippets to no avail.
Thanks in advance!
EDIT:
Solved my problem by just doing:
SendMessage(target_hwnd.ToInt32(), WM_SYSCOMMAND, SC_MINIMIZE, 0);
SendMessage(target_hwnd.ToInt32(), WM_SYSCOMMAND, SC_MAXIMIZE, 0);
Instead of using ShowWindow.
Per the ShowWindow() documentation:
nCmdShow
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.
I will start by saying that my experience with pinvoke is extremely limited. I know very little of C++, too. But here is my situation...
I have a program in C# that uses pinvoke to get functions like SendMessage and FindWindow and the like from C++. It looks for the name of the window, gets the handles, etc. This works fine right now. I will put the code down a bit lower.
Now I have a situation where I want to find a "button" on one of the child windows. I have the IntPtr of the Actual Child Window, at least I think it is. I am very confused about what is a window and what isn't.
I am trying to use EnumChildWindows and FindWindowByCaption and FindWindowEx etc to go through all of the children and find this button. I can get its "caption" with Spy++, but giving that to the FindWindowByCaption does not yield anything.
This is the code that actually finds the parent window. I am sorry if it is confusing, it is being run against very old software.
This is all in C#, using pInvoke. I try to first get the button I want with this method..
FindWindowByCaption(IntPtr.Zero, "{{Caption as shown by Spy++}}");
This doesn't work, even though I give it the right caption. So I try this... In this example, Handle is the IntPtr of the parent window that contains the button I want to find.
// create a small counter so that we do not try to check
// too many windows and crash the program.
int i = 0;
var previous = new Window();
var current = new Window();
do {
current =
new Window(
InteropServices.FindWindowEx(
Handle,
previous.Handle,
null, null
)
);
if (current == IntPtr.Zero)
break;
// add current to list
// continue
previous = current; // never finds the right button, either.
++i;
} while (i < 500);
But even at this, the control I want is never found. Can anyone help me with this? It is getting very frustrating. I can see the Caption in Spy++, I can see that it has a handle. It does not appear to be anything special.
You need to do the following:
Call FindWindow to find the top-level window. Use either class name, or window title, or both, to identify it.
Call FindWindowEx repeatedly to find child windows. Pass the parent window as hwndParent and NULL as hwndChildAfter. Again use either class name, or window title, or both, to identify the child window.
Eventually you will descend the parent/child window hierarchy until you reach your target window.
So, for an example, consider the following hierarchy, using Spy++ notation:
Window 00001000 "My MainForm window title" "MainFormWindowClass"
|
-- Window 00002000 "Panel container" "PanelWindowClass"
|
-- Window 00003000 "Click me!" "ButtonWindowClass"
You would find this with the following sequence of calls:
HWND main = FindWindow("MainFormWindowClass", NULL); // just use class name
HWND panel = FindWindowEx(main, NULL, "PanelWindowClass", "Panel container");
HWND button = FindWindowEx(panel, NULL, "ButtonWindowClass", "Click me!");
Note that I've written this in C++ to avoid confounding things with p/invoke. And I've also omitted all error checking for the sake of a clean example. You obviously would check for errors.
I suggest that you keep Spy++ at hand and try to write a simple C++ console application that locates your window. Once you know the sequence of calls to FindWindow and FindWindowEx that do the job, translate them to C#.
I need an underlying process to gather information about other applications used by Windows. I suspect this would be done using WAPI hooks. What I wish to do is for my program to detect when windows changes focus from one program to another and tell me which one currently has focus.
First I need an event that triggers each time Windows swaps focus between two applications. All events I've found so far only handles changes made from or to the program it's being used by, but I need to find all focus-changes, even if it's between two other programs.
I'll also need a function that gives me the window in focus. Would this work, or is this only internally (windows within the current application and not other programs)?
Cheers
Depending on how accurate you need your focus change detection system to be you might be able to get away with a service that polls for the foreground window using the API function you described GetForegroundWindow (yes this is system-wide, not process specific).
You can then use the handle of that function to determine which process is the current active/focused process. Then retrieve the focused element (child window) of that process..
HWND hwnd = GetForegroundWindow();
DWORD remoteThreadId = GetWindowThreadProcessId(hwnd, NULL);
DWORD currentThreadId = GetCurrentThreadId();
AttachThreadInput(rThreadId, curThreadId, TRUE);
HWND focusElement = GetFocus();
AttachThreadInput(rThreadId, curThreadId, FALSE);
Keep doing this.. and do whatever you need to do with focusElement
UPDATE
Well, apparently, as #Kenneth K. posted in a comment, there is a global EVENT_SYSTEM_FOREGROUND event that you can hook so that you application gets notified when the foreground (focused) window changes. This way you don't need to loop continuously to detect these changes.
EVENT_SYSTEM_FOREGROUND = 3;
WINEVENT_OUTOFCONTEXT = 0;
You can follow the example in this answer to see how to hook this event and get the notifications. Then whenever the foreground (focused) window changes you can hook that window's message loop and look form focus changed events withing that window using the SetWindowsHookEx function.
Another options is to consult the list of system events on MSDN and see if there is one you can use instead of the EVENT_SYSTEM_FOREGROUND, or along with it to detect control-level focus events. Perhaps the EVENT_OBJECT_FOCUS might be useful.
Please let me know if this is still unclear..
I want to know if it is possible to acquire the desktop's Form. I have tried to get the hWnd from the desktop and use Form.FromHandle to get the form. But it always returns null. So I assume this is not possible; if it is possible can someone show me an example code.
Here is the code that did not work below:
hWnd = GetDesktopWindow();
desktop = Form.FromHandle(hWnd) as Form;
System.Diagnostics.Debugger.Break();
P.S. Can someone also explain what I did wrong here.
You can't do this because the desktop window isn't a Form. FromHandle() tries to find the managed Control (in this case a Form) that corresponds to the given window handle. Since no such Control exists, it returns null.