c# - WPF command-line argument for opening file gives infinite loop - c#

This is a weird one! I am working on an application that reads vCard files, which contain contact etc. information for a person. Each file may contain separate 'sections' that each contain the details for one person, which are separated by BEGIN:VCARD [data here] END:VCARD.
To enable my users to view all of the different details, I've allowed my program to populate the textboxes in my app with the details and then open a new Window and do this with that one, but for each of the different sections in the file.
The problem comes about when my program opens when a vCard file has been double clicked in Explorer. It keeps looping through the vCard. I don't know what to do, but below is my problematic code:
public void readVcard(string fname)//Reads vCard and then loops through sections
{
try
{
using (StreamReader r = new StreamReader(fname))
{
string input = File.ReadAllText(fname);//read through file
String[] vArray = input.Split(new string[] { "BEGIN:VCARD" }, StringSplitOptions.None);
int i;
for (i = 1; i < vArray.Length; i++)
{
MainWindow a = new MainWindow();
a.parser(vArray[i]); //Parser is the function that populates the app
a.Show();
}
return;
}
}...
This function is called from here:
void MainWindow_Loaded(object sender, RoutedEventArgs e)//Processes a file when opened externally
{
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
readVcard(fname);
}
}
If anyone could help, it would be greatly appreciated.

I think that Artyom is on the right track.
Every time you create another MainWindow and load it you will be getting the current applications argurment and jumping back in to readVcard, which will process the same vCard that you are already processing and open yet another MainWindow which will continue the process.
Consider moving all of the code you have inside of MainWindow_Loaded() to the Startup event for your application. That way it will only get called once when your program first loads, instead of every time you create a new window.
To do this you need to register for the event in your App.xaml file like so:
<Application x:Class="MyProgram.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
</Application>
And then in the code behind App.xaml you put your code for reading the vCard. Like this:
namespace MyProgram
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
readVcard(fname);
}
}
}
}

When you create and show new MainWindow (a.Show()), the MainWindow_Loaded event fires again and it again calls a readVcard method. So there is an infinite loop.
Or may be not really infinite, because, I belive, some time later a StackOverflowException may happen.
You just need to review startup logic, so readVcard will launch not in the MainWindow_Loaded event, but, for example, in the Main method (in program.cs file). Or you may add some flag, which will be set when readVcard method first called.

I get it! I've now got the following code in App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
base.OnStartup(e);
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
MainWindow mw = new MainWindow();
mw.readVcard(fname);
}
}
}
It works fine! Thanks everyone. BTW the following blog contains the command-line info I originally used if anyone needs it: http://blogs.msdn.com/b/avip/archive/2008/10/27/wpf-supporting-command-line-arguments-and-file-extensions.aspx.

Related

Passing arguments to run application

