SetWindowPos implementation - c#

I need open two instances of internet browser and each instance open in different monitor (have two) from console app. I found SetWindowPos method and can't find way to use it. In my case it doesn't do anything...
Please help me way to right using of this method...
Here is the code what I'm using for:
[DllImport("user32.dll")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
public static void Launch()
{
Process process = new Process();
process.StartInfo.FileName = "iexplore.exe";
process.StartInfo.Arguments = "microsoft.com";
process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
process.Start();
Rectangle monitor = Screen.AllScreens[1].WorkingArea;
SetWindowPos(process.MainWindowHandle, 0, monitor.Left, monitor.Top, monitor.Width - 200, monitor.Height, 0);
}
Thanks David

That window handle you're passing to the method is empty as the process has not had time to open its main window.
Try adding a reasonable timeout before calling SetWindowPos, a second or two should be enough:
process.Start();
System.Threading.Thread.Sleep(1000);
process.WaitForInputIdle(); // just in case
SetWindowPos(...);

This code would work, for example, with notepad.exe. With iexplore.exe is not working because process.MainWindowHandle == IntPtr.Zero, and process.HasExited == true. You need to find the right way to find a window handle.

Related

Is there a way to make a winforms window allways on bottom? [duplicate]

Some background
One of my current clients runs a chain of Internet points where customers an access the net through PC:s set up as "kiosks" (a custom-built application "locks" the computer until a user has signed in, and the running account is heavily restricted through the Windows group policy). Currently, each computer is running Windows XP and uses Active Desktop to display advertisements in the background. However, since my client has got problems with Active Desktop crashing on a daily basis (in addition to generally slowing down the computer) I have been asked to develop an application that replaces it.
The problem
I am trying to investigate whether it is possible to build a Windows forms application (using C#) that always stays in the background. The application should lie above the desktop (so that it covers any icons, files etc) but always behind all other running applications. I guess I'm really looking for a BottomMost property of the Form class (which doesn't exist, of course).
Any tips or pointers on how to achieve this would be highly appreciated.
This isn't directly supported by the .NET Form class, so you have two options:
1) Use the Win32 API SetWindowPos function.
pinvoke.net shows how to declare this for use in C#:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
So in your code, call:
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
As you commented, this moves the form to the bottom of the z-order but doesn't keep it there. The only workaround I can see for this is to call SetWindowPos from the Form_Load and Form_Activate events. If your application is maximized and the user is unable to move or minimise the form then you might get away with this approach, but it's still something of a hack. Also the user might see a slight "flicker" if the form gets brought to the front of the z-order before the SetWindowPos call gets made.
2) subclass the form, override the WndProc function and intercept the WM_WINDOWPOSCHANGING Windows message, setting the SWP_NOZORDER flag (taken from this page).
I think the best way to do so is using the activated event handler and SendToBack method, like so:
private void Form1_Activated(object sender, EventArgs e)
{
this.SendToBack();
}
Set your window to be a child window of the desktop (the "Program Manager" or "progman" process). I've succeeded with this method in Windows XP (x86) and Windows Vista (x64).
I stumbled on this method while searching for a way to make a screensaver display as if it were wallpaper. It turns out, this is sort of built in to the system's .scr handler. You use screensaver.scr /p PID, where PID is the process id of another program to attach to. So write a program to find progman's handle, then invoke the .scr with that as the /p argument, and you have screensaver wallpaper!
The project I'm playing with now is desktop status display (shows the time, some tasks, mounted disks, etc), and it's built on Strawberry Perl and plain Win32 APIS (mainly the Win32::GUI and Win32::API modules), so the code is easy to port to or understand any dynamic language with similar Win32 API bindings or access to Windows' Scripting Host (eg, ActivePerl, Python, JScript, VBScript). Here's a relevant portion of the class that produces the window:
do { Win32::API->Import(#$_) or die "Win32::API can't import #$_ ($^E)" } for
[user32 => 'HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)'],
[user32 => 'HWND SetParent(HWND hWndChild, HWND hWndNewParent)'],
sub __screen_x {
Win32::GUI::GetSystemMetrics(SM_CXSCREEN)
}
sub __screen_y {
Win32::GUI::GetSystemMetrics(SM_CYSCREEN)
}
sub _create_window { # create window that covers desktop
my $self = shift;
my $wnd = $$self{_wnd} = Win32::GUI::Window->new(
-width => __screen_x(), -left => 0,
-height => __screen_y(), -top => 0,
) or die "can't create window ($^E)";
$wnd->SetWindowLong(GWL_STYLE,
WS_VISIBLE
| WS_POPUP # popup: no caption or border
);
$wnd->SetWindowLong(GWL_EXSTYLE,
WS_EX_NOACTIVATE # noactivate: doesn't activate when clicked
| WS_EX_NOPARENTNOTIFY # noparentnotify: doesn't notify parent window when created or destroyed
| WS_EX_TOOLWINDOW # toolwindow: hide from taskbar
);
SetParent($$wnd{-handle}, # pin window to desktop (bottommost)
(FindWindow('Progman', 'Program Manager') or die "can't find desktop window ($^E)")
) or die "can't pin to desktop ($^E)";
Win32::GUI::DoEvents; # allow sizing and styling to take effect (otherwise DC bitmaps are the wrong size)
}
This program buffers output to prevent flickering, which you'll probably want to do as well. I create a DC (device context) and PaintDesktop to it (you could use any bitmap with only a couple more lines -- CreateCompatibleBitmap, read in a file, and select the bitmap's handle as a brush), then create a holding buffer to keep a clean copy of that background and a working buffer to assemble the pieces -- on each loop, copy in background, then draw lines and brush bitmaps and use TextOut -- which is then copied to the original DC, at which time it appears on screen.
Yes, function SetWindowPos with flag HWND_BOTTOM should help you. But, from my experience: even after calling SetWindowPos as result of some user operations your window may bring to front.
subclass the form, override the WndProc function and intercept the Windows message(s) that are responsible for moving it up the z-order when it gets activated.
Create a Panel that cover your form, but what ever you want on that Panel, then in the Panel's Click-Event write this.sendback .
I've managed to get rid of the flickering when using setwindowpos...
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}

