In a WPF app how do you override the Console Close command? - c#

In my WPF application I use the Console Window as a debug/message window - I have options setup to show and hide the window as the user desires and all that works great. The only issue is that when the user closes the Console window it exits the entire program. How can I override this so that when the user click on the X it just hides the Console instead of exiting the application?
this is what I have so far:
const int SW_HIDE = 0;
const int SW_SHOW = 5;
public static bool ConsoleVisible { get; private set; }
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void HideConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_SHOW);
ConsoleVisible = true;
}
** For people wanting to utilize this you need: using System.Runtime.InteropServices;
This code was derived from this answer: https://stackoverflow.com/a/3571628/649382
** Edit **
Looking around a bit more it seems like this is not possible. I saw an answer here: https://stackoverflow.com/a/12015131/649382 that talks about removing the exit button which would also be acceptable, except it looks like the code is in C++ and I can't figure out it's C# alternative.
** Edit 2 **
I was able to figure out the conversion to C# and have written it as the answer below.

So as has been discussed there is no way to prevent the Console Window from closing the WPF/Application Window. Prior to Windows Vista there were some workarounds, but since then they have been removed (probably for security reasons). The work around I was able to come up with was to disable the Exit button on the Console Window and place show/hide options into my application. The application start class looks like this:
using System;
using System.Windows;
using System.Runtime.InteropServices;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
ConsoleVisible = true;
DisableConsoleExit();
}
#region Console Window Commands
// Show/Hide
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
const uint SW_HIDE = 0;
const uint SW_SHOWNORMAL = 1;
const uint SW_SHOWNOACTIVATE = 4; // Show without activating
public static bool ConsoleVisible { get; private set; }
public static void HideConsole()
{
IntPtr handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole(bool active = true)
{
IntPtr handle = GetConsoleWindow();
if (active) { ShowWindow(handle, SW_SHOWNORMAL); }
else { ShowWindow(handle, SW_SHOWNOACTIVATE); }
ConsoleVisible = true;
}
// Disable Console Exit Button
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern IntPtr DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
const uint SC_CLOSE = 0xF060;
const uint MF_BYCOMMAND = (uint)0x00000000L;
public static void DisableConsoleExit()
{
IntPtr handle = GetConsoleWindow();
IntPtr exitButton = GetSystemMenu(handle, false);
if (exitButton != null) DeleteMenu(exitButton, SC_CLOSE, MF_BYCOMMAND);
}
#endregion
}
}
Hope this helps everyone out who may have a similar issue.

I think you should look into creating the console using AllocConsole and releasing it using FreeConsole. That way you may be able to give the user the ability to close the console window while keeping your WPF application running.

Related

Pre-Launch Console Configuration

I am doing a console app that I change in start up the width, is it possible to do it before the console launch? right now the console launches at default then changes.
I wish it to launch after the configuration was applied.
using C# in Visual Studio.
Thanks
It is sadly (as far as I can tell) impossible.
At first I thought "Hey, let's hide the window at start, set the size, and show the window again" like so:
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SwHide = 0;
private const int SwShow = 5;
public static void Main()
{
_consoleHandle = GetConsoleWindow();
ShowWindow(_consoleHandle, SwHide);
Console.SetWindowSize(100, 30);
ShowWindow(_consoleHandle, SwShow);
}
But that didn't work. Even when I tried hiding the console window in a static constructor (so it gets executed before main). And by "didn't work" I mean, the console showed up, vanished, and reappeared with a different size.
Then I stumbled on a Q&A here that said you could hide the console window by setting the output type to "Windows Application" instead of "Console Application". So I thought "hey, I could attach a console after the fact, let's try it":
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SwHide = 0;
private const int SwShow = 5;
private const uint AttachParentProcess = 0x0ffffffff;
public static void Main()
{
AttachConsole(AttachParentProcess);
var handle = GetConsoleWindow();
Console.SetWindowSize(100, 30);
ShowWindow(handle, SwShow);
Console.WriteLine(Console.WindowHeight);
Console.WriteLine(Console.WindowWidth);
}
But that didn't work either, for some reason it can't attach a console and it returns an invalid handle meaning I can't do anything.

ShowWindow is unable to show or minimize Task Manager

