I have the following code in a WPF application that shows a splash screen while a long running process happens. On all of our developer machines and testing machines this works perfectly. However on some customer machines this code is leaving the main process running.
I've tried various methods of calling a shutdown including Environment.Exit(0); and we are still seeing this process left running after it has completed.
Is there something that I have missed about how my task and the application are interacting?
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO.Pipes;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace GKUpdate
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
MainWindow oWindow;
string sPipeName;
string sGKPath;
//Call base startup
base.OnStartup(e);
//Find the GK path
sPipeName = FindArgument(e.Args, "n");
sGKPath = FindArgument(e.Args, "p");
//Check if we have a path
if (!string.IsNullOrEmpty(sGKPath))
{
//Start listening
Task.Factory.StartNew(() => ListenForSuccess(sPipeName, sGKPath));
//Show the splash window
oWindow = new MainWindow();
oWindow.Show();
}
else
{
//Exit
this.Shutdown();
}
}
private string FindArgument(string[] oArgs, string sArgumentName)
{
string sFilter;
string sArgument;
//Get the argument
sFilter = string.Format("/{0}=", sArgumentName).ToLower();
sArgument = oArgs.FirstOrDefault(x => x.ToLower().StartsWith(sFilter));
//Check if we found the argument
if (!string.IsNullOrEmpty(sArgument) && sArgument.Length > sFilter.Length)
{
//Set the argument
sArgument = sArgument.Substring(sFilter.Length).Trim('"');
}
else
{
//Set null
sArgument = null;
}
//Return the argument
return sArgument;
}
private void ListenForSuccess(string sPipeName, string sGKPath)
{
int iStatus;
try
{
//Set default status
iStatus = -1;
//Loop until the service is online
do
{
//Create the named pipe
using (NamedPipeClientStream oNamedPipe = new NamedPipeClientStream(".", sPipeName, PipeDirection.InOut))
{
//Connect the pipe allowing 5 mins
oNamedPipe.Connect(300000);
//Send the byte asking for a status report
oNamedPipe.WriteByte(0);
oNamedPipe.WaitForPipeDrain();
//Read the return
iStatus = oNamedPipe.ReadByte();
//Disconnect
oNamedPipe.Close();
}
} while (iStatus != 1);
//Check if we can do the success actions
if (iStatus == 1)
{
//Start GateKeeper using the remaining command arguments
Process.Start(sGKPath, string.Join(" ", Environment.GetCommandLineArgs().Skip(3)));
}
}
catch (Exception)
{
//Do nothing
}
finally
{
//Exit the application
Application.Current.Dispatcher.InvokeShutdown();
}
}
}
}
Check the Threads window is visual studio. One of your non-background threads is not running to completion when your application closes. I expect that you are still 'listening' at this point.
How you handle this is up to you but i recommend implementing task cancellation.
There can be multiple reason. first you have to check window Event Viewer, you will able to find actual reason.
also you should handle DispatcherUnhandledException="Application_DispatcherUnhandledException". this will show actual error.
in App.XAML :
DispatcherUnhandledException="Application_DispatcherUnhandledException"
and in App.cs:
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true;
}
Your background thread is blocked waiting for the pipe to connect. You need to close the pipe from the foreground thread with an oNamedPipe.Close(). As Erno de Weerd says, you also need to make sure you can exit your do/while loop once the pipe aborts.
A nicer way would be to pass in a CancellationToken to the task, and use that to close the pipe when the foreground thread requests cancellation. You can then also check the cancellation state in your loop.
See How to force Task.Factory.StartNew to a background thread? to mark a Task.Factory.StartNew as a background thread so the thread is stopped as soon as all 'foreground' threads have stopped execution:
Task.Factory.StartNew(action,
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default).ContinueWith(completeAction);
Related
The WebBrowser.Print method has the limitation of not allowing the caller to specify a printer other than the system's default one. As a workaround, it has been suggested[1], [2] to alter the system's default printer prior to calling Print(), however it's also reported[3] (and I experienced firsthand) that the WebBrowser instance will continue to print to the previously defined printer even after the system default is altered.
To work around that, registering a handler to the PrintTemplateTeardown event by accessing the underlying ActiveX object of the managed WebBrowser instance and waiting for the event to fire before printing further documents has been proposed[4], [5], and that is what I am trying to implement. I simplified what is a much more complex program to the MVCE presented below.
(The program is a .NET Core 3.1 Windows Forms application, with one form containing nothing more than a BackgroundWorker object named bw.)
Form1.cs
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Printing;
using System.Management;
namespace Demo_1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
BwPolling();
Thread.Sleep(20000);
}
}
private void BwPolling()
{
string[] htmlStrings = { "test1", "test2" };
foreach (string html in htmlStrings)
{
Invoke((MethodInvoker)delegate
{
PrinterSettings.StringCollection installedPrinters = PrinterSettings.InstalledPrinters;
foreach (string printer in installedPrinters)
{
string[] validPrinterNames =
{
"Microsoft Print to PDF",
"Microsoft XPS Document Writer"
};
if ( validPrinterNames.Contains(printer) )
{
SetDefaultPrinter(printer);
var wb = new WebBrowser();
wb.DocumentText = html;
// With inspiration from code by Andrew Nosenko <https://stackoverflow.com/users/1768303/noseratio>
// From: https://stackoverflow.com/a/19737374/3258851
// CC BY-SA 3.0
var wbax = (SHDocVw.WebBrowser)wb.ActiveXInstance;
TaskCompletionSource<bool> printedTcs = null;
SHDocVw.DWebBrowserEvents2_PrintTemplateTeardownEventHandler printTemplateTeardownHandler =
(p)
=> printedTcs.TrySetResult(true); // turn event into awaitable task
printedTcs = new TaskCompletionSource<bool>();
wbax.PrintTemplateTeardown += printTemplateTeardownHandler;
try
{
MessageBox.Show("Printing to " + printer);
wb.Print();
printedTcs.Task.Wait();
}
finally
{
wbax.PrintTemplateTeardown -= printTemplateTeardownHandler;
}
wb.Dispose();
}
}
});
}
}
private static bool SetDefaultPrinter(string name)
{
// With credits to Austin Salonen <https://stackoverflow.com/users/4068/austin-salonen>
// From: https://stackoverflow.com/a/714543/3258851
// CC BY-SA 3.0
using ( ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer") )
{
using ( ManagementObjectCollection objectCollection = objectSearcher.Get() )
{
foreach (ManagementObject mo in objectCollection)
{
if ( string.Compare(mo["Name"].ToString(), name, true) == 0 )
{
mo.InvokeMethod("SetDefaultPrinter", null);
return true;
}
}
}
}
return false;
}
}
}
The problem being faced is when I remove the message box from the BwPolling() method, right before calling Print(), i.e. when this line is removed:
MessageBox.Show("Printing to " + printer);
then the program freezes, nothing is printed, and the process must eventually be terminated.
I believe I can sort of understand the issue on its surface: WebBrowser requires an STA thread with an active message loop[6], [7]; by calling printedTcs.Task.Wait(); within a Invoke((MethodInvoker)delegate block (called on the Form1 instance; this. is ommited), I am blocking the STA thread and the application hangs waiting for an event that is never fired. This is in fact mentioned in a comment under the answer I credited in my code.
Just can't figure out what a proper solution would be. Got lost in attempts to run the printing routine in a secondary thread. Maybe something wrong in my execution, guess I require assistance in this. Any help?
Thanks.
I have a long-running WCF service, hosted in IIS, that handles printing without any user interaction. After about 2 hours of looping through 1000 print jobs, the WCF service just seems to die. The log file I track indicates that the last print job was sent to the printer, but it never returns success nor failure (again, according to the log file).
From the log file, it would normally say:
2015-12-17 19:00:23,673 [27] INFO Barn.WCF.SysPrsPrintServer - Sending Print Job... PrintDocumentId=168;PrintSectionId=742;CustomerId=112702;DeliveryAddressId=474984;DocumentName=/SystemProcesses/Reports/CertificateOfRegistry;PrinterLocation=HB-MFP1;PrinterPaperSource=Cassette 3
2015-12-17 19:00:32,626 [27] INFO Barn.WCF.SysPrsPrintServer - PdfPrintHandler.Print: Printer HB-MFP1 indicates the print job is complete.
But the last log file entry is:
2015-12-17 19:00:32,688 [27] INFO Barn.WCF.SysPrsPrintServer - Sending Print Job... PrintDocumentId=169;PrintSectionId=742;CustomerId=112702;DeliveryAddressId=474984;DocumentName=/SystemProcesses/Reports/CertificateOfRegistry;PrinterLocation=HB-MFP1;PrinterPaperSource=Cassette 3
Can you see how this might be possible that I get no message back at all? Is it possible the WCF service just died or IIS recycled the app pool or something like that? So without further ado, here is my class:
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Threading;
using log4net;
using Aspose.Pdf.Facades;
namespace Barn.API.Print.PrintHandlers
{
public class PdfPrintHandler
{
private const int LargePdfByteCount = 3000000;
private readonly ILog _log;
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public PdfPrintHandler(ILog log)
{
_log = log;
var license = new Aspose.Pdf.License();
var pathLicense = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "bin\\Aspose.Pdf.lic";
license.SetLicense(pathLicense);
}
/// <summary>
/// Prints the specified stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="printerSettings">The printer settings.</param>
/// <param name="pageSettings">The page settings.</param>
/// <param name="timeout">The timeout.</param>
/// <param name="errors"></param>
/// <returns>The status of the print job.</returns>
public PrintJobStatusEnum Print(Stream stream, PrinterSettings printerSettings, PageSettings pageSettings, PrintDocumentModel printJob, int timeout, out List<string> errors)
{
errors = new List<string>();
// Reset the wait handler to make sure the event is not signaled
_resetEvent.Reset();
// Set attributes for printing
var viewer = new PdfViewer
{
AutoResize = true,
AutoRotate = true,
PrintPageDialog = false,
PrintAsImage = false
};
// Add an event listener when print job sent to printer
viewer.EndPrint += ViewerOnEndPrint;
//Print document using printer and page settings
try
{
_log.InfoFormat("Sending Print Job... PrintDocumentId={0};PrintSectionId={1};CustomerId={2};DeliveryAddressId={3};DocumentName={4};PrinterLocation={5};PrinterPaperSource={6}",
printJob.PrintDocumentId,
printJob.PrintSectionId != null ? printJob.PrintSectionId.ToString() : "null",
printJob.CustomerId != null ? printJob.CustomerId.ToString() : "null",
printJob.DeliveryAddressId != null ? printJob.DeliveryAddressId.ToString() : "null",
printJob.DocumentName != null ? printJob.DocumentName : "null",
printJob.PrinterLocation != null ? printJob.PrinterLocation : "null",
pageSettings.PaperSource.SourceName != null ? pageSettings.PaperSource.SourceName : "null");
// Print
if (stream.Length <= LargePdfByteCount)
{
// Bind the stream then print
viewer.BindPdf(stream);
viewer.PrintDocumentWithSettings(pageSettings, printerSettings);
}
else
{
// Use a more efficient printing method with larger documents
viewer.PrintLargePdf(stream, pageSettings, printerSettings);
}
// Block until the event finishes or timeout reached
_resetEvent.WaitOne(TimeSpan.FromMinutes(timeout));
// Check the print status
if (viewer.PrintStatus != null)
{
// An exception was thrown
var ex = viewer.PrintStatus as Exception;
if (ex != null)
{
// Get exception message
_log.Error("PdfPrintHandler.Print: Print Error: " + ex.Message + ex.StackTrace, ex);
errors.Add(ex.Message);
}
return PrintJobStatusEnum.Error;
}
else
{
// No errors were found. Printing job has completed successfully
_log.InfoFormat("PdfPrintHandler.Print: Printer {0} indicates the print job is complete.", printerSettings.PrinterName);
return PrintJobStatusEnum.Printed;
}
}
catch (Exception e)
{
_log.Error("PdfPrintHandler.Print Exception: " + e.Message + e.StackTrace, e);
errors.Add(e.Message);
}
finally
{
viewer.Close();
}
return PrintJobStatusEnum.Error;
}
private void ViewerOnEndPrint(object sender, PrintEventArgs printEventArgs)
{
// Signal the event is finished
_resetEvent.Set();
}
}
}
WCF Service Interface:
using System.ServiceModel;
namespace Barn.WCF
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ISysPrsPrintServer" in both code and config file together.
[ServiceContract]
public interface ISysPrsPrintServer
{
[OperationContract]
string DoWork(string data);
}
}
I would suggest checking the operation contracts of your print methods. I have seen WCF services fail out when the isOneWay attribute is not set or is set to false on a void method. try something like this
[OperationContract(IsOneWay=true)]
public void PrintDocumentWithSettings(PageSettings settings, PrinterSettings settings);
Here it is in the Windows event log:
A worker process with process id of '3236' serving application pool
'testws.mydomain.ca' was shutdown due to inactivity. Application Pool
timeout configuration was set to 20 minutes. A new worker process
will be started when needed.
So IIS seems to have thought that there was 20 minutes of inactivity because the WCF service is called asynchronously. The process is working away, but IIS decided to recycle the application pool in the middle of the process running.
I changed the settings in IIS on the application pool according to this:
https://serverfault.com/questions/333907/what-should-i-do-to-make-sure-that-iis-does-not-recycle-my-application
I'm having this issue. I need to be able to append the incoming messages to txtConsole from within OnMessage, but I'm getting an Illegal Cross Thread error. How do I get around this? I'm pretty basic when it comes to C# so some code (no psuedocode) with explanations would be helpful please.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Apache.NMS;
using Apache.NMS.Util;
namespace WindowsFormsApplication1
{
public partial class frmConsole : Form
{
public frmConsole()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{ }
public void cmdConnect_Click(object sender, EventArgs e)
{
// Output to the user that the connection is being set up
txtConsole.AppendText(Environment.NewLine + "Setting up connection...");
// Define the feed URL
IConnectionFactory factory = new NMSConnectionFactory(new Uri("stomp:tcp://datafeeds.networkrail.co.uk:61618"));
// Define the credentials
IConnection connection = factory.CreateConnection("REDACTED", "REDACTED");
// Create the session
ISession session = connection.CreateSession();
// Specify which feed - we want TRAIN_MVT_ALL_TOC to listen for all train movements
IDestination destination = session.GetDestination("topic://" + "TRAIN_MVT_ALL_TOC");
// Let the end user know where we will be subscribing to
txtConsole.AppendText(Environment.NewLine + "Will attempt subscription to " + destination);
// Create a consumer for the feed
IMessageConsumer consumer = session.CreateConsumer(destination);
// Let the end user know we are about to connect...
txtConsole.AppendText(Environment.NewLine + "Connecting...");
// Connection details are now all set up. Start the connection...
connection.Start();
// Check we are connected
if (connection.IsStarted == false)
{
txtConsole.AppendText(Environment.NewLine + "Connection closed.");
connection.Close();
}
// Now we need to handle messages using a MessageListener where we pass it to the OnMessage void.
consumer.Listener += new MessageListener(OnMessage);
txtConsole.AppendText(Environment.NewLine + "Connection established. Waiting for messages...");
// End of void
}
public void OnMessage(IMessage message)
{
ITextMessage msg = (ITextMessage)message;
message.Acknowledge();
txtConsole.AppendText(Environment.NewLine + msg.Text);
}
}
}
The reason you're getting that error is because you're trying to update a UI element from a non-UI thread. You can call the control's Invoke method to force it to run on the UI thread.
public void OnMessage(IMessage message)
{
ITextMessage msg = (ITextMessage)message;
message.Acknowledge();
if (txtConsole.InvokeRequired)
{
txtConsole.Invoke(new Action(() =>
{
txtConsole.AppendText(Environment.NewLine + msg.Text);
}));
}
else
{
txtConsole.AppendText(Environment.NewLine + msg.Text);
}
}
That exception appears when you try to access other thread (for example, UI Thread), from another different thread.
You can solve that calling
Dispatcher.BeginInvoke(() => delegate{// Here the code});
from any thread you want
How to Properly Handle Cross-thread Events and Update a Label with BeginInvoke and BackgroundWorker
Cross Thread UI control access
Just replace txtConsole.AppendText in method OnMessage with
txtConsole.Invoke((Action)(() => txtConsole.AppendText(Environment.NewLine + msg.Text)));
Is there any problem which i have to do carefully when starting new process in multiple thread application?
I tried this in a simple project:
static void Main(string[] args)
{
Process.Start(#"D:\System\Desktop\a.txt");
MessageBox.Show("Success");
}
And it runs perfectly. But when i do it in my big project which use multiple thread, it's thread is stopped working ("a.txt" is opened but "Success" is not shown) while my application (other thread) do well.
What is the problem in this situation?
If you have a Windows.Forms application and you try to show a message-box from a thread that is not the main user-interface thread, the behavior of the message-box is undefined. Meaning, it may or may not show, be inconsistent, or some other problem.
For instance, displaying a message-box from the BackgroundWorker's DoWork event may or may not work. In one case, the message-box-result was always cancel regardless of what button was clicked.
Therefore, if you are using a message-box just for debugging purposes, use a different technique. If you have to show a message-box, call it from the main user-interface thread.
A console-application should normally not have problems displaying message-boxes. Yet, I have had cases where I would have to sleep the thread for 100ms before the message-box call.
Note, as TomTom pointed out, the main user-interface thread is the application's Windows message loop. Which reminds me, I once had to create a Form in a Console application in order to create a Windows message loop, so my application could respond to Windows messages.
This isn't the answer - I can't put all this code in a comment...
This works for me. Tell me how your code differs from this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace Test
{
class Program
{
const string OutputFile = #"E:\Output.txt";
object _lock = new object();
static void Main(string[] args)
{
Program program = new Program();
Thread thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\Test.txt");
thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\DoesntExist.txt");
Console.ReadKey();
}
void ThreadMethod(object filename)
{
String result = RunNormal(filename as string);
lock (_lock)
{
FileInfo fi = new FileInfo(OutputFile);
if (!fi.Exists)
{
try
{
fi.Create().Close();
}
catch (System.Security.SecurityException secEx)
{
Console.WriteLine("An exception has occured: {0}", secEx.Message);
return;
}
}
StreamWriter sw = fi.AppendText();
sw.WriteLine(result);
sw.Close();
}
}
string RunNormal(string fullfilename)
{
try
{
Process.Start(fullfilename);
return fullfilename + "|Success";
}
catch (Exception e)
{
return fullfilename + "|" + e.ToString();
}
}
}
}
The output in Output.txt is:
E:\DoesntExist.txt|System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(String fileName)
at Test.Program.RunNormal(String fullfilename) in E:\Projekti\VS2010\Test\Test\Program.cs:line 59
E:\Test.txt|Success
How much different is your code? Do you call some other methods? How do you process the results?
Make sure Process.Start works. Passing a filename is not good enough in some cases. In you sample code, you would have to set the use-shell property; otherwise, you would have to use cmd start <filename> or equivalent.
Therefore, just start NotePad.exe to make sure Process.Start works. If it does then your problem is the process command and command line.
I use a Console Application in Windows Mobile to handle incoming message interception. In the same console application i accept parameters (string args[]) which based on the parameters, register the message interceptor.
InterceptorType is a enum
static void Main(string[] args)
{
if (args[0] == "Location")
{
addInterception(InterceptorType.Location, args[1],args[2]);
}
}
private static void addInterception(InterceptorType type, string Location, string Number )
{
if (type == InterceptorType.Location)
{
using (MessageInterceptor interceptor = new MessageInterceptor(InterceptionAction.NotifyAndDelete, false))
{
interceptor.MessageCondition = new MessageCondition(MessageProperty.Sender, MessagePropertyComparisonType.Contains, Number, false);
string myAppPath = Assembly.GetExecutingAssembly().GetName().CodeBase;
interceptor.EnableApplicationLauncher("Location", myAppPath);
interceptor.MessageReceived += new MessageInterceptorEventHandler(interceptor_MessageReceived);
}
}
}
static void interceptor_MessageReceived(object sender, MessageInterceptorEventArgs e)
{
//Do something
}
I made this a console application because i want it keep running in the background and intercept incoming messages.
This works fine for the first time. But the problem is that I have to keep calling the addInterception method to add subsequent interception rules. This makes the console application start again and again for each time i add a rule. How do i make this run only once and add more message interceptor rules?
Since you already have a method in place to call the command prompt once, update your logic with some simple looping so you can pass N commands.
EDIT: I wrote it a fully compileable example to show you exactly what I am talking about. Note how the child process can be called any number of times without re-launching. This is not just a simple command line launch with arguments being passed because that idea will lead to X processes which is exactly what you do not want.
PARENT PROCESS: (The one with System.Diagnostics.Process)
/// <summary>
/// This is the calling application. The one where u currently have System.Diagnostics.Process
/// </summary>
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process p = new Process();
p.StartInfo.CreateNoWindow = false;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = #"C:\AppfolderThing\ConsoleApplication1.exe";
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.BeginErrorReadLine();
p.BeginOutputReadLine();
StreamWriter inputStream = p.StandardInput;
inputStream.WriteLine(1);
inputStream.WriteLine(2);
inputStream.WriteLine(-1);//tell it to exit
p.WaitForExit();
}
}
CHILD PROCESS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
enum InterceptorType
{
foo,
bar,
zee,
brah
}
/// <summary>
/// This is the child process called by System.Diagnostics.Process
/// </summary>
class Program
{
public static void Main()
{
while (true)
{
int command = int.Parse(Console.ReadLine());
if (command == -1)
Environment.Exit(0);
else
addInterception((InterceptorType)command, "some location", "0");
}
}
private static void addInterception(InterceptorType type, string Location, string Number)
{
switch (type)
{
case InterceptorType.foo: Console.WriteLine("bind foo"); break;
case InterceptorType.bar: Console.WriteLine("bind bar"); break;
default: Console.WriteLine("default bind zee"); break;
}
}
static void interceptor_MessageReceived(object sender, EventArgs e)
{
//Do something
}
}
}
Note that codeplex has a managed service library.
EDIT
It seems that people are misunterstanding your question (or I am) so here's some clarification on how I'm seeing the problem.
You have an console app that takes in command-line parameters. These parameters are used for something (the what is irrelevant actually). You want to be able to add parameters after the app is already running by calling the app with new command line args.
What is happening is that when you call the app any time after teh first, a new instance of the process starts up instead of the command-line arguments going to the existing, already running application.
END EDIT
The solution is fairly straightforward and requires two pieces.
You need a named mutex. For whatever (poor) reason, the CF doesn't expose a version of a mutex that takes a name, so you have to P/Invoke CreateMutex or use a library (like the SDF) that already has it. Your app needs to create the mutex at startup and check to see if it already exists. if it doesn't you're the first running instance and run as normal. If the mutex exists, you need to pass your command line args to the one that is already running via a P2P queue then simply exits.
After checking the mutex, the first instance spawns a worker thread. This thread listens on a P2P queue for messages. When they come in, you handle them.