Check if window from loaded assembly is open - c#

I have the below code that is executed on a button click to open a window from another application.exe
gAssembly = Assembly.LoadFrom(JLDBConnection.Properties.Settings.Default.DefaultString + #"\JLRetailTerminal.exe");
Type typ = gAssembly.GetExportedTypes().Where(s => s.Name == "RetailWindow").FirstOrDefault();
typ.GetMethod("Show").Invoke(Activator.CreateInstance(typ), null);
gAssembly is a global variable.
How can I check if the "RetailWindow" is already opened and do not open another window?

Are you the only one creating it? If so, you can store the window instance and check the IsVisible:
I.e.:
g_wnd = (Window)Activator.CreateInstance(type);
if (!g_wnd.IsVisible)
{
}

Full Solution.
var gAssembly = Assembly.LoadFrom(JLDBConnection.Properties.Settings.Default.DefaultString + #"\JLRetailTerminal.exe");
Type typ = gAssembly.GetExportedTypes().Where(s => s.Name == "RetailWindow").FirstOrDefault();
//if extend sale is set prevent multiple windows
if (gWindow == null)
{//show if window has never been opened
gWindow = (Window)Activator.CreateInstance(typ);
gWindow.Show();
}
else
{//window has been opened
var windows = Application.Current.Windows; //get all opened windows in applications
if (!windows.OfType<Window>().Contains(gWindow) || !(JLDBConnection.Properties.Settings.Default.ExtendSale == "Yes"))
{ //if window has been closed or not multiple sale database
gWindow = (Window)Activator.CreateInstance(typ);
gWindow.Show();
}
}

Related

How to check the application path installed with ClickOnce

Install three applications, A, B, and C, on one PC as ClickOnce.
And is there a way to know the installation path for B and C when you run application A?
My final goal is to get the installation paths of B and C from A and run these two programs from A.
Thanks.
You can start your ClickOnce application from windows start menu path, for that you need application's publisher name, suite name and product name. You can set those information from project property page -> publish section -> click on options button and set publisher name, suite name and product name.
Now you can use below code to start any ClickOnce application.
string StartMenuProgramDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
string Publisher = "<<Publisher Name>>";
string SuiteName = "<<Suite Name>>";
string ProductName = "<<Product Name>>.appref-ms";
Process.Start(Path.Combine(StartMenuProgramDirectory, Publisher, SuiteName, ProductName));
If you still want to know installation location of ClickOnce application, I am using below way (not sure this is ideal way or not)
You need to set your application display icon with below Code. (This will used to display icon in Control Panel -> Programs and Features)
private void Application_Startup(object sender, StartupEventArgs e)
{
if (ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
string IconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "MyIcon.ico");
if (File.Exists(IconPath))
{
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Uninstall");
string[] subKeys = registryKey.GetSubKeyNames();
for (int i = 0; i < subKeys.Length; i++)
{
RegistryKey subRegistryKey = registryKey.OpenSubKey(subKeys[i], true);
object DisplayName = subRegistryKey.GetValue("DisplayName");
if (DisplayName != null && (DisplayName + "").ToUpper() == ("<<Product Name>>").ToUpper())
{
subRegistryKey.SetValue("DisplayIcon", IconPath);
break;
}
}
}
}
}
Now, Application's ClickOnce registry file is set with icon's path and your icon is available in installation directory, later you can fetch display icon's path with below code and based on that you can also able to find installation location.
string DisplayIconPath;
RegistryKey InstalledClickOncesRegistryKey = null;
RegistryKey InstalledProgramRegistryKey = null;
InstalledClickOncesRegistryKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
if (InstalledClickOncesRegistryKey != null && InstalledClickOncesRegistryKey.SubKeyCount > 0)
{
string[] InstalledClickOncesList = InstalledClickOncesRegistryKey.GetSubKeyNames();
if (InstalledClickOncesList != null && InstalledClickOncesList.Length > 0)
{
foreach (var InstalledClickOnces in InstalledClickOncesList)
{
InstalledProgramRegistryKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + InstalledClickOnces);
if (InstalledProgramRegistryKey != null && InstalledProgramRegistryKey.ValueCount > 0)
{
string RegistryApplicationName = Convert.ToString(InstalledProgramRegistryKey.GetValue("DisplayName"));
if (string.Equals(RegistryApplicationName, "<<Product Name>>"))
DisplayIconPath = Convert.ToString(InstalledProgramRegistryKey.GetValue("DisplayIcon"));
}
}
}
}