I need to show Task Manager app programatically, maximize it and minimize it as any other window but there is a problem and it simply doesn't respond to ShowWindow(int hWnd, int nCmdShow).
I am pretty sure I use the correct handle because I enumerated all of the windows with EnumWindows(PCallBack callback, int lParam) and the only window that didn't respond was the task manager window with title process.MainWindowTitle = "Task Manager", I even manually found its handle using spy++ but it still doesn't respond to SW_SHOWNORMAL or any other nCmdShow parameter. I tried running apps as administrator to see if it has something to do with the issue but they kept behaving like normal when proper handle was given to ShowWindow function;
private delegate bool PCallBack(int hWnd, int lParam);
private static void ShowWindows()
{
EnumWindows(new PCallBack(FindWindows), 0);
}
private bool FindWindows(int handle, int lparam)
{
Console.WriteLine("showing");
ShowWindow(handle, (int)SW.SHOWMINIMIZED);
ShowWindow(handle, (int)SW.SHOWNORMAL);
Thread.Sleep(3000);
return true;
}
static void Main(string[] args)
{
ShowWindows();
}
This code literally shows every window EnumWindows can find even if they are not visible and task manager was never shown which proved to me that the problem has nothing to do with wrong handle.
This is how I find it by the way.
// the correct handle of Task Manager window
var handle = (int)Process.GetProcessesByName("taskmgr").FirstOrDefault().MainWindowHandle;
Basically this is my problem. Need help.
it simply doesn't respond to ShowWindow(int hWnd, int nCmdShow).
I tested on Windows 10 and this works for me :
Manifest file with level="requireAdministrator"
Test :
IntPtr hWndTarget = FindWindow("TaskManagerWindow", null);
bool bRet = ShowWindow(hWndTarget, SW_SHOWMINIMIZED);
with declarations :
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int SW_HIDE = 0;
public const int SW_SHOWNORMAL = 1;
public const int SW_SHOWMINIMIZED = 2;
public const int SW_SHOWMAXIMIZED = 3;

PostMessage from windows service

My issue is PostMessage windows API is not working properly as it works when running from console application.
Working code:
I have 2 application [1] is console application [2] Windows Forms application.
Requirement is I want to send message to all the running instances of application.
console application code:
class Program
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
static void Main(string[] args)
{
//we tried to create a mutex, but there's already one (createdNew = false - another app created it before)
//so there's another instance of this application running
Process currentProcess = Process.GetCurrentProcess();
//get the process that has the same name as the current one but a different ID
foreach (Process process in Process.GetProcessesByName("ClientApp1"))
{
if (process.Id != currentProcess.Id)
{
IntPtr handle = process.MainWindowHandle;
//if the handle is non-zero then the main window is visible (but maybe somewhere in the background, that's the reason the user started a new instance)
//so just bring the window to front
//if (handle != IntPtr.Zero)
//SetForegroundWindow(handle);
//else
//tough luck, can't activate the window, it's not visible and we can't get its handle
//so instead notify the process that it has to show it's window
PostMessage((IntPtr)HWND_BROADCAST, WM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);//this message will be sent to MainForm
break;
}
}
}
}
Windows Forms application code:
public partial class Form1 : Form
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//someone (another process) said that we should show the window (WM_ACTIVATEAPP)
if (m.Msg == WM_ACTIVATEAPP)
this.Close();
}
}
Above code is working as expected.
My issues start from here. I want to run the same code from windows service instead of console application. Need immediate guidance.
It seems when I run this code from windows service its not getting hold of the process or service runs in different account so message is not getting delivered.
Most probably you run your service as Local System account in session 0 and it is rather isolated for good reasons. For example you don't have access to other desktops/sessions.
You have to implement a different IPC method, e.g. pipes or memory mapped files.

C# Console Disable Resize

How to achieve programmatically that the console window is not resizable.
I don't want user to change the console window size with their mouse.
See the answer in this post on MSDN. It requires p-invoking:
private const int MF_BYCOMMAND = 0x00000000;
public const int SC_CLOSE = 0xF060;
public const int SC_MINIMIZE = 0xF020;
public const int SC_MAXIMIZE = 0xF030;
public const int SC_SIZE = 0xF000;//resize
[DllImport("user32.dll")]
public static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags);
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetConsoleWindow();
static void Main(string[] args)
{
IntPtr handle = GetConsoleWindow();
IntPtr sysMenu = GetSystemMenu(handle, false);
if (handle != IntPtr.Zero)
{
DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND);
DeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND);
DeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND);//resize
}
Console.Read();
}
Note that this does not prevent Windows window snapping (e.g. dragging window to edge of screen, Win+Left and Win+Right)
Since you're trying to disable resizing, I'm guessing you won't want scrollbars either. For that, see this answer to Remove space left after console scrollbars in C# (leftover after matching Console.SetWindowSize and SetBufferSize; also requires p-invoking to "fix").
I'm not sure you can avoid that kind of action.
But you may try to use WindowHeight, WindowWidth, LargestWindowHeight and LargestWindowWidth.
See this:
https://msdn.microsoft.com/pt-br/library/system.console(v=vs.110).aspx