Looking for help on this project, I am new and might not know all terms so please bare with me.
Project contains multiple winforms panels. Start one is my dashboard then I have others that do specific functions/features. What I need to be able to do is call up the separate winforms from an outside bat file that comes from a third party software (I have no control over them).
So what I would like to do is this programname.exe runs the dashboard programname.exe -a runs a different winform with in my program. I am not sure what this is called to do this.
Any help would be great even if a place to go and look it up.
namespace Versi_Send_Email
{
public partial class DashBoard : Form
{
public DashBoard()
{
InitializeComponent();
}
private void DashBoard_Load(object sender, EventArgs e)
{
INIFile inif = new INIFile(#"c:\test\mailsettings.ini");
sitetxtbox.Text = inif.Read("Properties", "site");
emailtotxtbox.Text = inif.Read("Properties", "personto");
cctotxtbox.Text = inif.Read("Properties", "ccto");
bcctextbox.Text = inif.Read("Properties", "bcto");
}
This could be a switch statement or a simple if-else statement. In WinForms application and in the main form loaded event, you can read the command line arguments like this
string[] args = Environment.GetCommandLineArgs();
foreach(string arg in args){
if (arg=="EmployeeForm")
{
EmployeeForm.ShowDialog()
else if (arg=="Department")
Department.ShowDialog()
}
//after all arguments are read, you can simply kill the application and main window will never appear
Application.Current.Shutdown();
return;

Passing Command Line Arguments from VB to C# WPF Application

I have a C# WPF application which I want to be able to open from another existing application, which was written in VB.net. As far as the c# application goes, I think I know how to get command line parameters that are passed to it two different ways, which I got while researching google and using others' answers.
App.xaml Header
<Application x:Class="ChallengeHandler.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChallengeHandler"
Startup="Application_Startup">
App.xaml.cs Method 1
private void Application_Startup(object sender, StartupEventArgs e)
{
string[] args = Environment.GetCommandLineArgs();
if (args.Length < 1)
{
MessageBox.Show("No parameter provided. Failed to run.");
Shutdown();
}
else
{
MainWindow wnd = new MainWindow(args[0]);
wnd.Show();
}
}
The above method will result in the application opening but none of the data, which relies on the parameter, is populated. So the comboboxes and stuff in the views are just empty. That fails.
App.xaml.cs Method 2
private void Application_Startup(object sender, StartupEventArgs e)
{
if (e.Args.Length < 1)
{
MessageBox.Show("No parameter provided. Failed to run.");
Shutdown();
}
else
{
MainWindow wnd = new MainWindow(e.Args[0]);
wnd.Show();
}
}
This method just shows the error messagebox each time, as the args is empty.
I have a feeling the issue is when I'm trying to open the application from the VB.NET application and pass the string parameter to the c# app from there. But I am out of ideas on how to pass a string like a command line parameter from the VB.net code.
Calling from a VB.net Application
Dim sPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Programs) + "\Microsoft\ChallengeHandler.appref-ms"
Dim pHelp As New ProcessStartInfo
If System.IO.File.Exists(sPath) Then
pHelp.FileName = sPath
pHelp.Arguments = "097"
pHelp.UseShellExecute = True
pHelp.WindowStyle = ProcessWindowStyle.Normal
Dim proc As Process = Process.Start(pHelp)
End If
I have tried the VB code without the
pHelp.UseShellExecute = True
pHelp.WindowStyle = ProcessWindowStyle.Normal
with no avail; I added them in the hope the shell execute would force the parameters as command line parameters. I have also tried this in VB:
2nd VB Method
Dim sPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Programs) + "\Microsoft\ChallengeHandler.appref-ms"
If System.IO.File.Exists(sPath) Then
System.Diagnostics.Process.Start(sPath, "097")
End If
Any insight would be GREATLY appreciated! Thanks.
I see you're using "\Microsoft\ChallengeHandler.appref-ms". This is a ClickOnce application. Getting the parameters for a ClickOnce application is completely different from a normal application. You need to use ApplicationDeployment.CurrentDeployment.ActivationUri.Query and HttpUtility.ParseQueryString for retrieving them.
I believe to send them across you'll have to add them to the launch url by using "?param=value". I've only tried launching it from a web page, so I'm unsure if this is how it works.
The method you're currently using is valid for normal application. If you can locate the exe and launch that directly, you should be fine.
I created 2 projects: Line command C# invoker and a WPF Test application;
The invoker code on Program.cs:
namespace WPFInvoker
{
class Program
{
static void Main(string[] args)
{
Process.Start(#"C:\Users\...\bin\Debug\WPF_Test.exe", "example_parameter");
}
}
}
Then on the WPF App.xaml I have the startup event app_Startup and the main form MainWindow.xaml:
<Application x:Class="WPF_Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_Test"
Startup="app_Startup"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
And the App.xaml.cs code is:
namespace WPF_Test
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
void app_Startup(object sender, StartupEventArgs e)
{
if (e.Args != null && e.Args.Length > 0)
{
MessageBox.Show(e.Args[0]);
}
else
{
MessageBox.Show("e.Args is null");
}
}
}
}
When I open the WPFTest.exe with a double click shows a MessageBox with the message "e.Args is null":
Application without parameters
But if I open the WPFTest application through WPFInvoker:
Application with parameter
Finally I close the MessageBox and the MainWindow.xaml its shown.

How to open a file with an executable already open?

i have a C#/WPF project that represents a dbf editor that allows you to open files in 3 ways:
- with apposit button in toolbar
- with drag & drop
- with double click on file
Now i use a TabControl that contain every dbf open.
I can handle it with my internal button and drag add item to container.
if I open a file with the double click and there is an open instance I would like to add it to the container and instead open a new instance.
My code :
App
public partial class App : Application
{
private static Editor mainWindow = null;
protected override void OnStartup(StartupEventArgs e)
{
Editor mainWindow = new Editor(e.Args);
mainWindow.Show();
}
}
Editor:
public partial class Editor : Window
{
ChooseMessage.Choose choose;
public Dictionary<int, DBFStructure> ds;
string DbfName;
private string[] OldNew;
public Editor(string[] e)
{
InitializeComponent();
ds = new Dictionary<int, DBFStructure>();
OldNew = new string[2];
choose = ChooseMessage.Choose.OK;
if (e.Length > 0)
if (File.Exists(e[0]) && e[0].EndsWith(".dbf", StringComparison.InvariantCultureIgnoreCase))
EffOpen(e[0]);
}
private void dbf_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, true) == true)
{
string filename = ((string[])e.Data.GetData(DataFormats.FileDrop, true))[0];
if (File.Exists(filename) && filename.EndsWith(".dbf", StringComparison.InvariantCultureIgnoreCase))
EffOpen(filename);
}
}
....
}
I apologize for code display but I can not set it correctly.
my problem is to intercept the opening of a dbf from the last open editor instance and add it to the controltab, otherwise create a new instance.
P.S. EffOpen(filename) represents the method that, by passing the fil name, loads it and adds it to the container
Thanks to all
When you open a file with a double click, a new application instance will be started by Windows. This is by design, you cannot change this.
In this new instance you have no direct references to the objects of your first application instance, so you cannot just add the file being opened to the TabControl of the first instance.
You need to implement it like that:
since you can open files via double click, your application can handle files using the command line arguments - that's good
you need a way to check whether there is an instance already running - use a Mutex with a dedicated and unique name to do that; try creating a Mutex on app startup and check the out parameter of the Mutex(bool, string, out bool) constructor - if it's false, then there is an instance already
you need a way to tell the already running instance that it needs to open a file that has been passed as the command line argument to the second instance; use any of the inter-proc methods that better suits you: a NamedPipe would be a pretty simple way

