How to get the z-order in windows? - c#

I'm making an application where I interact with each running application. Right now, I need a way of getting the window's z-order. For instance, if Firefox and notepad are running, I need to know which one is in front.
Any ideas? Besides doing this for each application's main window I also need to do it for its child and sister windows (windows belonging to the same process).

You can use the GetTopWindow function to search all child windows of a parent window and return a handle to the child window that is highest in z-order. The GetNextWindow function retrieves a handle to the next or previous window in z-order.
GetTopWindow: http://msdn.microsoft.com/en-us/library/ms633514(VS.85).aspx
GetNextWindow: http://msdn.microsoft.com/en-us/library/ms633509(VS.85).aspx

Nice and terse:
int GetZOrder(IntPtr hWnd)
{
var z = 0;
for (var h = hWnd; h != IntPtr.Zero; h = GetWindow(h, GW.HWNDPREV)) z++;
return z;
}
If you need more reliability:
/// <summary>
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
/// </summary>
int[] GetZOrder(params IntPtr[] hWnds)
{
var z = new int[hWnds.Length];
for (var i = 0; i < hWnds.Length; i++) z[i] = -1;
var index = 0;
var numRemaining = hWnds.Length;
EnumWindows((wnd, param) =>
{
var searchIndex = Array.IndexOf(hWnds, wnd);
if (searchIndex != -1)
{
z[searchIndex] = index;
numRemaining--;
if (numRemaining == 0) return false;
}
index++;
return true;
}, IntPtr.Zero);
return z;
}
(According to the Remarks section on GetWindow, EnumChildWindows is safer than calling GetWindow in a loop because your GetWindow loop is not atomic to outside changes. According to the Parameters section for EnumChildWindows, calling with a null parent is equivalent to EnumWindows.)
Then instead of a separate call to EnumWindows for each window, which would also be not be atomic and safe from concurrent changes, you send each window you want to compare in a params array so their z-orders can all be retrieved at the same time.

Here is my C# solution:
The function returns the zIndex among the siblings of the given HWND, starting at 0 for the lowest zOrder.
using System;
using System.Runtime.InteropServices;
namespace Win32
{
public static class HwndHelper
{
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
public static bool GetWindowZOrder(IntPtr hwnd, out int zOrder)
{
const uint GW_HWNDPREV = 3;
const uint GW_HWNDLAST = 1;
var lowestHwnd = GetWindow(hwnd, GW_HWNDLAST);
var z = 0;
var hwndTmp = lowestHwnd;
while (hwndTmp != IntPtr.Zero)
{
if (hwnd == hwndTmp)
{
zOrder = z;
return true;
}
hwndTmp = GetWindow(hwndTmp, GW_HWNDPREV);
z++;
}
zOrder = int.MinValue;
return false;
}
}
}

// Find z-order for window.
Process[] procs = Process.GetProcessesByName("notepad");
Process top = null;
int topz = int.MaxValue;
foreach (Process p in procs)
{
IntPtr handle = p.MainWindowHandle;
int z = 0;
do
{
z++;
handle = GetWindow(handle, 3);
} while(handle != IntPtr.Zero);
if (z < topz)
{
top = p;
topz = z;
}
}
if(top != null)
Debug.WriteLine(top.MainWindowTitle);

For get Z-Order implement this function (by using GetWindow Windows API function)
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
static int GetWindowZOrder(IntPtr hWnd)
{
var zOrder = -1;
while ((hWnd = GetWindow(hWnd, 2 /* GW_HWNDNEXT */)) != IntPtr.Zero) zOrder++;
return zOrder;
}
The return value is a zero-based index. Returning -1 indicates an invalid hWnd.
The approach is to go up through the windows to the top. The total number of climbs is the value of the Z-Order.

Related

Is there a way of finding out the MDI child window at the back of the z-order? [duplicate]

