notepad on top of a winform - c#

good day guys, can anyone help me. I have a winforms that set TopMost = true. And I have a button that when I click it, it create a notepad. Now, I want my notepad to show at the top of my winforms without setting my winforms TopMost = false. Maybe I've miss something. I'm open for any suggestions. By the way I set my form to TopMost=true and BringToFront() because I don't want any user to select any program at the taskbar and bring it to front and minimize my winforms. Thanks in advance
public Form1()
{
InitializeComponent();
this.BringToFront();
this.TopMost = true;
}
// bunch of codes here...
private void button1_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = "notepad.exe";
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
process.Start();
}
// some codes here
private void Form1_Load(object sender, EventArgs e)
{
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
Left = Top = 0;
Width = Screen.PrimaryScreen.WorkingArea.Width;
Height = Screen.PrimaryScreen.WorkingArea.Height;
}

PInvoke solution:
using System.Runtime.InteropServices;
...
// Even if it is "user32.dll" it will do on IA64 as well
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, int uFlags);
...
// Since Process is IDisposable, put it into "using"
using (Process process = new Process()) {
process.StartInfo.FileName = "notepad.exe";
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
process.Start();
// wait for main window be created
process.WaitForInputIdle();
// Insert (change Z-order) as the topmost - (IntPtr) (-1);
// NoMove, NoSize - 0x0002 | 0x0001
SetWindowPos(process.MainWindowHandle, (IntPtr) (-1), 0, 0, 0, 0, 0x0002 | 0x0001);
}

Related

How to open a Form as an overlay over other full screen applications?

I am trying to open a form as an overlay to another running application which runs in full screen mode. I am only interested to have this overlay only on top of this specific running app.
I've done some searching and I tried different behavior:
First I tried to set the TopMost property of the form as true:
private void TrackerManagerOnGameStarted(object? sender, EventArgs e)
{
_frm = new DeckOverlayForm();
_frm.TopMost = true;
_frm.Show();
}
This didn't work at all. I see the form if I alt-tab, but it doesn't show over the running app, which I think is normal, since TopMost property as stated in the documentation
A topmost form is a form that overlaps all the other (non-topmost) forms even if it is not the active or foreground form.
I believe the other running app, since it's in full screen, has the priority.
I then tried to modify my form using Win32 Api with the following code:
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
static readonly IntPtr HWND_TOP = new IntPtr(0);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
protected override bool ShowWithoutActivation => true;
public DeckOverlayForm()
{
InitializeComponent();
}
private void DeckOverlayForm_Load(object sender, EventArgs e)
{
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}
This also didn't work.
I, then, combined the logic from point 2 with the following:
private void TrackerManagerOnGameStarted(object? sender, EventArgs e)
{
var processes = Process.GetProcesses();
var proc = processes.First(x => x.ProcessName == PROCESS_NAME);
NativeWindow win32Parent = new NativeWindow();
win32Parent.AssignHandle(proc.MainWindowHandle);
_frm = new DeckOverlayForm();
_frm.TopMost = true; //I tried with this and without it.
_frm.Show(win32Parent);
}
After trying this, I can see the form for a little bit, but the application become unresponsive for a little time. I can click on buttons inside it but nothing happens. Then after like 10 to 20 seconds, the form crashes and the running app becomes fully operational again.
How can I achieve this and end up with the form showing as an overlay without problems?
Thanks to #RezaAghaei's comment I ended up figuring it out.
The way I was opening that form was after an event was being raised from a file watcher. After adding a button to open the form manually to see if it works over other applications, it worked even with the application I want it to work with.
Therefor, I tried to change the following which ended up working:
private void TrackerManagerOnGameStarted(object? sender, EventArgs e)
{
_statisticsForm?.AnnounceGameStarted();
if (InvokeRequired)
{
Invoke(new MethodInvoker(delegate
{
var processes = Process.GetProcesses();
var proc = processes.First(x => x.ProcessName == PROCESS_NAME);
NativeWindow win32Parent = new NativeWindow();
win32Parent.AssignHandle(proc.MainWindowHandle);
_frm = Program.ServiceProvider!.GetRequiredService<DeckOverlayForm>();
_frm.TopMost = true;
_frm.Show(win32Parent);
}));
}
else
{
var processes = Process.GetProcesses();
var proc = processes.First(x => x.ProcessName == PROCESS_NAME);
NativeWindow win32Parent = new NativeWindow();
win32Parent.AssignHandle(proc.MainWindowHandle);
_frm = Program.ServiceProvider!.GetRequiredService<DeckOverlayForm>();
_frm.TopMost = true;
_frm.Show(win32Parent);
}
}
I wasn't getting the usual errors we get when something needs invoking, but when I made sure to check if invoke is required, it worked like a charm.