Is it possible to remove resizing and closing of another application with C#?

I was wondering if it is possible to remove the ability to close OTHER windows using C#?
I know that you can override your windows' close() method, but is that also possible for other processes? And what about changing the window style of another process to fixed___ so it cannot be resized?
So far I have gotten the main window handle of the application and I have removed all buttons and menus, but I still need to figure out how to make it uncloseable and unresizeable.
Here's what I've got:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ThirdTest
{
class Program
{
#region Constants
//Finds a window by class name
[DllImport("USER32.DLL")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
//Sets a window to be a child window of another window
[DllImport("USER32.DLL")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
//Sets window attributes
[DllImport("USER32.DLL")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
//Gets window attributes
[DllImport("USER32.DLL")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr GetMenu(IntPtr hWnd);
[DllImport("user32.dll")]
static extern int GetMenuItemCount(IntPtr hMenu);
[DllImport("user32.dll")]
static extern bool DrawMenuBar(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
//assorted constants needed
public static uint MF_BYPOSITION = 0x400;
public static uint MF_REMOVE = 0x1000;
public static int GWL_STYLE = -16;
public static int WS_CHILD = 0x40000000; //child window
public static int WS_BORDER = 0x00800000; //window with border
public static int WS_DLGFRAME = 0x00400000; //window with double border but no title
public static int WS_CAPTION = WS_BORDER | WS_DLGFRAME; //window with a title bar
public static int WS_SYSMENU = 0x00080000; //window menu
#endregion
public static void WindowsReStyle()
{
Process[] Procs = Process.GetProcesses();
foreach (Process proc in Procs)
{
Console.WriteLine("Found process: " + proc.ProcessName.ToString());
if (proc.ProcessName.StartsWith("notepad"))
{
IntPtr pFoundWindow = proc.MainWindowHandle;
int style = GetWindowLong(pFoundWindow, GWL_STYLE);
//get menu
IntPtr HMENU = GetMenu(proc.MainWindowHandle);
//get item count
int count = GetMenuItemCount(HMENU);
//loop & remove
for (int i = 0; i < count; i++)
RemoveMenu(HMENU, 0, (MF_BYPOSITION | MF_REMOVE));
//force a redraw
DrawMenuBar(proc.MainWindowHandle);
SetWindowLong(pFoundWindow, GWL_STYLE, (style & ~WS_SYSMENU));
SetWindowLong(pFoundWindow, GWL_STYLE, (style & ~WS_CAPTION));
}
}
}
static void Main(string[] args)
{
WindowsReStyle();
}
}
}
Any ideas? (:
As I've put in the comments, here are some more details on the issue:
I need two applications to be side-by-side on the monitor.
None of them can be closeable or resizeable. One is a browser, the other is an application called "Z-tree".
I have already fixed the issue with Z-tree as it, by default, runs with no closebutton and no resizing and you can specify the size and position of it in the command line.
Here's another idea, create a winforms project and set the window so it cannot be resized. Then embed a single WebBrowser control in the form and navigate to your page in the form load:
private void Form1_Load(object sender, EventArgs e)
{
//catch form closing event to prevent form being closed using alt-f4
FormClosing += Form1_FormClosing;
//remove close button from toolbar and remove window border to prevent
//moving and resizing
this.ControlBox = false;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
//set position to half of the screen
this.Left = Screen.PrimaryScreen.Bounds.Width / 2;
this.Top = 0;
this.Width = Screen.PrimaryScreen.Bounds.Width / 2;
this.Height = Screen.PrimaryScreen.Bounds.Height;
//mark the window as a top level window, reducing users ability to alt-tab away
TopMost = true;
webBrowser1.Navigate("www.google.com");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//prevent form being closed
e.Cancel = true;
}
//the only way to close the form
void DoExit()
{
//remove the closing handler first or it won't close
FormClosing -= Form1_FormClosing;
Close();
}
you can force internet explorer into an "unexitable" fullscreen mode if you start it with:
iexplore -k www.google.com
This is how shops and stuff get it to run so no-one can close it. Of course you can close it through task manager, but it just makes it difficult for most users to close it.
(CTRL-W will close it <--- secret key!)
http://support.microsoft.com/kb/154780

Categories