Set the position of the window of a program

I'm building a C# wpf application that runs on a windows 8 tablet, and I call the virtual keyboard in my application, like this :
Process.Start(Environment.GetFolderPath(Environment.SpecialFolder.System) + System.IO.Path.DirectorySeparatorChar + "osk.exe");
I would like to know if there is a way of setting the position of the keyboard window on my tablet when I open it first. Something like adding XY coordinates to Process.Start
Thanks
Manipulating the OSK window will require your process to be running elevated. (The OSK has certain privileges which means it can't be manipulated by processes which aren't elevated.) But if your app is running elevated, you should find the code below works.
Note that you need to find the OSK window after launching the OSK, rather than getting the MainWindowHandle from the Process object after calling Start(). Due to the way that the OSK starts up, you'll find the HasExited property on the Process object is true, and the MainWindowHandle is not available.
Thanks,
Guy
private void buttonLaunchOSK_Click(object sender, EventArgs e)
{
// Launch the OSK.
Process.Start(Environment.GetFolderPath(Environment.SpecialFolder.System) + System.IO.Path.DirectorySeparatorChar + "osk.exe");
// Wait a moment for the OSK window to be created.
Thread.Sleep(200);
// Find the OSK window.
IntPtr hwndOSK = Form1.FindWindow("OSKMainClass", null);
// Move and size the OSK window.
Form1.MoveWindow(hwndOSK, 0, 0, 800, 300, true);
}
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowTitle);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

Use Chrome or Firefox in Winforms application ?