Use multiple condition in Process.GetProcesses().Where(Title == args[0] and ProcessName == args[1]")

I'm currently using:
String Title = args[0].ToLower()+ " - Notepad";
Process process = Process.GetProcesses().Where(p => p.MainWindowTitle
== Title).SingleOrDefault();
I have been looking for a way to obtain the two conditions together, since the file passed in argument 0 may be open in a notepad in another language, so I have to work on a massive possibility unnecessarily, and not to, and edit the code to obtain the result in several languages for possible strings in the title of the notepad window in the current language of the system in use:
File_Name.eXtension - Notepad // in: en-EU
File_Name.eXtension - Bloco de Notas in: // en-BR
I'm trying something that looks like:
...
[DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsIconic(IntPtr hWnd);
...
String Title = args[0].ToLower()+ " - Notepad";
String Exec = System.IO.Path.GetFileNameWithoutExtension(args[1].ToLower());
Process process = Process.GetProcesses().Where(p => p.MainWindowTitle
== Title && p.ProcessName == Exec).SingleOrDefault();
if (process != null)
{
var wHnd = process.MainWindowHandle;
if (!IsIconic(wHnd))
{
Console.WriteLine("False"); // Maximized or Nommal Window: File - Notepad.exe //
return;
}
else if (IsIconic(wHnd));
{
Console.WriteLine("True"); // Minimized Window: File - Notepad.exe //
return;
}
}
Console.WriteLine("File not open!"); // Not Founded Windows //
With that, it would be possible to obtain bool for open file only when it was opened by notepad, tying the windowcondition and status already associated with args[0] and switched with args[1].
I could add a call to cmd and make someone similar, then I would save the result and act according to the return, but it is very much back, and I understand that there must be something similar in a method, however, I believe that the expressions I used (my English is poor) ask not to have helped me find a method for the same processing...
wmic process where "Name like '%Notepad.exe%' and CommandLine like '%\\File_Name.eXtension%'" get process id.
The case of Notepad in particular, is that it can have a window name that varies according to the language of the windows, and is a pre-defined "standard" editor.
The case is summarized in how to obtain 2 conditions in a process,
where I will have a title in one condition and a process name in another condition.
Where filters all processes to those that match the condition. SingleOrDefault will search for a single process that matches the condition and if multiple or no process matches it will return null. This is what you should do:
Process process = Process
.GetProcesses()
.SingleOrDefault(p => p.MainWindowTitle == Title && p.ProcessName == Exec);

Prevent file association from opening the app again

I develop a wpf app and i associated a file type named .fcsc. The application opens when the file is doubled clicked but it execute a new instance of the app. What i want is that if the app is already running, open the file in that instance not in a new one.
How can i archive that?
This is what i have when a file is open:
if (AppDomain.CurrentDomain.SetupInformation != null
&& AppDomain.CurrentDomain.SetupInformation.ActivationArguments != null
&& AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData != null &&
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData.Any())
{
// is a file association invoke, open the window
InstallPluginWindow installPluginWindows = new InstallPluginWindow(AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData);
installPluginWindows.Show();
installPluginWindows.Owner = this;
this.Opacity = 0.5;
this.IsEnabled = false;
installPluginWindows.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
installPluginWindows.Closed += installPluginWindows_Closed;
}
else
{
NegotiateLogin();
}
The easy part is setting up a mutex to check for another instance. In your app main or startup code you waould need to so something like the code below:
bool exclusive;
System.Threading.Mutex appMutex = new System.Threading.Mutex(true, "MyAppName", out exclusive);
if (!exclusive)
{
//Another instance running
} ...
GC.KeepAlive(appMutex);
Next, you need to implement a way to message the first application instance and pass in the filename that was double clicked. You can do this in many ways, however, sending a custom message to the main window seems to be the most straightforward. Here is an alternative to message another application.

Obtaining tab title/text using process ID

I don't want to use SetForegroundWindow(), sending keyboard keys or similar techniques, because that can cause issues (unexpected behaviour) in my software.
I have tried to find the title using Cheat Engine program (but haven't found anything useful as Google Chrome seems to work "upside-down").
So I went step ahead, using Process Hacker program I have realized that there is a parent (chrome.exe) process with a valid window handle to the current active tab and all other chrome processes are children of it a.k.a. background processes (with invalid window handle).
By browsing deeper into windows of chrome.exe (parent process), I have found the class name of the window handle being "Chrome_WidgetWin_1" and current active tab's title/text.
Here's a picture of Google Chrome's Task Manager.
I'm looking for a function in C# or C or C++ that will take an integer (process ID) and return a string (tab title/text).
static string GetChromeTabTitle(uint processId)
{
// Assuming I call this function with valid process identifier (PID).
// What do I do next, here??
}
The best way I have found is by using the System.Windows.Automation library. It allows interacting with an application (primarily for accessibility purposes), but you can use it for other purposes like getting Chrome tabs.
Note that this will only work when the Chrome windows is not minimized.
The process is not exactly simple, if you want you can look how I did it in my own project, though it's not something you can just copy it paste, you'll find what you need in the ChromeTabsFinder: https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Plugins.ExpandBrowsersTabs/Chrome/ChromeTabsFinder.cs
Here's the code (you'll need the automation librairies):
public IEnumerable<ITab> GetTabsOfWindow(IntPtr hWnd)
{
var cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(AutomationElement.LocalizedControlTypeProperty);
cacheRequest.Add(SelectionItemPattern.Pattern);
cacheRequest.Add(SelectionItemPattern.SelectionContainerProperty);
cacheRequest.TreeScope = TreeScope.Element;
AutomationElement tabBarElement;
using (cacheRequest.Activate())
{
var chromeWindow = AutomationElement.FromHandle(hWnd);
var mainElement = chromeWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (mainElement == null)
yield break;
tabBarElement = mainElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab"));
}
if(tabBarElement == null)
yield break;
var tabElements = tabBarElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab item"));
for (var tabIndex = 0; tabIndex < tabElements.Count; tabIndex++)
{
yield return "Tab: " + tabElements[tabIndex].Current.Name + ", Index: " + tabIndex + 1;
}
}

Control explorer window programmatically

I want to be able to change the directory of an existing explorer window. Is there an api call to send a "navigate there" message to a window (perhaps with a handle to it)?
First, add a reference to the Microsoft Internet Control library. Then you can use the following code, assuming you already know the window handle for your explorer window:
var shellWindows = new SHDocVw.ShellWindows();
var myFolder = "C:\\temp"; // folder name you want to navigate to
var myHwnd = 0; // whatever window handle you're looking for
foreach (SHDocVw.InternetExplorer shellWindow in shellWindows)
{
if (shellWindow.HWND == myHwnd)
{
shellWindow.Navigate(myFolder);
break;
}
}

Categories

Resources