Embed Unity3D app inside WPF page causes process name to disappear after navigating to new page

I am using the code posted in this question with some minor changes:
Embed Unity3D app inside WPF *without* having it take up the whole window
This approach worked great, but when I am looking at my task manager processes. My main WPF executable name is removed from the process when I exit the page running the unity player and load another page. The application still works just fine. Task Manager just shows an icon with no process name next to it. This is only an issue because I have another background service that monitor my WPF application and starts and stops it remotely based on its name. Any suggestion?
Page Code Shown Below.
public System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
[DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private Process process;
private IntPtr unityHWND = IntPtr.Zero;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
bool initialized = false;
public UnityPlayer()
{
InitializeComponent();
if (this.grid1.Children.Count == 0)
{
this.grid1.Children.Add(host);
}
dispatcherTimer.Tick += attemptInit;
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Start();
}
void attemptInit(object sender, EventArgs e)
{
if (process != null)
{
if (process.HasExited)
{
Thread.Sleep(1000);
FSC.GoHome();
}
}
if (initialized)
return;
HwndSource source = (HwndSource)HwndSource.FromVisual(host);
Console.WriteLine("attempting to get handle...");
if (source == null)
{
Console.WriteLine("Failed to get handle source");
return;
}
IntPtr hWnd = source.Handle;
try
{
process = new Process();
process.StartInfo.FileName = "Block Breaker.exe";
process.StartInfo.Arguments = "-parentHWND " + hWnd.ToInt32() + " " + Environment.CommandLine;
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForInputIdle();
// Doesn't work for some reason ?!
//unityHWND = process.MainWindowHandle;
EnumChildWindows(host.Handle, WindowEnum, IntPtr.Zero);
//unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
Console.WriteLine("Unity HWND: 0x" + unityHWND.ToString("X8"));
panel1_Resize(this, EventArgs.Empty);
initialized = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
}
}
private void ActivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
}
private void DeactivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
}
private int WindowEnum(IntPtr hwnd, IntPtr lparam)
{
unityHWND = hwnd;
ActivateUnityWindow();
return 0;
}
private void panel1_Resize(object sender, EventArgs e)
{
MoveWindow(unityHWND, 0, 0, (int)host.Width, (int)host.Height, true);
Console.WriteLine("RESIZED UNITY WINDOW TO: " + (int)host.Width + "x" + (int)host.Height);
ActivateUnityWindow();
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
dispatcherTimer.Stop();
dispatcherTimer = null;
host.Dispose();
}
Image Show Process Before the pages loads
Image show Process While the page is running
Image show Process After the pages exits and loads new page

Transparent form won't always stay on top

I need Form2 to be always on top of every single window - including games in fullscreen. This always works with windowed-mode applications, but it sometimes won't appear topmost when another app is in fullscreen mode. (Games, OpenGL, direct)
How can I fix this?
Form1:
Overlay overlayui = new Overlay();
overlayui.TopMost = true; // I have tried setting TopMost to false, same result.
overlayui.Show();
Form2:
Settings in WinForms designed view:
FormBorderStyle = none
ControlBox = false
ShowIcon = false
ShowInTaskBar = false
TopMost = false
I've implemented this piece of code used in similar issues:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_SHOWWINDOW = 0x0040;
public Overlay()
{
InitializeComponent();
this.Bounds = Screen.PrimaryScreen.Bounds;
}
I then implemented a timer (interval 10 ms):
private void timer1_Tick(object sender, EventArgs e)
{
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
}
internal class MessagesFilter : IMessageFilter
{
private readonly IntPtr ControlHandler;
private const int WM_KEYUP = 0x0101;
public MessagesFilter(IntPtr ControlHandler)
{
this.ControlHandler = ControlHandler;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
// TODO: Add MessagesFilter.PreFilterMessage implementation
if (m.Msg == WM_KEYUP)
{
if (m.HWnd == ControlHandler)
{
Keys k = ((Keys)((int)m.WParam));
if (k == Keys.Enter)
return true;
}
}
return false;
}
#endregion
}
EDIT:
I've implemented new timer :
SetWindowPos(processNOtopmost, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE )
So first time brings to top my app, and second one is removing from topmost external app.
Still same problem, sometimes it works, sometimes it doesn't.
You want to set:
TopMost = true
As far as i'm aware though this only makes it the topmost Window for you Application.
You have no control over other applications unless you prevent loss of Focus completely (not advised)
And besides most of the applications that are 'Stealing' focus from you will be DirectX and get priority on the GPU.

