Passing Command Line Arguments from VB to C# WPF Application - c#

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.

Related

C# Add Windows Form Application To A Console Application

I have a console application doing some things in the background, and I want to make it so that the user can change some things that the console application is doing.
I want to add a Windows Form Application to just get the user input and send it to the console application to use. I have looked and couldn't find what I'm looking for
I found this question - Add GUI to Existing Code in Visual Studio - but this didn't help me much.
I have this:
bool OnOrOff = true;
but I want to check if a check box from the windows form is checked instead of seting it to true like this:
on Windows Form the checkbox is named CheckOnOrOff and it is checked.
bool OnOrOff = CheckOnOrOff.Checked();
I assume that the user can change the settings while the console application is running and the effect should be taken immediately. Adding your winforms application as reference in console will not help since it's will be a different application. So this is what I suggest:
Make a new winforms application and change the output type from 'Windows Application' to 'Console Application' so we can see the console. Port your console logic proccess to the winforms project
Add a new static class which will hold flag between winforms and console. Example:
namespace FormWithConsole {
public static class SharedData {
public static bool Feature01 { get; set; }
}
}
Add a checkbox your Windows Form and add code bellow to the checkbox changed event:
private void checkBox1_CheckedChanged(object sender, EventArgs e) {
SharedData.Feature01 = checkBox1.Checked;
}
Add a button to start your console process, and use thread to start your console process as follow:
Thread thread;
private void button1_Click(object sender, EventArgs e) {
if (thread != null) {
try {
Console.WriteLine("Aborting current process");
thread.Abort();
thread = null
}
catch (ThreadAbortException) { }
}
ConsoleProcess process = new ConsoleProcess();
thread = new Thread(process.StartProcess);
thread.IsBackground = true;
thread.Start();
}
This is ConsoleProcess class, which hold your console logic
class ConsoleProcess {
public void StartProcess() {
while (true) {
if (SharedData.Feature01) {
// Do something here
}
Console.WriteLine("Regular process here...");
}
}
}
If you want the form minimized to system tray refer to minimize app to system tray
I think you should design a database to store the user input. Your console project and window project will run and manage by this database.
You can take input from windows form (by User) and then pass it to Console application by using parameter argument in Console Application.
The parameter of the Main method is a String array that represents the command-line arguments
So, if I had a console program (MyCApp.exe) like this:
class Program
{
static void Main(string[] args)
{
foreach (var arg in args)
{
Console.WriteLine(arg);
}
}
}
That I started at the command line like this:
MyCApp.exe Arg1 Arg2 Arg3 The Main method would be passed an array
that contained three strings: "Arg1", "Arg2", "Arg3".
If you need to pass an argument that contains a space then wrap it in quotes. For example:
MyCApp.exe "Arg 1" "Arg 2" "Arg 3"
Command line arguments commonly get used when you need to pass information to your application at runtime. For example if you were writing a program that pass basic information to copies a file from one location to another you would probably pass the two locations as command line arguments. For example:
MyCApp.exe C:\file1.txt C:\file2.txt Copyit
Here 'C:\file1.txt' is first Argument, 'C:\file2.txt' is first Argument, 'Copyit' is third Argument

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!

WPF Application exit code

I am trying to set and get the application exit code .
I am trying to do something following :
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if ( e.Args.Length != 0)
{
}
else
{
new MainWindow().ShowDialog();
}
Environment.ExitCode = 110;
this.Shutdown();
}
And then I am trying in cmd to get it by echo %ERRORLEVEL%
But I get always result 0 , any idea what is the issue ?
For WPF, try
Application.Current.Shutdown(110);
Note that the application needs to be running as a console app. This answer is the easiest way I know of; the accepted answer looks more difficult.
An easy test to tell if you're running in console mode: call your app from the command line (make sure your code doesn't shut down right away). The main window should be showing. If you can type another command in the console, your app is not running in its context. The command prompt should be locked, waiting for you to close the window.
You can do it in Main method. Just change its return-value-type to int instead of void and return your exit-code
static int Main(string[] args) {
// something to do
Console.ReadKey();
return 110;
}
UPDATE:
To create a custom Main in WPF application, you should follow these steps:
First: unload the project by right-click on it in Solution Explorer
and click on Unload Project
Modify the .csproj file by change the <ApplicationDefinition Include="App.xaml"> to this one: <Page Include="App.xaml">
Now you can create your own Main method in your project:
Sample Main method and App class:
public partial class App : Application {
[STAThread]
public static int Main() {
App app = new App();
app.InitializeComponent();
var i = app.Run();
return i;
}
public App() : base() { }
protected override void OnExit(ExitEventArgs e) {
e.ApplicationExitCode = 110;
base.OnExit(e);
}
}
override the OnExit method, and in the ExitEventArgs you can set that value.
protected override void OnExit(ExitEventArgs e)
{
e.ApplicationExitCode = your_value;
}
It works for me with either method (Environment.ExitCode=110 or Environment.Exit(110)). I hope you are calling the program from the console and not from Visual Studio to then check the ExitCode...
Do it like this:
Environment.Exit(110);
This will terminate the current application with exit code 110.
You can do this in the following ways...
Application.Current.Shutdown(110);
Environment.Exit(10);
this.Close();
Shoutdown() returns a code. and Exit() also returns an exit code, but Close() only closes the application.

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...

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

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.

Categories

Resources