I have a winform application (.NET 2.0 C#). From this application, I want to start another process (another winform application) and dock it to my window (or at least make it look like it is docked). So far, I can only find information about docking controls, not windows in separate processes. My first thought is to get the handle of the window and use unmanaged system calls to set the height/width and position of the window to my docking area. But before I got started, I wanted to check to see if any of you good people have done something similar. I have access to the source code of the application I want docked but would rather not make any changes if I can avoid it. I have complete programming control over what will be the parent application. Any advice? Thanks in advance!
The solution I have used before is to set the application window as a child of the control you want to dock it in.
using System.Diagnostics;
using System.Runtime.InteropServices;
private Process pDocked;
private IntPtr hWndOriginalParent;
private IntPtr hWndDocked;
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
private void dockIt()
{
if (hWndDocked != IntPtr.Zero) //don't do anything if there's already a window docked.
return;
hWndParent = IntPtr.Zero;
pDocked = Process.Start(#"notepad");
while (hWndDocked == IntPtr.Zero)
{
pDocked.WaitForInputIdle(1000); //wait for the window to be ready for input;
pDocked.Refresh(); //update process info
if (pDocked.HasExited)
{
return; //abort if the process finished before we got a handle.
}
hWndDocked = pDocked.MainWindowHandle; //cache the window handle
}
//Windows API call to change the parent of the target window.
//It returns the hWnd of the window's parent prior to this call.
hWndOriginalParent = SetParent(hWndDocked, Panel1.Handle);
//Wire up the event to keep the window sized to match the control
Panel1.SizeChanged += new EventHandler(Panel1_Resize);
//Perform an initial call to set the size.
Panel1_Resize(new Object(), new EventArgs());
}
private void undockIt()
{
//Restores the application to it's original parent.
SetParent(hWndDocked, hWndOriginalParent);
}
private void Panel1_Resize(object sender, EventArgs e)
{
//Change the docked windows size to match its parent's size.
MoveWindow(hWndDocked, 0, 0, Panel1.Width, Panel1.Height, true);
}
* Adding some solution in Answer..**
This code has helped me to dock some executable in windows form. like NotePad, Excel, word, Acrobat reader n many more...
But it wont work for some applications.
As sometimes when you start process of some application.... wait for idle time... and the try to get its mainWindowHandle.... till the time the main window handle becomes null.....
so I have done one trick to solve this
If you get main window handle as null... then search all the runnning processes on sytem and find you process ... then get the main hadle of the process and the set panel as its parent.
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "xxxxxxxxxxxx.exe";
info.Arguments = "yyyyyyyyyy";
info.UseShellExecute = true;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Maximized;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = false;
info.RedirectStandardError = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForInputIdle();
Thread.Sleep(3000);
Process[] p1 ;
if(p.MainWindowHandle == null)
{
List<String> arrString = new List<String>();
foreach (Process p1 in Process.GetProcesses())
{
// Console.WriteLine(p1.MainWindowHandle);
arrString.Add(Convert.ToString(p1.ProcessName));
}
p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
//p.WaitForInputIdle();
Thread.Sleep(5000);
SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
}
else
{
SetParent(p.MainWindowHandle, this.panel2.Handle);
}
This is a lot clunkier than I hoped for, but working so far. I am using system calls to force the child window in the location that reflects the docking area. It is not working perfectly yet. I get a few oddities caused by the HWND_TOPMOST and I still need to add logic preventing the user from moving the child window directly.
//This is my docking window
private System.Diagnostics.Process notepad;
private void windowDockTest()
{
/*
* Docking notepad to panel2 of the splitcontainer
*/
//if panel2 moves or is resized, call the docking function
spcScript.Panel2.Move += new EventHandler(Panel2_Resize);
spcScript.Panel2.SizeChanged += new EventHandler(Panel2_Resize);
//Call the docking function if main form is moved
this.LocationChanged += new EventHandler(Panel2_Resize);
//Start the notepad process
notepad = new System.Diagnostics.Process();
notepad.StartInfo.FileName = "notepad";
notepad.Start();
//Wait a second for notpad to fully load
notepad.WaitForInputIdle(1000);
//Dock it
Panel2_Resize(new Object(), new EventArgs());
}
void Panel2_Resize(object sender, EventArgs e)
{
//Get the screen location of panel2
Rectangle r = spcScript.Panel2.RectangleToScreen(spcScript.Panel2.ClientRectangle);
//Dock it
redock(notepad.MainWindowHandle, r.X, r.Y, r.Width, r.Height);
}
[DllImport("user32.dll")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
public static void redock(IntPtr handle, int x, int y, int width, int height)
{
IntPtr HWND_TOPMOST = new IntPtr(-1);
const short SWP_NOACTIVATE = 0x0010;
SetWindowPos(handle,HWND_TOPMOST, x, y, width, height,SWP_NOACTIVATE);
}
Related
I need my window to be on top of another window. That "other" window (application) is from different developer. I do not have source codes for it. I can only use Spy++ to get information about it.
I am using Windows 7.
I tryed several things but they did not work.
This is what I tryed so far:
1) Timer + BringWindowToTop
2) I changed Owner of my window
IntPtr handle = User32.FindWindow("Vega Prime", "Vega Prime");
NativeWindow win = new NativeWindow();
win.AssignHandle(handle);
ChildForm form = new ChildForm();
form.Show(win);
When I am saying that it does not work I mean this:
1) at first everything looks alright: my window is on top
2) then I click on window (Vega Prime) which is below mine
3) my window disappears
4) I click on place where my window should be and it reappears (!!!!!!)
What is that? How is it possible at all?
UPDATE:
I spent some time trying to find solution to my problem.
Here is what I found:
TopMost window going behind non-TopMost fullscreen window sometimes
https://social.msdn.microsoft.com/Forums/vstudio/en-US/92e66584-6cb8-4976-9531-eab3b9a129e3/mfc-window-with-wsextopmost-hidden-by-full-screen-window?forum=vcgeneral
I am pretty sure that my problem has something to do with "Full Screen Issue" at Windows 7 (sometimes, when not top most window becomes full screen it forces top most windows to become hidden). That explains described above weird behaviour, right?
I realized this example that seems to do the trick:
public partial class Form1 : Form
{
IntPtr hWndToStayOver = User32.FindWindow("Vega Prime", "Vega Prime");
private System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(this.Form1_Load);
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start(); // Routine starts now that I'm sure my Form exists
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
timer1.Stop(); // Stop routine (my Form doesn't exist anymore)
}
private bool AmIAboveOtherWindow()
{
IntPtr tmpHwnd = User32.GetNextWindow(hWndToStayOver, User32.GetNextWindowCmd.GW_HWNDPREV);
while (tmpHwnd != (IntPtr)0)
{
if (tmpHwnd == this.Handle)
return true;
tmpHwnd = User32.GetNextWindow(tmpHwnd, User32.GetNextWindowCmd.GW_HWNDPREV);
}
return false;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
if (!AmIAboveOtherWindow()) // Check if I am behind the target window
{
User32.SetWindowPos(this.Handle, hWndToStayOver, 0, 0, 0, 0, // Move my Form behind the target
User32.SetWindowPosFlags.SWP_NOMOVE |
User32.SetWindowPosFlags.SWP_NOSIZE |
User32.SetWindowPosFlags.SWP_SHOWWINDOW |
User32.SetWindowPosFlags.SWP_NOACTIVATE |
User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
User32.SetWindowPos(hWndToStayOver, this.Handle, 0, 0, 0, 0, // Move target behind my Form
User32.SetWindowPosFlags.SWP_NOMOVE |
User32.SetWindowPosFlags.SWP_NOSIZE |
User32.SetWindowPosFlags.SWP_SHOWWINDOW |
User32.SetWindowPosFlags.SWP_NOACTIVATE |
User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
}
timer1.Start();
}
}
User32 class
public class User32
{
[Flags]
public 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,
}
public enum GetNextWindowCmd : uint
{
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindow")]
public static extern IntPtr GetNextWindow(IntPtr hWnd, GetNextWindowCmd uCmd);
}
Clearly, you have to add to this code some check on the target handle: this example works only if the target window exists when the Form is loaded and if it isn't closed until the end
Make sure you set the owner of your window to the other window.
That will make sure that your window is always on top of the owner.
Here's how to import the appr. native functions, just change the 'Get' to 'Set' everywhere.
Then you can invoke it like this:
SetWindowLong(handle_of_owned_window, -8, handle_of_owner_window);
Hint #1: it's easy to set the ownership b/w 2 Form instances via the Form.Owner property, however you don't have access to one of them Forms.
Hint #2: in order to access the handle of a window, it needs to be shown at least once first.
It turned out that "other" application (which I was trying to merge with my application) is using Full-Screen Exclusive Mode. That explains why my topmost window disappeared from view and never reappeared (unless I switch off fullscreen mode with mouse click).
Details about Full-Screen Exclusive Mode are here:
https://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html
Basic idea is that Full-Screen Exclusive Mode "allows the programmer to suspend the windowing system so that drawing can be done directly to the screen". I believe that means that during Full-Screen Exclusive Mode (some experts call it "real full screen") OS ignores different windows (to save resources).
The only solution in situation like that is to configure "other" application to disable full screen mode.
It helped in my case - I studied documentation and found a place in configuration file where to set full screen mode to false.
I have a Win Forms app in which I am starting a process from a different .exe file, then setting it's Handle property to a panel in my Win Form app. This gives the effect of the other app running inside of the Win Forms app.
Here is how I accomplish that:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hwc, IntPtr hwp);
string exepath = "myProgram.exe";
ProcessStartInfo p = new ProcessStartInfo(exepath);
process = Process.Start(p);
Thread.Sleep(500); //sleep to allow program to start up properly
SetParent(process.MainWindowHandle, pictureBox1.Handle); //then set the handle to give the effect of being run inside the win forms app
Now I know I can call process.Kill() in a FormClosed event, which terminates the process when this form is closed, but how would I go about killing the process if my Win Form app is forcefully quit? Is that even possible?
Because the process's handle is set to a panel in the Win Form app, it doesn't appear on the taskbar but it will still continue to run if process.Kill() is not called, which happens when the Win Form is forcefully closed. This means each time I have to shut it off via task manager, which is a pain..
If this is not possible, I will not bother with setting the Handle to the panel, and I will just have it open in a new window.
Thanks
Try with this code, I was using it for a long time:
public partial class Launcher : Form
{
/// <summary>Collection of process. </summary>
private Dictionary<IntPtr, Process> _processCollection = new Dictionary<IntPtr, Process>();
#region DLL Import
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
#endregion
/// <summary>Default constructor. </summary>
public Launcher()
{
InitializeComponent();
try {
FormClosing += Launcher_FormClosing;
StartInstances();
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
/// <summary>Starts the instances. </summary>
private void StartInstances()
{
var path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
var numberOfInstances = Int32.Parse(ConfigurationManager.AppSettings["NumerOfInstances"]);
for (int i = 0; i < numberOfInstances; i++) {
StartInstance(i, path);
}
}
/// <summary>Starts an instance. </summary>
private void StartInstance(int instanceId, string path)
{
Process proc = Process.Start(path + "\\foo.exe", instanceId.ToString());
IntPtr handlerDocked = IntPtr.Zero;
Panel panel = new Panel();
panel.Size = new Size(flwPanel.Width / 3, flwPanel.Height / 2);
flwPanel.Controls.Add(panel);
do {
try {
proc.WaitForInputIdle(1000); //wait for the window to be ready for input;
proc.Refresh(); //update process info
if (proc.HasExited) {
return; //abort if the process finished before we got a handle.
}
handlerDocked = proc.MainWindowHandle; //cache the window handle
}
catch {
Thread.Sleep(500);
}
} while (handlerDocked == IntPtr.Zero);
//hWndOriginalParent = SetParent(hWndDocked, panel1.Handle);
SetParent(handlerDocked, panel.Handle);
var docked = new DockedElement(handlerDocked, panel);
panel.SizeChanged += new EventHandler(Panel_Resize);
Panel_Resize(docked, new EventArgs());
_processCollection.Add(handlerDocked, proc);
}
private void Panel_Resize(object sender, EventArgs e)
{
var docked = (DockedElement)sender;
//Change the docked windows size to match its parent's size.
MoveWindow(docked.Handle, 0, 0, docked.Container.Width, docked.Container.Height, true);
}
/// <summary>Finallize instances. </summary>
public void FinallizeInstances()
{
foreach (var docked in _processCollection) {
docked.Value.Close();
}
_processCollection.Clear();
}
private void Launcher_FormClosing(object sender, FormClosingEventArgs e)
{
FinallizeInstances();
}
protected override void Dispose(bool disposing)
{
FinallizeInstances();
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
}
To be sure your child processes are properly wiped as soon as your application closes (gracefully or forcefully), you can use Job Objects.
A job object allows groups of processes to be managed as a unit. Job
objects are namable, securable, sharable objects that control
attributes of the processes associated with them. Operations performed
on a job object affect all processes associated with the job object.
Examples include enforcing limits such as working set size and process
priority or terminating all processes associated with a job.
There is a similar question on StackOverflow with a detailed implementation in C#: Working example of CreateJobObject/SetInformationJobObject pinvoke in .net?
I am working on a wpf application where instead of exiting the app when user closes the button I am minimizing it to the tray(similar to google talk).
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
What I need is if user forgets that there is an instance of the app and tries to open a new instance I have to shut down the second instance and set my application as the foreground app. If the app is in minimized state (not hidden) I am able to do this. I am using the following code
protected override void OnStartup(StartupEventArgs e)
{
Process currentProcess = Process.GetCurrentProcess();
var runningProcess = (from process in Process.GetProcesses()
where
process.Id != currentProcess.Id &&
process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal)
select process).FirstOrDefault();
if (runningProcess != null)
{
Application.Current.Shutdown();
ShowWindow(runningProcess.MainWindowHandle, 5);
ShowWindow(runningProcess.MainWindowHandle, 3);
}
}
[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
When the app is minimized it has some unique value for MainWindowHandle. When I hide the app, the MainWindowHandle of runningProcess is showing as 0. I think this is why my application is not opening when it is in hidden state, but don't know how to fix it.
Tell me if I need to post more code or clarify anything. Thank you in advance.
When I hide the app, the MainWindowHandle of runningProcess is showing as 0
You're right. If a process doesn't have a graphical interface associated with it (hidden/ minimized) then the MainWindowHandle value is zero.
As workaround, you could try getting the HANDLE for the hidden window by enumerating all open windows using EnumDesktopWindows function and compare its process id with the hidden/ minimized windows's process id.
Update
The WPF's WIN32 window has a bit different behavior than the standard WIN32 window. It has the class name composed of the word HwndWrapper, the name of AppDomain it was created in, and a unique random Guid (which changes on every launch), e.g., HwndWrapper[WpfApp.exe;;4d426cdc-31cf-4e4c-88c7-ede846ab6d44].
Update 2
When WPF's window is hidden by using the Hide() method, it internally calls UpdateVisibilityProperty(Visibility.Hidden), which in turns set the internal visibility flag for UIElement to false. When we call the Show() method of WPF Window, UpdateVisibilityProperty(Visibility.Visible) is called, and the internal visibility flag for UIElement is toggled.
When we show the WPF window using the ShowWindow(), the UpdateVisibilityProperty() method is not trigerred, thus the internal visibility flag does not get reversed (which causes the window to be displayed with black backround).
By looking at the WPF Window internal implementation, the only way to toggle the internal visiblity flag, without calling the Show() or Hide() method, is by sending a WM_SHOWWINDOW message.
const int GWL_EXSTYLE = (-20);
const uint WS_EX_APPWINDOW = 0x40000;
const uint WM_SHOWWINDOW = 0x0018;
const int SW_PARENTOPENING = 3;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc ewp, int lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll")]
private static extern uint GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern uint GetWindowText(IntPtr hWnd, StringBuilder lpString, uint nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
static bool IsApplicationWindow(IntPtr hWnd) {
return (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_APPWINDOW) != 0;
}
static IntPtr GetWindowHandle(int pid, string title) {
var result = IntPtr.Zero;
EnumWindowsProc enumerateHandle = delegate(IntPtr hWnd, int lParam)
{
int id;
GetWindowThreadProcessId(hWnd, out id);
if (pid == id) {
var clsName = new StringBuilder(256);
var hasClass = GetClassName(hWnd, clsName, 256);
if (hasClass) {
var maxLength = (int)GetWindowTextLength(hWnd);
var builder = new StringBuilder(maxLength + 1);
GetWindowText(hWnd, builder, (uint)builder.Capacity);
var text = builder.ToString();
var className = clsName.ToString();
// There could be multiple handle associated with our pid,
// so we return the first handle that satisfy:
// 1) the handle title/ caption matches our window title,
// 2) the window class name starts with HwndWrapper (WPF specific)
// 3) the window has WS_EX_APPWINDOW style
if (title == text && className.StartsWith("HwndWrapper") && IsApplicationWindow(hWnd))
{
result = hWnd;
return false;
}
}
}
return true;
};
EnumDesktopWindows(IntPtr.Zero, enumerateHandle, 0);
return result;
}
Usage Example
...
if (runningProcess.MainWindowHandle == IntPtr.Zero) {
var handle = GetWindowHandle(runningProcess.Id, runningProcess.MainWindowTitle);
if (handle != IntPtr.Zero) {
// show window
ShowWindow(handle, 5);
// send WM_SHOWWINDOW message to toggle the visibility flag
SendMessage(handle, WM_SHOWWINDOW, IntPtr.Zero, new IntPtr(SW_PARENTOPENING));
}
}
...
Thanks IronGeek, This is great. I'm just learning c# and struggled for a while trying to get this to work. Also I cant 'add comment' as insufficient reputation here so hence this post. I'm using WPF .Net 5.0.
I searched around to implement this, so for other newbies they will also need something like the following in their program to receive the message (sorry, not sure what page I copied this from, so many of them (individuals will need to make their own Mainwindow_Loaded event handler).
private void Mainwindow_Loaded_Event(object sender, RoutedEventArgs e)
{
hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
hwndSource.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SHOWWINDOW)
{
MessageBox.Show("I recieved WM_SHOWWINDOW");
handled = true;
}
return IntPtr.Zero;
}
The 'bring to front' tip you mentioned was also needed in my case, here is what is needed:
(from Bring Word to Front )
put this in the declarations section:
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
and put the following just after the 'SendMessage(handle, WM_SHOWWINDOW, IntPtr.Zero, new IntPtr(SW_PARENTOPENING));' statement:
SetForegroundWindow(handle);
Without this, the activated window just remains hidden behind other windows and it has to be found by manually fishing around in the taskbar.
So now I've finally got this going for a non-hidden window but now need to look at what is needed for a hidden one as that is my real goal.
Following on from my early post, and to quote an earlier comment from IronGeek the issue is ' If a process doesn't have a graphical interface associated with it (hidden/ minimized) then the MainWindowHandle value is zero'. Therefore any attempt to pass a hidden Window's handle is doomed as it doesn't exist.
So I have found a work-around, although it requires the target process to regularly check for the presence of a new message. Therefore this is still not ideal, but it works for me in 2021 (WPF, .Net 5.0) and doesn't need to import the user32.dll. Rather it carries out a makeshift type of Inter Process Communication (IPC) using the MainWindowTitle as a container to send a message passively.
The MainWindowTitle is settable at runtime and is viewable from other processes, therefore it can be used like an inter-process variable. This is my entire solution below, note it needs to be published to a local folder to see how it runs as the point is to run multiple instances.
<Window x:Class="TitleComsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TitleComsTest"
mc:Ignorable="d"
Name="TitleComsTest" Title="TitleComsTest" Height="400" Width="600"
WindowStartupLocation = "CenterScreen" Closing="TitleComsTest_Closing" Visibility="Hidden">
<Grid>
<TextBox Name ="TextBox1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="230" Width="460"/>
<Button Name="QuitButton" Content=" Really Quit " HorizontalAlignment="Center" Margin="0,0,0,30" VerticalAlignment="Bottom" Click="QuitButton_Click"/>
</Grid>
The code behind:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
namespace TitleComsTest
{
public partial class MainWindow : Window
{
//string for MainWindowTitle when first instance and visible:
const string my_Visible_exe_Name = "TitleComstest";
//string for MainWindowTitle when first instance and Hidden:
const string my_Hidden_exe_Name = "TitleComstest...";
//string for MainWindowTitle when 2nd instance :
const string my_exe_Name_Flag = "TitleComstest, Please-Wait";
bool reallyCloseThisProgram = false;
private DispatcherTimer timer1, timer2;
public MainWindow()
{
InitializeComponent();
//Get an array of processes with the chosen name
Process[] TitleComstests = Process.GetProcessesByName(my_Visible_exe_Name);
if (TitleComstests.Length > 1)
{ //Then this is not the first instance
for (int i = 0; i < TitleComstests.Length; i++)
{
if (TitleComstests[i].MainWindowTitle == my_Visible_exe_Name)
{ //The first instance is visible as the MainWindowTitle has been set to the visible name
Close(); //Quit - nothing to do but close the new instance
}
}
//The first instance is hidden, so set MainWindowTitle so the first instance can see it and react
this.Title = my_exe_Name_Flag;
this.WindowState = WindowState.Minimized; //Minimize the window to avoid having two windows shown at once
this.Visibility = Visibility.Visible; //The second instance needs to be visible (minimized is enough) to be seen
StartTimerQuit(4000); //arbitrary time, needs to be longer than 2000ms which is the checking period - see StartTimerLook(2000);
}
else
{
TextBox1.Text = "This is Multi-instance demo using the 'MainWindowTitle' to send messages\r\nto the first (hidden) instance to wake it up.";
TextBox1.Text += "\r\n\r\nThis demo requires the program be published to a local folder and \r\nnot run in the debugger.";
TextBox1.Text += "\r\n\r\nYou can type here to mark this instance: _____________ \r\n\r\nand then hide me by clicking top right close window 'X'";
TextBox1.Text += "\r\n\r\nOnce closed then start the program again to see the 1st instance pop up.";
TextBox1.Text += "\r\n\r\nFinally use the 'Really Quit' button to end this demo.";
this.Visibility = Visibility.Visible;
}
}
private void StartTimerQuit(Int32 interval) //Timer to Quit setup and start
{
timer1 = new DispatcherTimer(); timer1.Tick += timerQuit_Tick;
timer1.Interval = new TimeSpan(0, 0, 0, 0, interval); timer1.Start();
}
private void timerQuit_Tick(object sender, EventArgs e)
{
reallyCloseThisProgram = true; Close();
}
private void TitleComsTest_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!reallyCloseThisProgram)
{
e.Cancel = true;
this.Title = my_Hidden_exe_Name; //Set the Title text to flag a hidden state
this.Visibility = Visibility.Hidden;
//Start checking every 2 secs at the process names - could be faster but this is a constant background process
StartTimerLook(2000);
}
}
private void StartTimerLook(Int32 interval) //Timer to look for new instances setup and start
{
timer2 = new DispatcherTimer(); timer2.Tick += timerLook_Tick;
timer2.Interval = new TimeSpan(0, 0, 0, 0, interval); timer2.Start();
}
private void timerLook_Tick(object sender, EventArgs e)
{ //Every timer interval check to see if a process is present with the Ttile name flag
Process[] myNameFlagProcesses = Process.GetProcessesByName(my_Visible_exe_Name);
for (int i = 0; i < myNameFlagProcesses.Length; i++)
{
if (myNameFlagProcesses[i].MainWindowTitle == my_exe_Name_Flag) //If name flag is seen ...
{ //... then wake up
TextBox1.Text += "\r\n Saw the other window";
this.Visibility = Visibility.Visible;
this.Title = my_Visible_exe_Name; //Set the Title text to flag a visible state
this.Show();
this.Activate();
timer2.Stop();
}
}
}
private void QuitButton_Click(object sender, RoutedEventArgs e)
{
reallyCloseThisProgram = true; Close();
}
}
}
Intention
Using the following code, I managed to load some applications in my windows form.
Code
What this function does is...
stating a process
embedding the process into a panel of my form
maximizing the embedded process
adding a resize event handler to the panel to update the size of the embedded process on panel resize
adding a closed event handler to the form to terminate the embedded process on form close
Usings
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
Constants
const int GWL_STYLE = -16;
const long WS_VISIBLE = 0x10000000,
WS_MAXIMIZE = 0x01000000,
WS_BORDER = 0x00800000,
WS_CHILD = 0x40000000;
Function
IntPtr LoadExtern(Control Panel, string Path)
{
try
{
Process Process = Process.Start(Path);
Process.WaitForInputIdle();
IntPtr Handle = Process.MainWindowHandle;
SetParent(Handle, Panel.Handle);
SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE+(WS_MAXIMIZE|WS_BORDER)));
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
Panel.Resize += new EventHandler(
delegate(object sender, EventArgs e)
{
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
}
);
this.FormClosed += new FormClosedEventHandler(
delegate(object sender, FormClosedEventArgs e) {
SendMessage(Handle, 83, 0, 0);
Thread.Sleep(1000);
Handle = IntPtr.Zero;
}
);
return Handle;
}
catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
return new IntPtr();
}
DLL Imports
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr Handle, int Msg, int wParam, int lParam);
Result
This code works nice with some applications, like the windows notepad. Notepad is started and included in the panel of my form. There is no caption and the are no borders, as it should be.
LoadExtern(panel1, "notepad.exe");
After closing the form the embedded process gets terminated like expected.
Problem
Unfortunately my code doesn't work for some other (bigger) applications like firefox or sublimetext.
LoadExtern(panel2, #"C:\Program Files (x86)\Mozilla Firefox\firefox.exe");
What happens is that my form starts and firefox starts, but in its own window. Could you help me to include sublimetext or firefox in my applications?
Part of the solution
Thanks to Sheng Jiang's answers, I got it working for some more applications. What I did is to wait for a main window handle.
Process.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 300; i++)
{
Handle = Process.MainWindowHandle;
Thread.Sleep(10);
}
But I still can't embed applications like the windows explorer.
Your code worked nice by coincidence.
WaitForInputIdle wouldn't necessary wait for the UI thread. For example an input method or a hook created by some other program may create a simple thread that becomes idle while the UI thread is still busy doing initialization.
MainWindowHandle searches for the first visible top level window. It won't return the logical main window when
The main window is not the first visible window created (e.g. a login dialog is created first)
The main window is not created with the visible style (think about a program that has only an icon in the notification area on the system tray)
There is no main window created at all (e.g. some applications open new documents/urls in an existing instance, like browsers and Windows Explorer)
There isn't a main window but multiple top level windows that have equal status. Think about IE6/Outlook/Word.
Even if the main window is created visibly and in fact is the first visible window in the new process, you may still have issues.
From the documentation of SetParent:
An application can use the SetParent function to set the parent window of a pop-up, overlapped, or child window.
It does not say you can reparent a top level window. In fact the top level window offers a lot of services that the program may be relying on, such as
Act as the measuring tool to determine if a full screen request is complete (conflicts with your requirement that new program needs to appear inside your panel)
Getting notified when a new DDE conversation starts, when the active window/program changes, when new hardware arrives, when system setting changes, when the user is logging off, when Windows Explorer is started, when the user pressed the Enter key on a nested dialog, etc. The list of window messages that only sent to top level windows is too long to list here,
Act as the default owner window of modal dialogs if the program choose to (and if you display modal dialog in your program as well, watch out for crashes)
This code works for most applcations. I embedded the file explorer simply using a webbrowser control on my form and set its url to a file location. The internet explorer control magically turns into a file explorer then.
This is my final code, feel free to use this for you own projects.
IntPtr EmbedProcess(Control Panel, string Path)
{
string Name = NameFromPath(Path);
foreach (Process Task in Process.GetProcesses())
{
if (NameFromPath(Task.ProcessName).Contains(Name))
{
try { Task.Kill(); }
catch (Exception e) { }
}
}
try
{
Process Task = Process.Start(Path);
Task.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 10; i++) { Handle = Task.MainWindowHandle; Thread.Sleep(100); }
SetParent(Handle, Panel.Handle);
SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER)));
MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
Panel.Resize += new EventHandler(delegate(object sender, EventArgs e) { MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true); });
this.FormClosed += new FormClosedEventHandler(delegate(object sender, FormClosedEventArgs e)
{
SendMessage(Handle, 83, 0, 0);
Thread.Sleep(100);
Handle = IntPtr.Zero;
});
return Handle;
}
catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
return new IntPtr();
}
I somebody is interested in the hole C# classes for embedding window processes and console processes into your form, check out this github repository.
I have a C# winforms app that runs a macro in another program. The other program will continually pop up windows and generally make things look, for lack of a better word, crazy. I want to implement a cancel button that will stop the process from running, but I cannot seem to get the window to stay on top. How do I do this in C#?
Edit: I have tried TopMost = true; , but the other program keeps popping up its own windows over top. Is there a way to send my window to the top every n milliseconds?
Edit: The way I solved this was by adding a system tray icon that will cancel the process by double-clicking on it. The system tray icon does no get covered up. Thank you to all who responded. I read the article on why there is not a 'super-on-top' window... it logically does not work.
Form.TopMost will work unless the other program is creating topmost windows.
There is no way to create a window that is not covered by new topmost windows of another process. Raymond Chen explained why.
I was searching to make my WinForms application "Always on Top" but setting "TopMost" did not do anything for me. I knew it was possible because WinAmp does this (along with a host of other applications).
What I did was make a call to "user32.dll." I had no qualms about doing so and it works great. It's an option, anyway.
First, import the following namespace:
using System.Runtime.InteropServices;
Add a few variables to your class declaration:
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
Add prototype for user32.dll function:
[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);
Then in your code (I added the call in Form_Load()), add the call:
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
[Reference][1]
[1]: http://www.c-sharpcorner.com/uploadfile/kirtan007/make-form-stay-always-on-top-of-every-window/
If by "going crazy" you mean that each window keeps stealing focus from the other, TopMost will not solve the problem.
Instead, try:
CalledForm.Owner = CallerForm;
CalledForm.Show();
This will show the 'child' form without it stealing focus. The child form will also stay on top of its parent even if the parent is activated or focused. This code only works easily if you've created an instance of the child form from within the owner form. Otherwise, you might have to set the owner using the API.
Set Form.TopMost
I had a momentary 5 minute lapse and I forgot to specify the form in full like this:
myformName.ActiveForm.TopMost = true;
But what I really wanted was THIS!
this.TopMost = true;
Set the form's .TopMost property to true.
You probably don't want to leave it this way all the time: set it when your external process starts and put it back when it finishes.
The way i solved this was by making a system tray icon that had a cancel option.
Why not making your form a dialogue box:
myForm.ShowDialog();
The following code makes the window always stay on top as well as make it frameless.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StayOnTop
{
public partial class Form1 : Form
{
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private 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);
public Form1()
{
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
TopMost = true;
}
private void Form1_Load(object sender, EventArgs e)
{
SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
}
protected override void WndProc(ref Message m)
{
const int RESIZE_HANDLE_SIZE = 10;
switch (m.Msg)
{
case 0x0084/*NCHITTEST*/ :
base.WndProc(ref m);
if ((int)m.Result == 0x01/*HTCLIENT*/)
{
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)12/*HTTOP*/ ;
else
m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
}
else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)10/*HTLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)2/*HTCAPTION*/ ;
else
m.Result = (IntPtr)11/*HTRIGHT*/ ;
}
else
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)15/*HTBOTTOM*/ ;
else
m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
}
}
return;
}
base.WndProc(ref m);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= 0x20000; // <--- use 0x20000
return cp;
}
}
}
}
What is the other application you are trying to suppress the visibility of? Have you investigated other ways of achieving your desired effect? Please do so before subjecting your users to such rogue behaviour as you are describing: what you are trying to do sound rather like what certain naughty sites do with browser windows...
At least try to adhere to the rule of Least Surprise. Users expect to be able to determine the z-order of most applications themselves. You don't know what is most important to them, so if you change anything, you should focus on pushing the other application behind everything rather than promoting your own.
This is of course trickier, since Windows doesn't have a particularly sophisticated window manager. Two approaches suggest themselves:
enumerating top-level windows
and checking which process they
belong to, dropping their
z-order if so. (I'm not sure if
there are framework methods for
these WinAPI functions.)
Fiddling with child process permissions to prevent it from accessing the desktop... but I wouldn't try this until the othe approach failed, as the child process might end up in a zombie state while requiring user interaction.
Here is the SetForegroundWindow equivalent:
form.Activate();
I have seen people doing weird things like:
this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;
http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html
I know this is old, but I did not see this response.
In the window (xaml) add:
Deactivated="Window_Deactivated"
In the code behind for Window_Deactivated:
private void Window_Deactivated(object sender, EventArgs e)
{
Window window = (Window)sender;
window.Activate();
}
This will keep your window on top.
Based on clamum's answer, and Kevin Vuilleumier's comment about the other flag responsible for the behavior, I made this toggle that switches between on-top and not on-top with a button press.
private void button1_Click(object sender, EventArgs e)
{
if (on)
{
button1.Text = "yes on top";
IntPtr HwndTopmost = new IntPtr(-1);
SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
on = false;
}
else
{
button1.Text = "not on top";
IntPtr HwndTopmost = new IntPtr(-2);
SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
on = true;
}
}
I did something i little bit differnt kinda found it much easier
so first on Form Load
private void Form1_Load(object sender, EventArgs e)
{
this.Shown += new EventHandler(Form1_Shown);//let your form show up here
}
private void Form1_Shown(Object sender, EventArgs e)
{
Form1.ActiveForm.TopMost = true //and then do your TopMost logic
}