I am programming a .NET Framework application that open a website in Chrome and change it to full screen.
The problem I am facing is to determine if the web is already open (I don't want to open multiple pages unnecessary), In that case the program should just switch the current active window to the correct tab and switch to full screen.
The problem is only the active tab is visible to the C# program at a time, so the program needs to check each tab manually (by switching the active tab and check is title).
I don't care if the screen is flickering throw all the tabs.
I found this solution online:
private void GetCaptionOfActiveWindow()
{
Process[] procs = Process.GetProcessesByName("Chrome");
// to find the tabs we first need to locate something reliable - the 'New Tab' button
if (procs.Length == 0)
{
//some code to open chrome with the correct web site and change to full screen
return;
}
IntPtr hWnd = IntPtr.Zero;
int id = 0;
int numTabs = procs.Length;
foreach (Process p in procs)
{
if (p.MainWindowTitle.Length > 0)
{
hWnd = p.MainWindowHandle;
id = p.Id;
break;
}
}
SetForegroundWindow(hWnd);
SendKeys.SendWait("^1"); // change focus to first tab
Thread.Sleep(100);
int next = 1;
string title;
while (next <= numTabs)
{
try
{
title = Process.GetProcessById(id).MainWindowTitle.Replace(" - Google Chrome", "");
//richTextBox1.Text += title + "\n";
if (title.ToLower().Contains("some web site name"))
{
// some code to change the current active tab and change to full screen.
return;
}
next++;
SendKeys.SendWait("^{TAB}"); // change focus to next tab
Thread.Sleep(100);
}
catch (Exception ex)
{
// Chrome internal process, doesn't have tab.
}
}
//richTextBox1.Text += "web not exists\n";
//some code to open new tab with the correct web site and change to full screen
}
This solution works fine if all the Chrome tabs are open in the same window.
My problem is to figure out what to do if the tabs are scatterd across multiple Chrome Windows.
I have created WinForm application which has webBrowser control docked with Fill property and redirects the user to the URL user provides.I am executing the winForm process from chrome Extension and getting the handle of chrome and changing the parent of my child process(WinForm) to chrome browser process.
I am losing the focus when I Minimize the chrome and restore again.
But on switching the tab or either from Alt+Tab to locate my winForm .exe the focus comes back.
How to set the focus of my .exe whenever the user comes to my active tab.
I have written the application in C# and used the Unmanaged code SetParent(child handle, parent handle) API to attach my .exe.
Some links I already tried :
https://www.codeproject.com/Articles/101367/Code-to-Host-a-Third-Party-Application-in-our-Proc
Docking Window inside another Window
Hosting external app in WPF window
Code Snippet :
Listening the below C# code snippet in a thread in an infinite while loop to get the messages from my main.js
private void SetAsParent()
{
try
{
Process[] p = Process.GetProcessesByName("chrome");
foreach (Process item in p)
{
if (!String.IsNullOrEmpty(item.MainWindowTitle))
{
if (item.MainWindowTitle.Contains("Some Title to match my condition to show the exe...."))
{
this.Top = 78;
this.Left = 0;
SetParent(this.Handle, item.MainWindowHandle);
this.Visible = true;
this.Show();
this.Focus();
webBrowser.Navigate(some URL);
webBrowser.Size = new Size(1366, 979);//dynamic values coming from chrome main.js message via postMessage()
}
}
else
{
this.Hide();
}
}
this.Size = new Size(1366, 979);
}
catch (Exception ex)
{
Debug.WriteLine("Inside Form Load : {0}", ex.Message.ToString());
}
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
Chrome Extenion main.js :
chrome.windows.onFocusChanged.addListener(function() {
chrome.windows.getCurrent(function(window){
console.log(window.state);
if(window.state == "normal")
{
Some message to port...
}
else if(window.state == "maximized")
{
Focus message to port...
}
else if(window.state == "minimized")
{
Minimised message to port....
}
});
});
Expected Output :
On switching the tab the focus is retained but on minimizing and restored the focus is lost.
I know there are lots of article over Google related to this, and believe me I have tried almost everything.
So the problem is, I have a wpf application in which when user focuses on any input control like textbox I am showing external touch keyboard using TabTip.exe.
Following code:
public class KeyboardHelper
{
private const string PROCESS_NAME = "TabTip";
private const string PROCESS_PATH = "Common Files/Microsoft Shared/ink/TabTip.exe";
public static void ShowKeyboard()
{
Process keyboard = null;
Process[] pname = Process.GetProcessesByName(PROCESS_NAME);
if (pname.Length == 0)
{
keyboard = new Process();
}
else
{
keyboard = pname[0];
}
string processPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), PROCESS_PATH);
keyboard.StartInfo.FileName = processPath;
keyboard.Start();
}
public static void Closekeyboard()
{
Process[] pname = Process.GetProcessesByName(PROCESS_NAME);
if (pname.Length > 0)
{
pname[0].Kill();
}
}
}
Now problem starts here keyboard open but if it is in docked mode, it causes my app to resize almost half of the screen. And when I close the keyboard app remains same in size, I want to restore to full screen state again.
Controlling my size from cs because it comes from a DB
WindowState = maximized;
ResizeMode= NoResize;
WindowsStyle = None;
Any work around to solve the issue.
I have tried UpdateLayout, Dispatcher.BeginInvoke, Invalidate methods.
Thanks Jason, now I know the cause or can say case scenario of the problem. In my app I have to decide the startup page at runtime, So I am using code like this
private void Application_Startup(object sender, StartupEventArgs e)
{
//Set startup page for the application.
MainWindow window = new MainWindow();
window.ShowDialog();
}
And this is the problem. If I use normal default way than everything works fine.
I have a modal window that works on desktops with multiple monitors however when I attempt to run the application on a laptop its not overlapping the task bar. Below is the code I'm using to launch it.
if (result != DialogResult.Yes) return;
try
{
using (var fullscreenForm = new fullscreenkForm(eventDetails))
{
fullscreenForm.ShowDialog(this);
}
}
finally
{
Application.Exit();
}
Inside the application I have:
WindowState = Maximized
StartPosition = CenterScreen
AutoSizeMode = GrowOnly
FormBorderStyle = None
TopMost = true
Any help thoughts or help is well appreciated
I have a small launcher program, it loads a Splash screen on it's own thread and displays it. If a set of conditions are met it needs to launch another application and keep the splash screen visible till the other application says it is ok to close the splash screen.
The Launcher will always have a lifetime that starts before Child App and ends after Child App closes.
Here is some snippets of relevant code
The common DLL:
namespace Example.Common
{
public partial class SplashScreen : Form
{
public SplashScreen()
{
InitializeComponent();
}
static SplashScreen splashScreen = null;
static Thread thread = null;
static public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashScreen != null)
return;
thread = new Thread(new ThreadStart(SplashScreen.ShowForm));
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
// A static entry point to launch SplashScreen.
static private void ShowForm()
{
splashScreen = new SplashScreen();
Application.Run(splashScreen);
}
// A static method to close the SplashScreen
static public void CloseForm()
{
splashScreen.Close();
}
}
}
The Inital Launcher:
/// <summary>
/// This application is a small launcher to launch the real graphical launcher. It is small and lightweight and should be rarely be updated.
/// It will call the ProgramLauncher, the program launcher will return in it's status code the PID of the instance it launched or -1
/// if no subsequent program was started.
/// </summary>
[STAThread]
static void Main()
{
//Show the Splash screen;
Example.Common.SplashScreen.ShowSplashScreen();
//(Snip)
if (rights == UserRights.None)
{
SplashScreen.CloseForm();
MessageBox.Show("Your user does not have permission to connect to the server.", "Unable to logon", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//If the user has full desktop access, give it to them and launch a new instance of the launcher.
else if (rights.HasFlag(UserRights.FullDesktopAccess))
{
Process explorer = new Process();
explorer.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
if (explorer.Start() == false)
{
MessageBox.Show("Explorer failed to start.");
}
else
{
//Close the splash screen.
SplashScreen.CloseForm();
//If the user can shadow start a new instance of the launcher inside explorer.
if (rights.HasFlag(UserRights.ShadowNormalUser) || rights.HasFlag(UserRights.ShadowDemoUser))
{
//Start a new copy of the program so people can use it to shadow easily.
var shadowProc = new Process();
shadowProc.StartInfo.FileName = "ProgramLauncher.exe";
shadowProc.StartInfo.UseShellExecute = false;
shadowProc.Start();
}
explorer.WaitForExit();
}
}
else
{
Process programLauncher = new Process();
programLauncher.StartInfo.FileName = "ProgramLauncher.exe";
programLauncher.StartInfo.UseShellExecute = false;
//Launch the graphical launcher.
programLauncher.Start();
programLauncher.WaitForExit();
//Check to see if the graphical launcher launched some other process.
if (programLauncher.ExitCode >= 0)
{
//If there was a pid, don't close the micro launcher till after it closes.
Process runningProcess = Process.GetProcessById(programLauncher.ExitCode);
runningProcess.WaitForExit();
}
}
}
What is the easiest way to let ProgramLauncher close the SplashScreen instance MicroLauncher created?
You need to have SplashScreen pass it's window handle (HWND) to ProgramLauncher. Then, ProgramLauncher can use the SendMessage winapi function to send a WM_SYSCOMMAND message to the target window:
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
In WinForms, you can get a form's native handle with Handle.
The platform invoke code for SendMessage is here.
At least I don't see an easier way now, but I think it's easier than any IPC mechanism out there.
There are lots of ways of doing this, with pros and cons to each. Possibly the easiest way is to redirect standard output from your ProgramLauncher process and wire it up to an event in the MicroLauncher application (see here for an example). From your ProgramLauncher program, you write a certain message to standard output. When that message is received by MicroLauncher, you close the window.
Another option is to pass the HWND of your splash screen to ProgramLauncher as a command-line parameter, then ProgramLauncher can use SendMessage(WM_SYSCOMMAND, SC_CLOSE) to close the window (see here for an example).
You can also look into methods of IPC, sending custom Windows messages, or probably a thousand other possibilities, but those two ideas may get you started.
Easiest way I can think of: have the child app create a named mutex, and have the parent app wait until someone's created it, checking every now and then.
Not very elegant and open to abuse (where another app intentionally creates a mutex with the same name), but in practice, I doubt that'll be a problem.