How to start WPF based on Arguments

I'm currently developing an application that does some file manipulation and I want to be able to do the manipulation through the console or via an UI (I chose WPF).
I pretty much want to say: (psuedo)
if ( Environment.GetCommandLineArgs().Length > 0 )
{
//Do not Open WPF UI, Instead do manipulate based
//on the arguments passed in
}
else
{
//Open the WPF UI
}
I've read about a few different ways of starting the WPF Window/application programmatically like:
Application app = new Application ();
app.Run(new Window1());
But I'm not entirely sure I want to just plug this into a Console Application.
Does anyone have best practices or recommendations on how I can achieve this? The main processing functionality is in a Helper class I created. So basically I either want a static start method (like standard Console Application creates) or the UI to access the Helper class depending on the arguments passed in.
In Application class there is an event "StartUp" you can use it . It provide you the args you provide through command prompt. Here is an example from MSDN:
App.xaml
<Application x:Class="WpfApplication99.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
</Application>
App.xaml.cs
public partial class App : Application
{
void App_Startup(object sender, StartupEventArgs e)
{
// Application is running
// Process command line args
bool startMinimized = false;
for (int i = 0; i != e.Args.Length; ++i)
{
if (e.Args[i] == "/StartMinimized")
{
startMinimized = true;
}
}
// Create main application window, starting minimized if specified
MainWindow mainWindow = new MainWindow();
if (startMinimized)
{
mainWindow.WindowState = WindowState.Minimized;
}
mainWindow.Show();
}
}
I hope this will help.
There are 2 options to get the command line arguments
1) If you want to read the arguments OnStartup. This is good for global access of the args.
Override OnStartup in App.xaml.cs and look at the Args property of the StartupEventArgs class.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
foreach (string arg in e.Args)
{
// TODO: whatever
}
base.OnStartup(e);
}
}
2) Another easy way is to read the arguments from the Environment Object.
Environment.GetCommandLineArgs();
This can be used from anywhere in the application like from the Form / Page also.
I Prefer override OnStartup because the Startup event is usually registered in "App.xaml" and I sometimes don't want to modify it. And OnStartup function can provide a way to do some preprocess before Startup event is invoked. This is why we can override OnStartup!