I'm making an application where I interact with each running application. Right now, I need a way of getting the window's z-order. For instance, if Firefox and notepad are running, I need to know which one is in front.
Any ideas? Besides doing this for each application's main window I also need to do it for its child and sister windows (windows belonging to the same process).
You can use the GetTopWindow function to search all child windows of a parent window and return a handle to the child window that is highest in z-order. The GetNextWindow function retrieves a handle to the next or previous window in z-order.
GetTopWindow: http://msdn.microsoft.com/en-us/library/ms633514(VS.85).aspx
GetNextWindow: http://msdn.microsoft.com/en-us/library/ms633509(VS.85).aspx
Nice and terse:
int GetZOrder(IntPtr hWnd)
{
var z = 0;
for (var h = hWnd; h != IntPtr.Zero; h = GetWindow(h, GW.HWNDPREV)) z++;
return z;
}
If you need more reliability:
/// <summary>
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
/// </summary>
int[] GetZOrder(params IntPtr[] hWnds)
{
var z = new int[hWnds.Length];
for (var i = 0; i < hWnds.Length; i++) z[i] = -1;
var index = 0;
var numRemaining = hWnds.Length;
EnumWindows((wnd, param) =>
{
var searchIndex = Array.IndexOf(hWnds, wnd);
if (searchIndex != -1)
{
z[searchIndex] = index;
numRemaining--;
if (numRemaining == 0) return false;
}
index++;
return true;
}, IntPtr.Zero);
return z;
}
(According to the Remarks section on GetWindow, EnumChildWindows is safer than calling GetWindow in a loop because your GetWindow loop is not atomic to outside changes. According to the Parameters section for EnumChildWindows, calling with a null parent is equivalent to EnumWindows.)
Then instead of a separate call to EnumWindows for each window, which would also be not be atomic and safe from concurrent changes, you send each window you want to compare in a params array so their z-orders can all be retrieved at the same time.
Here is my C# solution:
The function returns the zIndex among the siblings of the given HWND, starting at 0 for the lowest zOrder.
using System;
using System.Runtime.InteropServices;
namespace Win32
{
public static class HwndHelper
{
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
public static bool GetWindowZOrder(IntPtr hwnd, out int zOrder)
{
const uint GW_HWNDPREV = 3;
const uint GW_HWNDLAST = 1;
var lowestHwnd = GetWindow(hwnd, GW_HWNDLAST);
var z = 0;
var hwndTmp = lowestHwnd;
while (hwndTmp != IntPtr.Zero)
{
if (hwnd == hwndTmp)
{
zOrder = z;
return true;
}
hwndTmp = GetWindow(hwndTmp, GW_HWNDPREV);
z++;
}
zOrder = int.MinValue;
return false;
}
}
}
// Find z-order for window.
Process[] procs = Process.GetProcessesByName("notepad");
Process top = null;
int topz = int.MaxValue;
foreach (Process p in procs)
{
IntPtr handle = p.MainWindowHandle;
int z = 0;
do
{
z++;
handle = GetWindow(handle, 3);
} while(handle != IntPtr.Zero);
if (z < topz)
{
top = p;
topz = z;
}
}
if(top != null)
Debug.WriteLine(top.MainWindowTitle);
For get Z-Order implement this function (by using GetWindow Windows API function)
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
static int GetWindowZOrder(IntPtr hWnd)
{
var zOrder = -1;
while ((hWnd = GetWindow(hWnd, 2 /* GW_HWNDNEXT */)) != IntPtr.Zero) zOrder++;
return zOrder;
}
The return value is a zero-based index. Returning -1 indicates an invalid hWnd.
The approach is to go up through the windows to the top. The total number of climbs is the value of the Z-Order.

Retrieving name and/or text from a WinForms control using the WinAPI (CE)

