Prevent file association from opening the app again - c#

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.

Related

Check if window from loaded assembly is open

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();
}
}

Make application close when .exe is executed again

I'm currently working on a simple converter tool and was wondering if it's possible to make the application close if I run the .exe again. Some kind of "if two instances run -> close both".
I need this function because I run the application via a shortcut-button inside a third party program. So I would like if my converter app closes once I press this shortcut-button again.
I know it sounds counter intuitive running the exe again to close, but i have to have my app work the same way as the integrated tools in the third party program, and this involves opening and closing tools by pressing their respective toggle-buttons. I can't add a plug-in running inside the third party program, but i CAN add a shortcut button next to the integrated tools. It's a work around, but it will at least act like a toggle button.
You could do something like this:
Process currentProcess = Process.GetCurrentProcess();
bool suocide = false;
foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
{
if (process.MainModule.FileName == currentProcess.MainModule.FileName && process.Id != currentProcess.Id)
{
process.CloseMainWindow();
process.Close();
//process.Kill(); or you can do kill instead
suocide = true;
}
}
if (suocide)
currentProcess.Kill(); // you probably don't care about new process as it is just for closing purpose but if you do then do a proper application exit
You can put it inside your window constructor.
Step 1 Identify a 2nd instance:
I'd recommend the MUTEX answer in this question:
How can I prevent launching my app multiple times?
Step 2 Get that first instance closed
Although the MUTEX answer identifies a second instance, it gives no way to find it and tell it to close.
Solution: Listen with a named pipe in the app (first instance the ClosEE):
//using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public static class SomeClass
{
public static void SomeMethod()
{
Threading.Thread t = new Threading.Thread(() =>
{
try {
while (true) {
dynamic server = new NamedPipeServerStream("Closer", PipeDirection.InOut, -1);
server.WaitForConnection();
if (!server.IsConnected)
return;
dynamic reader = new IO.StreamReader(server);
dynamic casetxt = reader.ReadToEnd();
server.Close();
RootForm.Invoke(() =>
{
if (casetxt == "End") {
System.Environment.Exit(0);
}
});
}
} catch (Exception ex) {
// try/catch required in all child threads as error silently ends app.
// log it...
}
});
t.IsBackground = true;
t.Name = "EnderListener";
t.Start();
}
}
//=======================================================
//Service provided by Telerik (www.telerik.com)
Then when you detect a second instance via the Mutex, send this message from the 2nd instance the "Closer":
dynamic serverloopcount = 1;
dynamic iteration = 1;
dynamic GotServerCount = false;
do {
NamedPipeClientStream client = new NamedPipeClientStream("Closer");
client.Connect();
if (!GotServerCount) {
GotServerCount = true;
serverloopcount = client.NumberOfServerInstances;
}
dynamic reader = new IO.StreamReader(client);
dynamic writer = new IO.StreamWriter(client);
writer.WriteLine("End");
writer.Flush();
writer.Close();
client.Close();
iteration += 1;
} while (iteration <= serverloopcount);
Good luck.

How to close a file in Autocad using C# keeping acad.exe running?