C# - Program without a Window

I'm wondering whether it is possible to 'turn off' my main Window from loading automatically when my program starts with a command-line argument (i.e. when a file name is passed). The problem I have is that my program loads when a file associated with it is clicked, but does so by opening another main window and using that. The problem I have is that the program still launches the MainWindow afterwards, thus opening two Windows, one with the file contents and one that is empty.
How do I prevent the blank Window? As I see it, I either stop it from opening the main Window, close the main Window or make the program pass the file to the main Window. My problem is that I don't know which of these would be the best or how to to do it.
This is the code:
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
base.OnStartup(e);
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
MainWindow mw = new MainWindow();
mw.Show();
mw.readVcard(fname);
Application.Current.Windows.
}
}
EDIT:
My solution is at the bottom.
I believe you can add a separate class with its own Main method and set that to be the entry point of your executable. Then you can parse the method arguments there and either bring up the main window or not.
(I'm assuming this is a WPF app - it's simpler in a WinForms app as you can modify the original Main method directly.)
I assume you use WPF? You'll want to replace the entry point (Main) that WPF supplies for you. Then, you can start WPF or not depending on the command-line arguments. See this question for more info:
Replacing the WPF entry point
Remove the WindowUri from APP.XAML page.
That will not show any window. Also, add your logic on app() constructor or startup event.
I'd rewrite your code as follows:
protected override void OnStartup(StartupEventArgs e)
{
// start application window
MainWindow mw = new MainWindow();
mw.Show();
// store argument and read card info
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
mw.readVcard(fname);
}
}
This assumes that the method MainWindow.readVcard(string) simply loads data into the current instance.
Hi everyone and thanks for getting back to me, sorry I've not come back sooner. Part of what Nate said was correct in that I needed to call my Window earlier and then, if there was the command-line argument, parse the file name. The issue as I saw it was that it still started up a main Window afterwards because that was set as my startup, So I used the information suggested by Qwertie to alter my app.xaml, which meant that it pointed to a different startup, which in turn meant that the Window wasn't opened unnecessarily.
In ' App : Application ' class in App.xaml.cs:
private void OnStartUp(object sender, StartupEventArgs e)
{
OnStartup(e);
}
protected override void OnStartup(StartupEventArgs e)
{
MainWindow mw = new MainWindow();
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
//base.OnStartup(e);
if (Application.Current.Properties["ArbitraryArgName"] != null)
{
string fname = Application.Current.Properties["ArbitraryArgName"].ToString();
mw.Show();
mw.readVcard(fname);
//Application curApp = Application.Current;
//curApp.Shutdown();
}
else if (e.Args.Count() == 0)
{
mw.Show();
}
}
In App.xaml:
<Application x:Class="Vcardviewer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="OnStartUp"
>
<Application.Resources>
</Application.Resources>
</Application>
<!--StartupUri="MainWindow.xaml"-->
Thanks again to everyone for their answers. Regards to you all.
I edit the app.xmal to remove the start URL. I then edit the app.xaml.cs and add a constructor for App and do my processing there - I use "Shutdown()" to close the application.
You can open windows as needed. When I launch other windows, I use the OnStartup event to do it...

Categories

Resources