I'm trying to make a program that opens a browser on each multi-display.
When I did it with a notepad, it worked. However, when it's a browser didn't work and showed the error "System.InvalidOperationException: Process must exit before requested information can be determined". I will appreciate your help with this situation.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int MoveWindow(IntPtr hwnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
private void Form1_Load(object sender, EventArgs e)
{
foreach (Screen item in Screen.AllScreens)
{
//Open a web browser
System.Diagnostics.Process process1 = System.Diagnostics.Process.Start("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe");
//System.Diagnostics.Process process2 = System.Diagnostics.Process.Start("notepad.exe");
process1.WaitForInputIdle();
//process2.WaitForInputIdle();
//Move the browser window
MoveWindow(process1.MainWindowHandle, item.Bounds.X, item.Bounds.Y, item.Bounds.Width, item.Bounds.Height, 1);
//MoveWindow(process2.MainWindowHandle, item.Bounds.X, item.Bounds.Y, item.Bounds.Width, item.Bounds.Height, 1);
}
}
}
}
It seems that msedge.exe use a host process to start different tabs, so we can't use the created process ID to handle it.
Compare created process ID with processes in the Task Manager, the
process 38756 is missing.
Another tool to browse edge relative processes is the built-in task manager of edge.
We can't find process 38756 as well.
So re-search the target edge process is necessary.
This repo https://github.com/alex-tomin/Tomin.Tools.KioskMode demostrate how to move chrome window into different monitors and set as full screen.
I tried to extract the necessary and modify your code like below, hope it helps:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestWinFormsApp
{
// Get full definition of SetWindowPosFlags here:
// https://www.pinvoke.net/default.aspx/Enums/SetWindowPosFlags.html
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_NOREDRAW = 0x0008,
SWP_NOZORDER = 0x0004
}
// Get full definition of ShowWindowCommands here:
// https://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html
public enum ShowWindowCommands
{
Maximize = 3,
Restore = 9,
}
public static class WinApi
{
[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")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load_1(object sender, EventArgs e)
{
var ignoreHandles = new List<IntPtr>();
foreach (Screen item in Screen.AllScreens)
{
this.StartEdgeProcess();
var windowHandle = this.GetWindowHandle("msedge", ignoreHandles);
ignoreHandles.Add(windowHandle);
WinApi.ShowWindow(windowHandle, ShowWindowCommands.Restore);
WinApi.SetWindowPos(windowHandle, IntPtr.Zero, item.Bounds.Left, item.Bounds.Top, 800, 600, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOREDRAW);
WinApi.ShowWindow(windowHandle, ShowWindowCommands.Maximize);
}
}
private void StartEdgeProcess()
{
var process = new Process();
process.StartInfo.FileName = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
process.StartInfo.Arguments = "about:blank" + " --new-window -inprivate ";
process.Start();
process.WaitForInputIdle();
}
private IntPtr GetWindowHandle(string processName, List<IntPtr> ignoreHandlers)
{
IntPtr? windowHandle = null;
while (windowHandle == null)
{
windowHandle = Process.GetProcesses()
.FirstOrDefault(process => process.ProcessName == processName
&& process.MainWindowHandle != IntPtr.Zero
&& !string.IsNullOrWhiteSpace(process.MainWindowTitle)
&& !ignoreHandlers.Contains(process.MainWindowHandle))
?.MainWindowHandle;
}
return windowHandle.Value;
}
}
}
Related
I Would like to know how to check if a process is in fullscreen or not
Thanks
You'll need to use P/Invoke to get access to that kind of information about another process. This code should get you on the right track:
using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApp13
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static WINDOWPLACEMENT GetPlacement(IntPtr hwnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hwnd, ref placement);
return placement;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
private void button1_Click(object sender, EventArgs e)
{
var procs = Process.GetProcesses();
foreach(Process p in procs)
{
if(p.ProcessName == "notepad")
{
var state = GetPlacement(p.MainWindowHandle);
MessageBox.Show(state.showCmd.ToString());
}
}
}
}
[Serializable]
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPLACEMENT
{
public int length;
public int flags;
public ShowWindowCommands showCmd;
public Point ptMinPosition;
public Point ptMaxPosition;
public Rectangle rcNormalPosition;
}
internal enum ShowWindowCommands : int
{
Hide = 0,
Normal = 1,
Minimized = 2,
Maximized = 3,
}
}
(I am an novice/beginner when it comes to C# and Winform)
I have a Windows Forms application with 2 forms. The first form is transparent and is just there to show and hide the second form. The second form is an "Overlay" with some buttons. I have a hotkey to open the second form but I can't get it to close the second form with a hotkey. I have tried some different things and googled a lot. The purpose with this application is to open the overlay menu and from that menu you can switch between applications.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
//Click thurgh
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr Hwnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
//
//Hotkey
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
private IntPtr thisWindow;
private Hotkey hotkey;
//
FormOverlay frm = new FormOverlay();
public Form1()
{
InitializeComponent();
this.BackColor = Color.LimeGreen;
this.TransparencyKey = Color.LimeGreen;
this.TopMost = true;
this.FormBorderStyle = FormBorderStyle.None;
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
}
private void Form1_Load(object sender, EventArgs e)
{
thisWindow = FindWindow(null, "Form1");
hotkey = new Hotkey(thisWindow);
hotkey.RegisterHotKeys();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
hotkey.UnRegisterHotKeys();
}
protected override void WndProc(ref Message keyPressed)
{
Process[] pname = Process.GetProcessesByName("FormOverlay");
if (keyPressed.Msg == 0x0312)
{
if (pname.Length > 0) //my latest try
{
frm.Hide();
MessageBox.Show("1");
}
else
{
frm.Show();
}
}
base.WndProc(ref keyPressed);
}
}
}
Hotkey.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
class Hotkey
{
public enum fsModifiers
{
Alt = 0x0001,
Control = 0x0002,
Shift = 0x0004,
Window = 0x0008,
}
private IntPtr _hWnd;
public Hotkey(IntPtr hWnd)
{
this._hWnd = hWnd;
}
public void RegisterHotKeys()
{
RegisterHotKey(_hWnd, 1, (uint)fsModifiers.Control, (uint)Keys.G);
}
public void UnRegisterHotKeys()
{
UnregisterHotKey(_hWnd, 1);
UnregisterHotKey(_hWnd, 2);
}
#region WindowsAPI
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
#endregion
}
}
If you have any documentation, article, video or tutorial that contains your answer please post it, too, so I don't ask too many questions in the comments.
I have this Form5 and When i load it, is supposed to load google chrome into this window form if chrome is already running or not then is going to start a new process.
Then issue stands at when im closing the Form5 then chrome is not being unembedded from the window form and as you can see on Form5_FormClosed is the code that is supposed to restore the window back to task bar but nothing happens and sometimes after 1 of 100 is working. What im doing wrong?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TrinityItemCreator
{
public partial class Form5 : Form
{
private string urlDestination;
private Process myprocess;
public Form5(string urlTarget)
{
InitializeComponent();
urlDestination = urlTarget;
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
// HWND Constants
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);
// P/Invoke
[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 bool ShowWindow(IntPtr hWnd, int nCmdShow);
private void Form5_Load(object sender, EventArgs e)
{
Process[] pname = Process.GetProcessesByName("chrome");
if (pname.Length != 0) // chrome is already opened
{
//Process.Start("chrome", urlDestination);
Process[] processlist = Process.GetProcesses();
foreach (Process theprocess in processlist)
{
if (theprocess.ProcessName == "chrome")
{
SetParent(theprocess.MainWindowHandle, panel2.Handle);
int left = 1;
int top = 1;
int width = 876;
int height = 656;
SetWindowPos(theprocess.MainWindowHandle, HWND_TOP, left, top, width, height, 0);
myprocess = theprocess;
}
}
}
else
{
myprocess = Process.Start("chrome", urlDestination);
Thread.Sleep(1000);
SetParent(myprocess.MainWindowHandle, panel2.Handle);
int left = 1;
int top = 1;
int width = 876;
int height = 656;
SetWindowPos(myprocess.MainWindowHandle, HWND_TOP, left, top, width, height, 0);
}
}
private void Form5_FormClosed(object sender, FormClosedEventArgs e)
{
SetParent(myprocess.MainWindowHandle, IntPtr.Zero);
ShowWindow(myprocess.MainWindowHandle, 3); // maximize back
ShowWindow(myprocess.MainWindowHandle, 2); // minimize to taskbar
Thread.Sleep(1500);
}
private void ButtonMyTemplates_Click(object sender, EventArgs e)
{
Close();
}
}
}
I am using trance32, and want to launch this process through Jenkins.
When I am launching T32mppc.exe, it is running as system process because of which it is running in background.
I want to launch this process with current user to see it on foreground without inserting username and password.
My system is having only 2 kind of process. System and User. No other users are there.
Attaching my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Globalization;
using System.Xml.Linq;
using System.Diagnostics;
using System.Threading;
using System.Configuration;
namespace Console_Startapps
{
class Program
{
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
[DllImport("User32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("TRACE32\t32api64.dll")]
public static extern int T32_ResetCPU();
private void startT32app()
{
IntPtr handle;
try
{
Console.WriteLine("T32 launching");
string path = #"C:\T32\bin\windows64\t32mppc.exe";
string args = #"C:\T32\config.t32";
ProcessStartInfo procInfo = new ProcessStartInfo(path, args);
procInfo.CreateNoWindow = false;
procInfo.UseShellExecute = true;
procInfo.WindowStyle = ProcessWindowStyle.Normal;
Process[] targetProcess = Process.GetProcessesByName("t32mppc.exe");
//if (targetProcess.Length > 1)
if (this.IsProcessOpen("t32mppc") == 1)
{
Console.WriteLine("TC32 already running");
}
else
{
Process procRun = Process.Start(procInfo);
handle = procRun.MainWindowHandle;
SwitchToThisWindow(handle, true);
}
//SetForegroundWindow(handle);
}
catch
{
Console.WriteLine("Failed to launch T32");
}
}
private int IsProcessOpen(string name)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.ToLower().Contains(name.ToLower()))
{
return 1;
}
}
return 0;
}
static void Main(string[] args)
{
Program Beginapps = new Program();
Beginapps.startT32app();
}
}
}
Have you tried to impersonate another user?
https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx
Basically, you can run another process with a given credential
How do you do Impersonation in .NET?
I have the method DrawToBitmap but how do I get the handle of a process window ?
For example notepad.exe
What I want to do is when I start the program if the process(for example notepad.exe) not running yet then start it once it's running get it's window handle and save the window to image file.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Elgato.BaseClasses.UI;
using Elgato;
using Elgato.BaseClasses.Streaming;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("USER32.DLL")]
private static extern bool PrintWindow(HandleRef hwnd, IntPtr hdcBlt, int nFlags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
public static Image DrawToBitmap(IntPtr handle)
{
RECT rect = new RECT();
GetWindowRect(handle, ref rect);
Bitmap image = new Bitmap(rect.Right - rect.Left, rect.Bottom - rect.Top);
using (Graphics graphics = Graphics.FromImage(image))
{
IntPtr hDC = graphics.GetHdc();
PrintWindow(new HandleRef(graphics, handle), hDC, 0);
graphics.ReleaseHdc(hDC);
}
return image;
}
}
}
To check whether a process exists and get the window handle if it exists, use the following code:
var processes = Process.GetProcessesByName("notepad");
if (processes.Length > 0)
{
var handle = processes[0].MainWindowHandle;
}
else
{
// Notepad is not started
}