Show any Process like Modal Window

MessageBox.Show shows a window, and until the MessageBox window is not closed, clicking the main app will still let the MessageBox window have focus and make the MessageBox window flash. Same behavior is with the TaskDialog API, Form.ShowDialog(IWin32Window) and Window.ShowDialog().
How can I do this for a different process like Process.Start("notepad.exe")?
This hack may do the trick. but be sure you question yourself twice before using this.
private void DoEvil()
{
var windowField = typeof(Control).GetField("window", BindingFlags.Instance |
BindingFlags.NonPublic);
Form notepad = new Form();
NativeWindow window = (NativeWindow)windowField.GetValue(notepad);
var process = Process.Start("notepad.exe");
process.EnableRaisingEvents = true;
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1);
}
window.AssignHandle(process.MainWindowHandle);
Control.CheckForIllegalCrossThreadCalls = false;
EventHandler handler = (s, ev) => notepad.DialogResult = DialogResult.OK;
process.Exited += handler;
notepad.ShowDialog(this);
process.Exited -= handler;
Control.CheckForIllegalCrossThreadCalls = true;
}
Warning: Don't try this at home, office or anywhere :p
Ok, so this is what I have come up with, not the greatest or most elegant way to do it nor does it contains any error handling but I think it works,
What I do in my code is create a dummy form (Form2) with a Panel control and assign the parent of EXE to the handle (InPtr) of the panel. I have removed to the border of the form to make it look as flush as possible but there is still a noticeable flicker between the EXE loading and the SetParent method being called.
Form1 Code
private void button4_Click(object sender, EventArgs e)
{
using (var f2 = new Form2())
{
f2.ShowDialog(this);
}
}
Form2 Code
public partial class Form2 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[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);
private readonly BackgroundWorker worker = new BackgroundWorker();
private IntPtr mainHandle;
private IntPtr processHandle;
private Panel panel;
public Form2()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.CenterParent;
AddPanel();
}
private void AddPanel()
{
panel = new Panel {Dock = DockStyle.Fill};
mainHandle = panel.Handle;
Controls.Add(panel);
}
private void Form2_Load(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke((MethodInvoker) Close);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
var process = Process.Start("cmd.exe", "/k echo echo");
if (process != null)
{
while (process.MainWindowHandle == IntPtr.Zero)
{
// Add some sort of timeout here, infintite loops are bad!!!
}
processHandle = process.MainWindowHandle;
// Get the size of the EXE window and apply it to this form.
var size = GetSize(processHandle);
Invoke((MethodInvoker) delegate { Size = new Size(size.Width, size.Height);});
// Hook the parent of the EXE window to this form
SetHandle(processHandle);
// Make sure the windows is positions at location x = 0, y = 0 of this form
SetWindowPos(processHandle, IntPtr.Zero, 0, 0, size.Width, size.Height, SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
// wait for the EXE to terminate
process.WaitForExit();
// Unhook the closed process window
SetParent(processHandle, IntPtr.Zero);
}
}
private void SetHandle(IntPtr ptr)
{
if (ptr != IntPtr.Zero)
SetParent(processHandle, mainHandle);
}
private static Size GetSize(IntPtr hWnd)
{
RECT pRect;
var size = new Size();
GetWindowRect(hWnd, out pRect);
size.Width = pRect.Right - pRect.Left;
size.Height = pRect.Bottom - pRect.Top;
return size;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[Flags]
private enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
}
Look here: Wait till a process ends
In short, you can either use "process.WaitForExit();" to pause execution till the process exits, but I don't think this will flash the window or auto-focus to that process.
If you want the fancier UI stuff you're going to have to do something like this:
while (!process.HasExited)
{
//update UI
}
For switching the focus to another process, this should get you started.
EDIT:
Here's more info on flashing the window.
Ok, here's a solution that might work:
Start the process with the following code:
Process p = new Process("cmd");
p.Start();
p.WaitForExit();
Now, in the event handler for activating your application (active), set the focus on the process again. You can use process.HasExited to verify that you process is still running or not.
Note: I haven't tested it, but I think it should bring you close.

Windows Forms - MdiClient scroll bars not automatically appearing as expected

I'm writing a windows forms application in C# whereby some windows utilities can be launched (e.g. CMD prompt, Registry editor, Events Viewer etc) and placed in an MdiClient control on the main form.
Everything is working great except that the scroll bars in the MdiClient control aren't automatically appearing when a child window falls beyond the MdiClient's borders. If the child windows were windows forms then I know that the MdiClient's scroll bars would automatically appear as expected. I've tried many things, including some complex workarounds.. and i'm beginning to think there must be something i'm completely overlooking.
I have attached some sample code below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
namespace MdiClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
System.Windows.Forms.MdiClient mdiClient = new System.Windows.Forms.MdiClient();
mdiClient.Dock = DockStyle.Fill;
mdiClient.BackColor = Color.WhiteSmoke;
this.Controls.Add(mdiClient);
int processID = StartCMD();
AddToMDIClient(processID, mdiClient.Handle);
}
private int StartCMD()
{
int processID = -1;
using (Process process = new Process())
{
ProcessStartInfo startInfo = process.StartInfo;
startInfo.FileName = "cmd.exe";
try
{
process.Start();
processID = process.Id;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
return processID;
}
private void AddToMDIClient(int processID, IntPtr mdiClientHandle)
{
try
{
Process process = Process.GetProcessById(processID);
int numberOfAttempts = 0;
while (string.IsNullOrEmpty(process.MainWindowTitle) && numberOfAttempts < 30)//max of 3 seconds
{
Thread.Sleep(100);
process.Refresh();
numberOfAttempts++;
}
if (!string.IsNullOrEmpty(process.MainWindowTitle))
{
SetWindowPos(process.MainWindowHandle, HWND_TOPMOST, 1, 1, 0, 0, TOPMOST_FLAGS);
SetParent(process.MainWindowHandle, mdiClientHandle);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public const UInt32 TOPMOST_FLAGS = /*SWP_NOMOVE | */SWP_NOSIZE;
public const UInt32 SWP_NOSIZE = 0x0001;
}
}
The screenshot below shows that when the CMD window is moved so its borders are outside the borders of the MdiClient there are no scroll bars:
Please see this link for the image: http://picasaweb.google.com/lh/photo/75rMVJMCWRg_s_DFF6LmNg?authkey=Gv1sRgCIKRlsu8xuDh8AE&feat=directlink
Any help would be much appreciated!
Thanks,
Shady
Without the screenshot it is hard to say, but I think the way you create the MDIParanet is way too complicated.
private void Form1_Load(object sender, EventArgs e)
{
// System.Windows.Forms.MdiClient mdiClient = new System.Windows.Forms.MdiClient();
// mdiClient.Dock = DockStyle.Fill;
// mdiClient.BackColor = Color.WhiteSmoke;
// this.Controls.Add(mdiClient);
this.IsMdiContainer = true;
int processID = StartCMD();
AddToMDIClient(processID, mdiClient.Handle);
}
If you need the Client, you can filter it from Controls.
Another problem might be setting a MDIChild as TOP_MOST, I don't think that's a good combination.
I have been doing some testing and it work ok for me was long as I have Autoscroll = true in the forms properties.
Also, I noticed if you enlarge the form and move the command window to say the bottom right it will not show the scroll bars, it will only when you minimize the form past the command windows (see screenshots below)
Screenshot 1
http://picasaweb.google.com/lh/photo/rfwm-S8y06Fl3HFNshgj3g?feat=directlink
Screenshot 2
http://picasaweb.google.com/lh/photo/y6qkN9Jj19vDGFNkTuL4FQ?feat=directlink
Also, can you set on the Form's properties AutoScrollMinSize, so that you always have scroll bars in the form is smaller than the size set
Hope that helps
Josh

Categories