Is there a way to use Firefox or Chrome (whichever is installed) in a .NET application? I am not thinking about putting gecko or webkit engine in my application but using the browser installed on the system instead (just like the WebBrowser control uses IE). I heard that it is possible via ActiveX controls but didn't find any more info about it.
Well you could use user32.dll to set the parent window of the specified child window (here firefox or chrome).
This is what the result look likes:
First of all I have two small problems and 1 bigger one:
As you can see firefox is not maximized, therefore some content is missing (still looking for a way to fix that [help appriciated])
Because you have to start the process first and then set the parent of the window, Firefox is running outside of your application for a small amount of time.
The biggest problem: The program your are trying to "bind" to your application mustn't run when running your application, because it cannot set the parent of firefox to your program
MSDN for explanation of the methods: http://msdn.microsoft.com/en-us/library/windows/desktop/ff468919(v=vs.85).aspx
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int nWidth, int nHeight, bool repaint);
//hwnd: A handle to the window | x: The new position of the left side of the window. | y: The new position of the top of the window.
//nWidth: The new width of the window. | nHeight: The new height of the window.
//repaint: Indicates whether the window is to be repainted. If this parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs.
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
Process ffprocess = new Process();
private void openProgramAndSetParent()
{
string str = #"C:\Program Files\Mozilla Firefox\firefox.exe"; //Enter full path to Firefox or Chrome
ffprocess.StartInfo.FileName = str;
ffprocess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
ffprocess.Start();
IntPtr ptr = IntPtr.Zero;
while ((ptr = ffprocess.MainWindowHandle) == IntPtr.Zero)
{
Application.DoEvents();
}
Thread.Sleep(1000);
SetParent(ffprocess.MainWindowHandle, panel1.Handle);
MoveWindow(ffprocess.MainWindowHandle, 0, 0, this.Width, this.Height -100, true);
}
private void Form1_Resize(object sender, EventArgs e)
{
try
{
MoveWindow(ffprocess.MainWindowHandle, 0, 0, this.Width, this.Height, true);
}
catch (Exception)
{}
}

Console app to launch multiple instances of IE maximized on multiple monitors

I would like to create a C# console application to launch multiple instances of IE maximized on multiple monitors.
Update: Here is what I have tried so far. When I launch the second IE instance, it does not open on the second screen. I think it has something to do with the MainWindowHandle since IE may have only one main window handle that it shares with multiple windows. The last line of code actually throws an InvalidOperationException. This code works for launching notepad, but not for IE.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestLauncher
{
class Launcher2
{
[DllImport("user32.dll")]
private extern static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
const int SWP_SHOWWINDOW = 0x0040;
static readonly IntPtr HWND_TOP = IntPtr.Zero;
public void Launch()
{
Process p1 = new Process();
p1.StartInfo.FileName = "iexplore.exe";
p1.StartInfo.Arguments = "microsoft.com";
p1.Start();
p1.WaitForInputIdle(2000);
System.Threading.Thread.Sleep(2000);
Rectangle monitor = Screen.AllScreens[0].WorkingArea;
SetWindowPos(p1.MainWindowHandle, HWND_TOP, monitor.Left, monitor.Top, monitor.Width, monitor.Height, SWP_SHOWWINDOW);
Process p2 = new Process();
p2.StartInfo.FileName = "iexplore.exe";
p2.StartInfo.Arguments = "google.com";
p2.Start();
p2.WaitForInputIdle(2000);
System.Threading.Thread.Sleep(2000);
Rectangle monitor2 = Screen.AllScreens[1].WorkingArea;
SetWindowPos(p2.MainWindowHandle, HWND_TOP, monitor2.Left, monitor2.Top, monitor2.Width, monitor2.Height, SWP_SHOWWINDOW);
}
}
}
If possible, I am seeking a solution that is flexible enough to launch other applications besides IE also.

Move external application to the front of the screen

The application that I'm running needs to call a separate app to do some scanning. I'm calling the other application by starting a new System.Diagnostics.Process. Once I get that process, I call a method to give that application the focus. I've tried two different ways to give that external app the focus, but neither are working. Could someone help?
Here's the code:
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, uint windowStyle);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
private static void GiveSpecifiedAppTheFocus(int processID)
{
try
{
Process p = Process.GetProcessById(processID);
ShowWindow(p.MainWindowHandle, 1);
SetWindowPos(p.MainWindowHandle, new IntPtr(-1), 0, 0, 0, 0, 3);
//SetForegroundWindow(p.MainWindowHandle);
}
catch
{
throw;
}
}
First scenario uses the ShowWindow and SetWindowPos methods, the other method uses the SetForegroundWindow method. Neither will work...
Am I using the wrong methods, or do I have an error in the code that I'm using? Thanks all!
Use SetWindowPos, but whenever you don't want the window to be the topmost anymore call it again with the second parameter set to -2 (HWND_NOTOPMOST) instead of -1(HWND_TOPMOST)
Try setting your process to the background ie: this.SendToBack(); This is just another solution, partial fix.

Categories