Form.ShowInTaskBar / Process.MainWindowHandle - c#

When an application's main Form - the one passed to Application.Run() - has
this.ShowInTaskBar = false;
then, an instance of Process representing that application has a MainWindowHandle of 0, meaning that Process.CloseMainWindow() does not work.
How can I get around this? I need to cleanly close down the Form via the Process instance.

I found an alternative way to do it by stepping back to Win32 stuff and using window titles. It's messy, but it works for my situation.
The example has the context menu of one application instance closing all instances of that application.
[DllImport("user32.dll")]
public static extern int EnumWindows(EnumWindowsCallback x, int y);
public delegate bool EnumWindowsCallback(int hwnd, int lParam);
[DllImport("user32.dll")]
public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
private void ContextMenu_Quit_All(object sender, EventArgs ea)
{
EnumWindowsCallback itemHandler = (hwnd, lParam) =>
{
StringBuilder sb = new StringBuilder(1024);
GetWindowText(hwnd, sb, sb.Capacity);
if ((sb.ToString() == MainWindow.APP_WINDOW_TITLE) &&
(hwnd != mainWindow.Handle.ToInt32())) // Don't close self yet
{
PostMessage(new IntPtr(hwnd), /*WM_CLOSE*/0x0010, 0, 0);
}
// Continue enumerating windows. There may be more instances to close.
return true;
};
EnumWindows(itemHandler, 0);
// Close self ..
}

Related

C# Simulating mouseclicks in game doesn't work

Recently I tried to create a bot for an MMO-Game (called Florensia).
It should click on several positions in the game.
My problem is that it only sets the cursour to the position but the click doesn't work out. If I try it at my desktop or some other programs, it clicks correctly.
The game of course is in windowed mode and I already tried to set delays between the Mouseup and Mousedown.
Also to set the game to foreground window before the click didn't work.
Looking forward to any answers! :)
Try to use WinApi functions like in this example.
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
static void Main(string[] args)
{
const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;
const int MK_LBUTTON = 0x0001;
uint x = 100;
uint y = 100;
IntPtr handle = new IntPtr(0x00090996); //Your window handle
SetForegroundWindow(handle);
Thread.Sleep(300);
SendMessage(handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), new IntPtr(y << 16 | x));
Thread.Sleep(300);
SendMessage(handle, WM_LBUTTONUP, new IntPtr(0), new IntPtr(y << 16 | x));
}

How to get active window that is not part of my application?

How can I get the Window Title that the user currently have focus on?
I'm making a program that runs with another Window, and if the user does not have focus on that window I find no reason for my program to keep updating.
So how can I determine what window the user have focus on?
I did try to look into
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
but I seems I can only use that if the Window is part of my application which is it not.
Check this code:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
Use GetForegroundWindow to retrieve the handle of the focused window and GetWindowText to get the window title.
[ DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[ DllImport("user32.dll") ]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);
static void Main() {
StringBuilder builder = new StringBuilder(255) ;
GetWindowText(GetForegroundWindow(), builder, 255) ;
Console.WriteLine(builder) ;
}

prevent more than one tray icon in c#

I am developing an application in c#.net,and for that i am writing code to display icon in a system tray,and whenever a new message arrives the balloon tooltip will be shown there,which has click event which will open new message arrived,everything works fine,but the problem is i am getting multiple numbers of icon generated in system tray,which shuld be only one,how can i prevent it?i found on internet how to dispose them,but couldn't find way to prevent more than one.or is there any better way to show notifications for newly received message..please help me if you know the solution..
There are better custom solutions available see here and here for some examples.
However, System Tray doesn't refresh automatically. If you show/hide multiple system tray icons, it can mess the tray up. Normally all disposed icons will disappear when you mouse hover. However, there is a way to refresh the system tray programmatically. Reference here.
Note : SendMessage function, sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
if (notificationAreaHandle == IntPtr.Zero)
{
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
RefreshTrayArea(overflowNotificationAreaHandle);
}
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
const uint wmMousemove = 0x0200;
RECT rect;
GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
}

Getting a second IE Window to open on a different display