I am trying to traverse the controls of a WinForms form (Windows CE) and retrieve/match their textual contents and names (that is, the Name property assigned to the WinForms Control object).
I do this from within a different process, thus WinAPI.
For traversing and for retrieving the text I wrote the following code. TextPresent uses DescendantControls for the traversing and Text to retrieve the text. I am fairly confident that the traversing code works correctly already - if not, please let me know. However, all of the controls that are returned have an empty text.
The code can be used as follows:
var foregroundWindow = new WinCEControl(GetForegroundWindow()); // working properly
Assert.That("The Window Title", foregroundWindow.Text); // working properly
Assert.That(manorMenu.TextPresent("Text to find")); // not working although /
// "Text to find" is a value within the control tree of the window.
As for the name, I do not even know how to retrieve it at all. So far my searching was unsuccessful.
public class WinCEControl
{
[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("coredll.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
private const int WM_GETTEXT = 0x000D;
private const uint GW_CHILD = 5;
private const uint GW_HWNDLAST = 1;
private const uint GW_HWNDNEXT = 2;
public WinCEControl(IntPtr handle)
{
this.Handle = handle;
}
public IntPtr Handle { get; private set; }
public string Text
{
get
{
var stringBuilder = new StringBuilder(256);
SendMessage(this.Handle, WM_GETTEXT, stringBuilder.Capacity, stringBuilder);
return stringBuilder.ToString();
}
}
public IEnumerable<WinCEControl> Children()
{
var childHandle = GetWindow(Handle, GW_CHILD);
if (childHandle == IntPtr.Zero)
{
yield break;
}
var lastHandle = GetWindow(childHandle, GW_HWNDLAST);
while (childHandle != lastHandle)
{
yield return new WinCEWindow(childHandle);
childHandle = GetWindow(childHandle, GW_HWNDNEXT);
}
yield return new WinCEWindow(lastHandle);
}
public IEnumerable<WinCEControl> DescendantControls()
{
var nodes = new Queue<WinCEControl>(new[] { this });
while (nodes.Any())
{
var node = nodes.Dequeue();
if (node != this)
{
yield return node;
}
foreach (var n in node.Children()) nodes.Enqueue(n);
}
}
public bool TextPresent(string text)
{
foreach (var descendantControl in DescendantControls())
{
if (descendantControl.Text == text)
{
return true;
}
}
return false;
}
}
My goal is to be able to perform UI tests against a running application on the CE device. If there is a solid C# library to achieve the same which works on Windows CE I am very hear.
Update: As mentioned in my comment I have failed to find a library or framework for automating foreign WinForms processes on the Win CE. I tried the "simple-wince-gui-automation" and it fails to retrieve the texts, too.

Suppress line break in a RichTextBox at certain positions

I need to suppress some linebreaks in a RichTextBox.
For example, consider d6+. There must not be a line break between 6 and +. Basically I'm looking for something like <nobr> in HTML.
So far, I've messed around with inserting \u+FEFF etc (which worked on some machines, but some showed vertical lines, maybe a font problem although windows standard font). I also tried to manipulate the rtf directly, i.e. box.rtf = ... with putting some \zwnbo in there, but I never seem to get it right.
Help much appreciated.
Yes, you can use all of the RichText API with your RichTextBox control.
You may be interested take a look at the following sites:
http://www.pinvoke.net/default.aspx/user32.sendmessage - how to send messages to windows using p/invoke.
You can use the Handle property of the RichTextBox to get the window handle of that control, and then send messages to it.
Also look at these include files shiped with SDK from Microsoft, thaty are not going to be used directly in C#, but these files constains all constants you may have to use, such as WB_ISDELIMITER, WB_CLASSIFY and others.
winuser.h
richedit.h
In the following example I
demonstrate how to use the APIs
provided.
EDIT:
This new sample has code marked as unsafe, but it is better because it does not suffer from the problem of single character string, since I can have a char* parameter and manipulate it. The old sample follows this one:
This is C# code, not C++... to compile it you will have to go to project options and mark the check-box to allow unsafe code to run.
Right click the project -> Properties (Alt+Enter) -> Build -> General -> Allow unsafe code (must be checked)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
NewMethod();
}
unsafe private void NewMethod()
{
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
var lpch2 = lpch;
// in this case, we must find the begining of a word:
for (int it = ichCurrent; it < cch; it++)
{
char ch = *lpch2;
if (it + 1 < cch && lpch2[0] == '-' && lpch2[1] != '-')
return it;
if (lpch2[0] == '\0')
return 0;
lpch2++;
}
}
return 0;
}
}
}
Old Sample Code
The sample consists of a class that inherits from RichTextBox and places a custom handler using EM_SETWORDBREAKPROC. This class will only break lines exactly when over '-' character. Not before, nor after.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
delegate int EditWordBreakProc(string lpch, int ichCurrent, int cch, int code);
int MyEditWordBreakProc(string lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
if (lpch.Length == 0 || lpch == null) return 0;
for (int it = ichCurrent; it < lpch.Length; it++)
{
char ch = lpch[it];
if (ch != '-') return it;
}
}
return 0;
}
}
}
It is just a draft, so you may have to further improve it, so you can achieve your goals.
Place the control in a windows form, and run.
Resize the window and see if this is what you want to do!
You will have to make the seeking of word boundaries... I didn't manage to make it work yet.
I'm not sure, but if RichTextBox is actually wrapping a Windows rich edit control, you might have some luck by reading this:
http://msdn.microsoft.com/en-us/library/hh270412%28v=vs.85%29.aspx
Or more specifically this:
http://msdn.microsoft.com/en-us/library/bb787877%28v=vs.85%29.aspx
I hope this helps.
Here's my solution (based on #Miguel Angelo, but modified and corrected a bit):
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MyNameSpace
{
public class SpaceBreakingRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
AddDelegate();
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
EditWordBreakProc myDelegate;
unsafe private void AddDelegate()
{
if (!this.DesignMode)
{
myDelegate = new EditWordBreakProc(MyEditWordBreakProc);
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(myDelegate));
}
}
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
const int WB_MOVEWORDLEFT = 4;
const int WB_MOVEWORDRIGHT = 5;
const int WB_LEFTBREAK = 6;
const int WB_RIGHTBREAK = 7;
const int WB_LEFT = 0;
const int WB_RIGHT = 1;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == ' ' ? 1 : 0;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else if (code == WB_LEFTBREAK)
{
for (int it = ichCurrent; it >= 0; it--)
{
if (lpch[it] == ' '/* && lpch2[1] != ' '*/)
{
if (it > 0 && lpch[it - 1] != ' ')
return it;
}
}
}
else if (code == WB_RIGHT)
{
for (int it = ichCurrent; ; it++)
{
if (lpch[it] != ' ')
return it;
}
}
else
{
// There might be more cases to handle (see constants)
}
return 0;
}
}
}
Note that you need to keep the delegate method around, or it will crash as it gets collected from the garbage collector (which was a pain to debug).
Basically, this subclass only breaks at spaces, which is good enough for my needs at the moment.
I've quickly tried this, it seems to work:
this.userControl.richTextBox1.LoadFile("C:\\test.rtf");
this.userControl.richTextBox1.Rtf = this.userControl.richTextBox1.Rtf.Replace(#"\par", String.Empty);
this.userControl.richTextBox1.SaveFile("C:\\test2.rtf", RichTextBoxStreamType.RichText);

How can i select a IE tab from its handle

I have the Internet Explorer Handle and i have the tab Handle that i want to select.
How can i select this tab?
I know how to select a tab by Index (Using IEAccessible), but i can't get the tabIndex from the handle. The Spy++ doesn't sort them in order.
There's a way hack way to do it. Not supported, and dependent upon the version of IE as well as the language of the OS.
In an IE instance, the control with the name "Tab Row" is the
thing that holds all the various tabs. Given an IAccessible
corresponding to that thing, it's easy to enumerate the children,
and get the tabs. An automation app can inspect the URL on each
tab, and then call IAccessible.accDoDefaultAction(0), to activate
a tab by URL. That's the basic approach.
But I couldn't figure how to directly get the "Tab Row" control
of an IE instance, from a SHDocVw.WebBrowser COM object, which is
what an app gets out of SHDocVw.ShellWindowsClass. I tried it a
million ways, and finally the simplest way I could find to get it
to work is this: get the WebBrowser COM object, get the HWND from that
object (which is really the HWND numerous levels "up"), then traverse down the OS HWND hierarchy to find the HWND
with the name "DirectUIHWND". From there, walk down the IAccessible
tree, to find the "Tab Row", then select the tab and invoke the
method.
It sounds ugly to describe, and it hurt to code it this way. I
could not figure out a way to do the traversal only in HWNDs or
only in IAccessible. I have no idea why I needed to do both.
In Code
First, get the HWND of the WebBrowser:
SHDocVw.WebBrowser ietab = ... ?
string urlOfTabToActivate = ietab.LocationURL;
IntPtr hwnd = (IntPtr) ietab.HWND;
Then get the HWND of the DirectUI thing in the IE instance that controls that WebBrowser:
var directUi = GetDirectUIHWND(hwnd);
This is the hacky part. The GetDirectUIHWND method is defined like this:
private static IntPtr GetDirectUIHWND(IntPtr ieFrame)
{
// try IE 9 first:
IntPtr intptr = FindWindowEx(ieFrame, IntPtr.Zero, "WorkerW", null);
if (intptr == IntPtr.Zero)
{
// IE8 and IE7
intptr = FindWindowEx(ieFrame, IntPtr.Zero, "CommandBarClass", null);
}
intptr = FindWindowEx(intptr, IntPtr.Zero, "ReBarWindow32", null);
intptr = FindWindowEx(intptr, IntPtr.Zero, "TabBandClass", null);
intptr = FindWindowEx(intptr, IntPtr.Zero, "DirectUIHWND", null);
return intptr;
}
...where FindWindowEx is a dllimport:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
Then, get the IAccessible for that DirectUI thing:
var iacc = AccessibleObjectFromWindow(directUi);
...where, of course, AccessibleObjectFromWindow is a DllImport, with some magic around it:
[DllImport("oleacc.dll")]
internal static extern int AccessibleObjectFromWindow
(IntPtr hwnd, uint id, ref Guid iid,
[In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
private static IAccessible AccessibleObjectFromWindow(IntPtr hwnd)
{
Guid guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible
object obj = null;
uint id = 0U;
int num = AccessibleObjectFromWindow(hwnd, id, ref guid, ref obj);
var acc = obj as IAccessible;
return acc;
}
Ok, then, you walk down the IAccessible tree, to find the "Tab Row". In IE this is the thing that displays all the tabs for the various browser windows.
var tabRow = FindAccessibleDescendant(iacc, "Tab Row");
...where that method is:
private static IAccessible FindAccessibleDescendant(IAccessible parent, String strName)
{
int c = parent.accChildCount;
if (c == 0)
return null;
var children = AccChildren(parent);
foreach (var child in children)
{
if (child == null) continue;
if (strName.Equals(child.get_accName(0)))
return child;
var x = FindAccessibleDescendant(child, strName);
if (x!=null) return x;
}
return null;
}
}
And...
private static List<IAccessible> AccChildren(IAccessible accessible)
{
object[] res = GetAccessibleChildren(accessible);
var list = new List<IAccessible>();
if (res == null) return list;
foreach (object obj in res)
{
IAccessible acc = obj as IAccessible;
if (acc != null) list.Add(acc);
}
return list;
}
Then, get the IAccessible within THAT, and click it:
var tabs = AccChildren(tabRow);
int tc = tabs.Count;
int k = 0;
// walk through the tabs and tick the chosen one
foreach (var candidateTab in tabs)
{
k++;
// the last tab is "New Tab", which we don't want
if (k == tc) continue;
// the URL on *this* tab
string localUrl = UrlForTab(candidateTab);
// same? if so, tick it. This selects the given tab among all
// the others, if any.
if (urlOfTabToActivate != null
&& localUrl.Equals(urlOfTabToActivate))
{
candidateTab.accDoDefaultAction(0);
return;
}
}
....where UrlForTab is....
private static string UrlForTab(IAccessible tab)
{
try
{
var desc = tab.get_accDescription(0);
if (desc != null)
{
if (desc.Contains("\n"))
{
string url = desc.Substring(desc.IndexOf("\n")).Trim();
return url;
}
else
{
return desc;
}
}
}
catch { }
return "??";
}
Whew. I could not find a cleaner, simpler way to do this.

Using AttachConsole, user must hit enter to get regular command line

I have a progaram that can be ran both as a winform, or from command line. If it is invoked from a command line I call AttachConsole(-1) to attach to parent console.
However, after my program ends, the user must hit enter to get back the standard command prompt ("c:\>"). is there a way to avoid that need?
Thanks.
I could wrap it in a cmd file to avoid that issue, but I would like to do it from my exe.
Try adding this line just before your exe exits...
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
Bit of a hack, but best I could find when I encountered that problem.
Here is the safest hack that solves the Enter key problem regardless of whether the console window is in the foreground, background, or minimized. You can even run it in multiple console windows.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace WindowsAndConsoleApp
{
static class Program
{
const uint WM_CHAR = 0x0102;
const int VK_ENTER = 0x0D;
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// Do this first.
AttachConsole(ATTACH_PARENT_PROCESS);
Console.Title = "Console Window - Enter Key Test";
Console.WriteLine("Getting the handle of the currently executing console window...");
IntPtr cw = GetConsoleWindow();
Console.WriteLine($"Console handle: {cw.ToInt32()}");
Console.WriteLine("\nPut some windows in from of this one...");
Thread.Sleep(5000);
Console.WriteLine("Take your time...");
Thread.Sleep(5000);
Console.WriteLine("Sending the Enter key now...");
// Send the Enter key to the console window no matter where it is.
SendMessage(cw, WM_CHAR, (IntPtr)VK_ENTER, IntPtr.Zero);
// Do this last.
FreeConsole();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
}
Rob L's approach is somewhat dangerous as it will send an Enter to the active window. A better approach is to actual send the Enter to the correct process (console).
here is how
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
internal static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
internal static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
internal const int VK_RETURN = 0x0D;
internal const int WM_KEYDOWN = 0x100;
}
--snip--
bool attached = false;
// Get uppermost window process
IntPtr ptr = NativeMethods.GetForegroundWindow();
int u;
NativeMethods.GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
if (string.Compare(process.ProcessName, "cmd", StringComparison.InvariantCultureIgnoreCase) == 0)
{
// attach to the current active console
NativeMethods.AttachConsole(process.Id);
attached = true;
}
else
{
// create new console
NativeMethods.AllocConsole();
}
Console.Write("your output");
NativeMethods.FreeConsole();
if (attached)
{
var hWnd = process.MainWindowHandle;
NativeMethods.PostMessage(hWnd, NativeMethods.WM_KEYDOWN, NativeMethods.VK_RETURN, 0);
}
This solution is build upon the code that is found here:
http://www.jankowskimichal.pl/en/2011/12/wpf-hybrid-application-with-parameters/
It's late to the party and there have been many suggestions over the years, but as I recently just solved this issue myself by stitching together a bunch of information from various posts, I thought I'd post the solution here since it has the most relevant title.
This solution works without using the Enter key or simulating a key press. The only thing I couldn't completely solve is intercepting the Enter from the parent console when your application starts. I think this is impossible because it happens before you get a chance to intercept it; however, there is a reasonable quasi-workaround.
Before diving into the code, here's the sequence of things we need to do:
Attach to the parent console
Capture the text of the current prompt output by the parent console
Clear the parent console's prompt by overwriting it with spaces (not sure it's possible to otherwise prevent this from happening)
Interact with the console as normal
Restore parent console's previous prompt by writing what we captured in #2
This is what it would look like in use:
using System;
using System.Windows.Forms;
public static void Main(string[] args)
{
if (args.Length > 0)
{
using (new ConsoleScope())
{
Console.WriteLine("I now own the console");
Console.WriteLine("MUA HA HA HA HA HA!!!");
}
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
... and now for the code. It's more than I'd like, but this is as succinct as I could make it for a post. May this help others attempting the same thing. Enjoy!
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
public sealed class ConsoleScope : IDisposable
{
const int ATTACH_PARENT_PROCESS = -1;
const int STD_OUTPUT_HANDLE = -11;
readonly bool createdNewConsole;
readonly string prompt;
bool disposed;
public ConsoleScope()
{
if (AttachParentConsole())
{
prompt = CaptureParentConsoleCurrentPrompt();
}
else
{
AllocConsole();
createdNewConsole = true;
}
}
~ConsoleScope() => CleanUp();
public void Dispose()
{
CleanUp();
GC.SuppressFinalize(this);
}
static string CaptureParentConsoleCurrentPrompt()
{
var line = (short)Console.CursorTop;
var length = (short)Console.CursorLeft;
var noPrompt = line == 0 && length == 0;
if (noPrompt)
{
return default;
}
return ReadCurrentLineFromParentConsoleBuffer(line, length);
}
static string ReadCurrentLineFromParentConsoleBuffer(short line, short length)
{
var itemSize = Marshal.SizeOf(typeof(CHAR_INFO));
var buffer = Marshal.AllocHGlobal(length * itemSize);
var encoding = Console.OutputEncoding;
var text = new StringBuilder(capacity: length + 1);
var coordinates = default(COORD);
var textRegion = new SMALL_RECT
{
Left = 0,
Top = line,
Right = (short)(length - 1),
Bottom = line,
};
var bufferSize = new COORD
{
X = length,
Y = 1,
};
try
{
if (!ReadConsoleOutput(GetStdOutputHandle(), buffer, bufferSize, coordinates, ref textRegion))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
var array = buffer;
for (var i = 0; i < length; i++)
{
var info = Marshal.PtrToStructure<CHAR_INFO>(array);
var chars = encoding.GetChars(info.CharData);
text.Append(chars[0]);
array += itemSize;
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
// now that we've captured the current prompt, overwrite it with spaces
// so that things start where the parent left off at
Console.SetCursorPosition(0, line);
Console.Write(new string(' ', length));
Console.SetCursorPosition(0, line - 1);
return text.ToString();
}
void CleanUp()
{
if (disposed)
{
return;
}
disposed = true;
RestoreParentConsolePrompt();
if (createdNewConsole)
{
FreeConsole();
}
}
void RestoreParentConsolePrompt()
{
var text = prompt;
if (!string.IsNullOrEmpty(text))
{
// this assumes the last output from your application used
// Console.WriteLine or otherwise output a CRLF. if it didn't,
// you may need to add an extra line here
Console.Write(text);
}
}
[StructLayout(LayoutKind.Sequential)]
struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] CharData;
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
// REF: https://learn.microsoft.com/en-us/windows/console/allocconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
// REF: https://learn.microsoft.com/en-us/windows/console/attachconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
// REF: https://learn.microsoft.com/en-us/windows/console/freeconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
static bool AttachParentConsole() => AttachConsole(ATTACH_PARENT_PROCESS);
// REF: https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
static IntPtr GetStdOutputHandle() => GetStdHandle(STD_OUTPUT_HANDLE);
}
Ok, I don't have the solution, but it seems to be because the cmd.exe is not waiting on the started process, whereas with a normal console application cmd.exe waits until the the application exits. I don't know what makes cmd.exe decide to wait or not on an application, normal Windows Forms applications are just started and cmd.exe doesn't wait for it to exit. Maybe this hint triggers somebody! I will dig a bit deeper in the mean while.
Try calling the FreeConsole function prior to exiting your executable.
This one has been the easiest solution for me:
myapp.exe [params] | ECHO.
I attempted my own Qt cpp version of Chris Martinez's C# answer:
https://github.com/NightVsKnight/QtGuiConsoleApp/blob/main/QtGuiConsoleApp/main.cpp
#include <QApplication>
#include <QMessageBox>
#ifdef Q_OS_WIN
// Solution posted to https://stackoverflow.com/a/73942013/252308
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
QString consolePromptClear()
{
QString prompt = nullptr;
auto bSuccess = AttachConsole(ATTACH_PARENT_PROCESS);
if (bSuccess)
{
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (bSuccess)
{
auto dwConsoleColumnWidth = (DWORD)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd != 0 || yEnd != 0)
{
DWORD dwNumberOfChars;
SHORT yBegin = yEnd;
{
// Walk backwards to find first all blank line
auto pBuffer = (LPWSTR)LocalAlloc(LPTR, dwConsoleColumnWidth * sizeof(WCHAR));
while (yBegin)
{
COORD dwReadCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, pBuffer, dwConsoleColumnWidth, dwReadCoord, &dwNumberOfChars);
if (!bSuccess) break;
DWORD i;
for (i=0; i < dwNumberOfChars; ++i)
{
WCHAR wchar = pBuffer[i];
if (wchar != L' ')
{
--yBegin;
break;
}
}
if (i == dwNumberOfChars)
{
// Found all blank line; we want the *next* [non-blank] line
yBegin++;
break;
}
}
LocalFree(pBuffer);
}
auto promptLength = (yEnd - yBegin) * dwConsoleColumnWidth + xEnd;
auto lpPromptBuffer = (LPWSTR)LocalAlloc(LPTR, promptLength * sizeof(WCHAR));
COORD dwPromptCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, lpPromptBuffer, promptLength, dwPromptCoord, &dwNumberOfChars);
if (bSuccess)
{
Q_ASSERT(promptLength == dwNumberOfChars);
prompt = QString::fromWCharArray(lpPromptBuffer, dwNumberOfChars);
bSuccess = SetConsoleCursorPosition(hStdOut, dwPromptCoord);
if (bSuccess)
{
FillConsoleOutputCharacterW(hStdOut, L' ', promptLength, dwPromptCoord, &dwNumberOfChars);
}
}
LocalFree(lpPromptBuffer);
}
}
}
}
if (prompt.isEmpty())
{
FreeConsole();
return nullptr;
}
else
{
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
return prompt;
}
}
void consolePromptRestore(const QString& prompt)
{
if (prompt.isEmpty()) return;
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut == INVALID_HANDLE_VALUE) return;
CONSOLE_SCREEN_BUFFER_INFO csbi;
BOOL bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (!bSuccess) return;
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd == 0 && yEnd == 0) return;
auto buffer = prompt.toStdWString();
auto lpBuffer = buffer.data();
auto nLength = (DWORD)buffer.length();
COORD dwWriteCoord = { 0, (SHORT)(yEnd + 1) };
DWORD dwNumberOfCharsWritten;
WriteConsoleOutputCharacterW(hStdOut, lpBuffer, nLength, dwWriteCoord, &dwNumberOfCharsWritten);
dwWriteCoord = { (SHORT)dwNumberOfCharsWritten, (SHORT)(yEnd + 1) };
SetConsoleCursorPosition(hStdOut, dwWriteCoord);
}
#else
// Non-Windows impl...
#endif
int main(int argc, char *argv[])
{
// NOTE: Any console output before call to consolePromptClear() may get cleared.
// NOTE: Console vs GUI mode has **NOTHING** to do with being passed arguments; You can easily pass arguments to GUI apps.
int returnCode;
auto prompt = consolePromptClear();
if (prompt.isEmpty())
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
QMessageBox msgBox(nullptr);
msgBox.setWindowTitle(a.applicationName());
msgBox.setTextFormat(Qt::RichText);
msgBox.setText("App is detected to be running as a GUI");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.show();
returnCode = a.exec();
}
else
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
qout << "App is detected to be running as a Console" << Qt::endl;
returnCode = 0;
consolePromptRestore(prompt);
}
return returnCode;
}

Categories

Resources