I am using visual studio 2010 and I am having a .DWG file which I want to open in autocad. Till now I have used this.
Process p = new Process();
ProcessStartInfo s = new ProcessStartInfo("D:/Test File/" + fileName);
p.StartInfo = s;
p.Start();
But what I want is to close the file inside the Autocad but not the autocad itself. (Means atocad.exe should be kept running).
Till now I hve used this but its closing the acad.exe not the file.
foreach (Process Proc in Process.GetProcesses())
{
if (Proc.ProcessName.Equals("acad"))
{
Proc.CloseMainWindow();
Proc.Kill();
}
}
Take the Autocad .NET libraries from Autodesk Sites (http://usa.autodesk.com/adsk/servlet/index?id=773204&siteID=123112)
Then you will be able to use Application and Document classes.
They will give you full control over opening and closing documents within the application.
You can find many articles on that, and can ask further questions.
AutoCAD does have an api. there are 4 assemblys. Two for in-process and two for COM.
inprocess :
acdbmgd.dll
acmgd.dll
COMInterop :
Autodesk.Autocad.Interop.dll
Autodesk.Autocad.Interop.Common.dll
this is a method that will open a new instance of AutoCAD or it will connect to an existing running instance of AutoCAD.
you will need to load these .dlls into your project references.
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
namespace YourNameSpace {
public class YourClass {
AcadApplication AcApp;
private const string progID = "AutoCAD.Application.18.2";// this is AutoCAD 2012 program id
private string profileName = "<<Unnamed Profile>>";
private const string acadPath = #"C:\Program Files\Autodesk\AutoCAD 2012 - English\acad.exe";
public void GetAcApp()
{
try
{
AcApp = (AcadApplication)Marshal.GetActiveObject(progID);
} catch {
try {
var acadProcess = new Process();
acadProcess.StartInfo.Arguments = string.Format("/nologo /p \"{0}\"", profileName);
acadProcess.StartInfo.FileName = (#acadPath);
acadProcess.Start();
while(AcApp == null)
{
try { AcApp = (AcadApplication)Marshal.GetActiveObject(progID); }
catch { }
}
} catch(COMException) {
MessageBox.Show(String.Format("Cannot create object of type \"{0}\"",progID));
}
}
try {
int i = 0;
var appState = AcApp.GetAcadState();
while (!appState.IsQuiescent)
{
if(i == 120)
{
Application.Exit();
}
// Wait .25s
Thread.Sleep(250);
i++;
}
if(AcApp != null){
// set visibility
AcApp.Visible = true;
}
} catch (COMException err) {
if(err.ErrorCode.ToString() == "-2147417846"){
Thread.Sleep(5000);
}
}
}
}
}
closeing it is as simple as
Application.Exit();
and forgive the code. its atrocious, this was one of my first methods when i just started developing...
I doubt you will be able to do this unless AutoCAD has an API that you can hook into and ask it to close the file for you.
Your c# app can only do things to the process (acad.exe) , it doesn't have access to the internal operations of that process.
Also, you shouldn't use Kill unless the process has become unresponsive and certainly not immediately after CloseMainWindow.
CloseMainWindow is the polite way to ask an application to close itself. Kill is like pulling the power lead from the socket. You aren't giving it the chance to clean up after itself and exit cleanly.
There is one other possibility - this will only work if your C# code is running on the same machine as the AutoCAD process and it is not really recommended, but, if you are really stuck and are prepared to put up with the hassle of window switching you can send key strokes to an application using the SendKeys command.
MSDN articles here:
http://msdn.microsoft.com/EN-US/library/ms171548(v=VS.110,d=hv.2).aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
Using this you could send the key strokes to simulate the user using the menu commands to close the file.
To perform the closing of file, best way out is to follow the steps at this ObjectARX SDK for c# and change the following code with the below code.
[CommandMethod("CD", CommandFlags.Session)]
static public void CloseDocuments()
{
DocumentCollection docs = Application.DocumentManager;
foreach (Document doc in docs)
{
// First cancel any running command
if (doc.CommandInProgress != "" &&
doc.CommandInProgress != "CD")
{
AcadDocument oDoc =
(AcadDocument)doc.AcadDocument;
oDoc.SendCommand("\x03\x03");
}
if (doc.IsReadOnly)
{
doc.CloseAndDiscard();
}
else
{
// Activate the document, so we can check DBMOD
if (docs.MdiActiveDocument != doc)
{
docs.MdiActiveDocument = doc;
}
int isModified =
System.Convert.ToInt32(
Application.GetSystemVariable("DBMOD")
);
// No need to save if not modified
if (isModified == 0)
{
doc.CloseAndDiscard();
}
else
{
// This may create documents in strange places
doc.CloseAndSave(doc.Name);
}
}
}

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;
}
}

Duplicate process (strange issue)

I am trying to prevent opening help file more than once.
This is the method I am using:
public void openHelp()
{
int count = 0;
string helpPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + #"\MyApp\Help\eHelp.chm";
System.Diagnostics.Process[] helpProcs = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process proc in helpProcs)
{
if (proc.MainWindowTitle == "Sample App Help")
{
count++;
}
}
if (count == 0)
{
System.Diagnostics.Process.Start(helpPath);
}
else
{
}
}
The idea is, if you find the process with the same MainWindowTitle, then do not start a new one.
However, this is not reliable. In some cases it still starts the process, even though one is already running. Is there an issue with a logic?
Thank you.
P.S. Of course the MainWindowTitle is "Sample App Help", at least that is what I see while debugging.
Update:
Issue only occurs when user has minimised help file. So I suspect something happens in the system and I need to check something. Any suggestions?
The Remarks section in Process.MainWindowTitle contains the following note:
The main window is the window that currently has the focus; note that
this might not be the primary window for the process. You must use the
Refresh method to refresh the Process object to get the current main
window handle if it has changed.
Could this perhaps be the cause of your problem?
What about keeping the process id of a newly started help viewer and before starting another one, just check if the old one is still alive.
int id = ...
try
{
var proc = Process.GetProcessById(id);
}
catch
{
// no process running with that id
}

Categories

Resources