I'm writing a .exe that is supposed to run as a scheduled task to check if I have required IE windows open running .Xbaps on specific monitors. I have code that checks what URL is supposed to run, if it's not I use this code to launch it, then move it to the correct monitor:
Process myProcess = Process.Start("iexplore.exe", "-new -k " + "http://server01:123/software.client.xbap");
myProcess.WaitForInputIdle();
Thread.Sleep(500);
MoveWindowToMonitor(myProcess.MainWindowHandle, 1);
Window Moving code:
private static void MoveWindowToMonitor(IntPtr windowHandler, int monitor)
{
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
int width = windowRec.Right - windowRec.Left;
int height = windowRec.Top - windowRec.Bottom;
if (width < 0)
width = width * -1;
if (height < 0)
height = height * -1;
SetWindowPos(windowHandler, (IntPtr)SpecialWindowHandles.HWND_TOP, Screen.AllScreens[monitor].WorkingArea.Left,
Screen.AllScreens[monitor].WorkingArea.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
}
Running a quick test version of this gets the first IE window open, Xbap launched, and then quickly moves it over to my other monitor. When I run it a second time, without closing the first IE window, I always get InvalidOperationException
"Process has exited, so the requested information is not available."
I've checked my Task Manager as this is happening and I actually get two iexplore.exe items under details the first time I run the task, and only one additional iexplorer.exe for each subsequent execution of the task. I also get one PresentationHost.exe per xbap launched.
Anyone have any idea what I'm doing wrong or a better way to do this?
My end goal is to be able to do this:
Launch IE in Kiosk Mode on monitor 1 with specific url X:
Launch IE in Kiosk Mode on monitor 2 with specific url Y:
After you launch an IE process, it does some funny stuff and that process you have launched can occasionally end right away as another takes over the window.
What I would do is, using the methods below, is EnumTheWindows which would step through every visible window running and look for Internet Explorer or my baseURL. Then I would pass that Window Handle to GetURL and get the specific URL that IE window was running. This allowed me to use ConfirmProcessIsOnProperMonitor() and MoveWindowToMonitor() to get the windows on the proper monitor.
Important stuff:
private static bool ConfirmProcessIsOnProperMonitor(IntPtr windowHandler, int monitor)
{
//make sure you don't go to an incorrect monitor
if (monitor >= Screen.AllScreens.Count()) monitor = Screen.AllScreens.Count() - 1;
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
if (windowRec.Left != Screen.AllScreens[monitor].WorkingArea.Left || windowRec.Top != Screen.AllScreens[monitor].WorkingArea.Top)
return false;
else
return true;
}
private static void MoveWindowToMonitor(IntPtr windowHandler, int monitor)
{
//make sure you don't go to an incorrect monitor
if (monitor >= Screen.AllScreens.Count()) monitor = Screen.AllScreens.Count() - 1;
RECT windowRec = new RECT();
GetWindowRect(windowHandler, ref windowRec);
int width = windowRec.Right - windowRec.Left;
int height = windowRec.Top - windowRec.Bottom;
if (width < 0)
width = width * -1;
if (height < 0)
height = height * -1;
SetWindowPos(windowHandler, (IntPtr)SpecialWindowHandles.HWND_TOP, Screen.AllScreens[monitor].WorkingArea.Left,
Screen.AllScreens[monitor].WorkingArea.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
}
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
string windowText = sb.ToString();
if (windowText.ToLower().Contains(_baseURL) || windowText.ToLower().Contains("internet explorer"))
{
string url = GetURL(hWnd);
_windowhandles.Add(hWnd, url);
}
}
return true;
}
private static string GetURL(IntPtr intPtr)
{
foreach (InternetExplorer ie in new ShellWindows())
{
if (ie.HWND == intPtr.ToInt32())
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ie.FullName);
if ((fileNameWithoutExtension != null) && fileNameWithoutExtension.ToLower().Equals("iexplore"))
{
return ie.LocationURL;
}
else
{
return null;
}
}
}
return null;
}
Hard to read windows API code:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, StringBuilder msgbody);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);

Hide Title Bar of Program using API

Its possible remove a window console title bar using c# and windows api, if yes howto? Please.
This simple app hides and shows the title bar of the console that it's in. It changes the console title to a guid momentarily to find the window handle. Afterwards it uses ToggleTitleBar to show or hide using the found handle.
public class Program
{
public static void ToggleTitleBar(long hwnd, bool showTitle)
{
long style = GetWindowLong(hwnd, -16L);
if (showTitle)
style |= 0xc00000L;
else
style &= -12582913L;
SetWindowLong(hwnd, -16L, style);
SetWindowPos(hwnd, 0L, 0L, 0L, 0L, 0L, 0x27L);
}
public static void Main()
{
Guid guid = Guid.NewGuid();
string oldTitle = Console.Title;
Console.Title = guid.ToString();
int hwnd = FindWindow("ConsoleWindowClass", guid.ToString());
Console.Title = oldTitle;
Console.WriteLine("Press enter to hide title");
Console.ReadLine();
ToggleTitleBar(hwnd, false);
Console.WriteLine("Press enter to show title");
Console.ReadLine();
ToggleTitleBar(hwnd, true);
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
[DllImport("user32", EntryPoint = "GetWindowLongA")]
public static extern long GetWindowLong(long hwnd, long nIndex);
[DllImport("user32", EntryPoint = "SetWindowLongA")]
public static extern long SetWindowLong(long hwnd, long nIndex, long dwNewLong);
[DllImport("user32")]
public static extern long SetWindowPos(long hwnd, long hWndInsertAfter, long x, long y, long cx, long cy,
long wFlags);
[DllImport("User32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
}
Edit: Sorry, I see that you're looking for a solution for a console application. No, there is no way I know of to do what you're trying to do. It is also not simple to host a console in a WinForms application.
However, if you ARE using a WinForms application or WPF, consider the following.
this.ControlBox = false;
this.Text = string.Empty;
Otherwise, you could set FormBorderStyle to None.
You can also hide the program from the task bar if you need to.
this.ShowInTaskBar = false;
This will probably not work. In theory you could use something like this:
HWND handle = FindWindow(L"ConsoleWindowClass", NULL);
LONG style = GetWindowLong(handle, GWL_STYLE);
style = style & ~WS_CAPTION;
SetWindowLong(handle, GWL_STYLE, style);
This will work for every window except console windows. SetWindowLong returns 0, and GetLastError returns 5 (Access denied), even if you run the application as administrator.
I have some (very) old code I think somehow related; I'd to display Microsoft Excel inside a winform application:
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int SetParent(int hWndChild, int hWndNewParent);
[DllImport("user32.dll")]
public static extern int MoveWindow(
int hWnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
//
private static int hwnExcel = 0;
private System.Windows.Forms.PictureBox picContainer;
// ...
private void Principal_Resize(object sender, EventArgs e)
{
picContainer.Width = this.Width - 8;
picContainer.Height = this.Height - 45;
User32.SetParent(hwnExcel, 0);
User32.MoveWindow(
hwnExcel, 0, 0,
picContainer.Width, picContainer.Height, 1);
}
use FindWindow to get the handle of the console window then SetWindowLong to modify his properties

Categories