windows service example code
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.IO;
namespace file_delete
{
public partial class file_delete : ServiceBase
{
public file_delete()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
private void deleteFile(string folder)
{
System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(folder);
System.IO.FileInfo[] fileNames = dirInfo.GetFiles("*.*");
foreach (System.IO.FileInfo fi in fileNames)
{
fi.Delete();
}
How can I call the deleteFile(string folder) from windows forms?
You can use the OnCustomCommand override, but this only takes an integer as an argument and does not support passing strings to the service.
The other options would be to create a WCF service or use Remoting to pass the information you need to the service and call the delete method.
EDIT: to respond to a question in the comments about how to use OnCustomCommand in a very strange way is as follows.
In the service you would need something like this.
private const int CMD_INIT_DELETE = 1;
private const int CMD_RUN_DELETE = 0;
private bool m_CommandInit = false;
private StringBuilder m_CommandArg = new StringBuilder();
protected override void OnCustomCommand(int command)
{
if (command == CMD_INIT_DELETE)
{
this.m_CommandArg.Clear();
this.m_CommandInit = true;
}
else if (this.m_CommandInit)
{
if (command == CMD_RUN_DELETE)
{
this.m_CommandInit = false;
this.deleteFile(this.m_CommandArg.ToString());
}
else
{
this.m_CommandArg.Append((char)command);
}
}
}
In the windows form application you would have something like this
private const int CMD_INIT_DELETE = 1;
private const int CMD_RUN_DELETE = 0;
private void RunServiceDeleteMethod(string delFolder)
{
serviceController1.ExecuteCommand(CMD_INIT_DELETE);
foreach (char ch in delFolder)
serviceController1.ExecuteCommand((int)ch);
serviceController1.ExecuteCommand(CMD_RUN_DELETE);
}
THIS IS NOT TESTED and only a proof of concept. Again, I DO NOT recommend doing it this way and the above example is only to show how NOT to do this type of communications between desktop applications and services.
Related
I have multiple instances of the same WCF service hosted. If there is a change in one of the service instance then it should be notified/updated in other services too. I implemented the solution such that the WCF service acts as both client and server with the DuplexChannelFactory. But in order to do that the service needs to register its peers and I am doing that in the constructor of the service. This is leading to deadlock, since it is never going to get out initializing each other. How is this sort of logic can be implemented in WCF service?
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Timers;
using WcfServiceLibrary.PeerServiceReference;
namespace WcfServiceLibrary
{
public interface IHeartbeat
{
bool Pulse();
void Ping();
}
[ServiceContract(CallbackContract = typeof(INotifications))]
public interface ISubscription : IHeartbeat, INotifications
{
[OperationContract(IsOneWay = true)]
void Subscribe(string ServiceName);
[OperationContract(IsOneWay = true)]
void Unsubscribe(string ServiceName);
}
[ServiceContract]
public interface INotifications
{
[OperationContract(IsOneWay = true)]
void UpdateData(int newValue);
}
[ServiceContract]
public interface IUserService
{
[OperationContract(IsOneWay = true)]
void MethodThatWillChangeData(int value);
}
public class ObservableService : IUserService, ISubscription
{
private static Dictionary<string, ISubscription> _Peers = new Dictionary<string, ISubscription>();
private int ClientAge;
private string ServiceName { get; }
private Timer _timer = new Timer() { Interval = 5000 };
private IWriter _writer;
public ObservableService() : this(new ConsoleWriter())
{
}
public ObservableService(IWriter writer)
{
_writer = writer;
_writer.WriteLine("Initiating construction...");
ServiceName = ConfigurationManager.AppSettings["ServiceName"];
_timer.Elapsed += Timer_Elapsed;
_timer.Start();
var PeerServersList = ConfigurationManager.AppSettings["PeerServers"].Split(';');
var callback = new InstanceContext(this);
foreach (var peer in PeerServersList)
{
try
{
var dualBinding = new WSDualHttpBinding();
var address = new EndpointAddress(peer);
var PeerServiceFactory = new DuplexChannelFactory<ISubscription>(callback, dualBinding);
var PeerService = PeerServiceFactory.CreateChannel(address);
PeerService.Subscribe(ServiceName);
}
catch (Exception ex)
{
//TODO handle the exception
}
}
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
_writer.WriteLine("Pinging the data");
this.Ping();
}
public void MethodThatWillChangeData(int newValue)
{
_writer.WriteLine($"MethodThatWillChangeData with {newValue}");
var PreviousClientAge = ClientAge;
ClientAge = newValue;
foreach (var peer in _Peers)
{
_writer.WriteLine($"Calling update on the client {peer.Key}");
peer.Value.UpdateData(newValue);
}
}
public void Ping()
{
var test = _Peers.Keys.ToList();
for (int i = _Peers.Count - 1; i >= 0; i--)
{
try
{
_writer.WriteLine($"Checking the pulse of {test[i]}");
_Peers[test[i]].Pulse();
}
catch (Exception)
{
_Peers.Remove(test[i]);
}
}
}
public bool Pulse()
{
_writer.WriteLine($"Pulse requested...");
return true;
}
public void UpdateData(int newValue)
{
_writer.WriteLine($"Updating the data to {newValue} from {ClientAge}");
ClientAge = newValue;
}
public void Unsubscribe(string ServiceName)
{
if (_Peers.Keys.Contains(ServiceName))
{
_Peers.Remove(ServiceName);
}
}
public void Subscribe(string ServiceName)
{
if (!_Peers.Keys.Contains(ServiceName))
{
_writer.WriteLine($"Registering {ServiceName}...");
_Peers.Add(ServiceName, OperationContext.Current.GetCallbackChannel<ISubscription>());
}
}
}
}
I would have a single separate WCF service that manages whatever data you're trying to keep synchronised across your services and have each service communicate with that.
Alternatively, you keep your data in a database so any change is automatically reflected across the services.
First the code:
public partial class Watcher : ServiceBase
{
private const string PathToFolder = #"D:\print\";
public Watcher()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
private void fileWatcher_Changed(object sender, FileSystemEventArgs e)
{
if (e.Name != "test.txt")
{
return;
}
using (var r = new StreamReader(e.FullPath))
{
var json = r.ReadToEnd();
dynamic tempTest = JsonConvert.DeserializeObject(json);
const string filename = PathToFolder + "textParsed.txt";
if (File.Exists(filename))
{
File.Delete(filename);
}
using (var file = File.CreateText(filename))
{
file.WriteLine(tempTest.Name.ToString());
}
}
}
}
If there are changes to the text.txt file I'm suppose to parse the content of that text file and create another file. If I attach VS to the service and debug the service, the event gets fired, but when running normally, nothing happens.
The installer has LocalSystem privileges and that's pretty much all the changes I've made... Should be pretty straight forward, but somehow isn't.
I am coding a rcon message tool for a game server in C#. However I've run across a error:
"The name 'm' does not exist in the current context"
By now you're shouting at your screen NOOB! and yes I admit I am; I have little real coding experience.
I've played with MFC C++ and OpenGL and I'm a fairly respected cod modder "script is gsc loosely based on c++" so I hope I can learn quickly, basically I tried to access an instance of b. outside of the main loop but it gave me the error:
The name b does not exist in the current context
so I made a new messages function that started a new connection in a new instance. Then I tried the access that in another function stopmessages() but I still get the error.
Sorry for the newb question. I've googled long and hard about this and I just don't understand.
Here's my code - it uses Nini.dll for config file access and BattleNET.dll for access to rcon for the game -
#region
using System;
using System.Net;
using System.Text;
using BattleNET;
using Nini.Config;
#endregion
namespace BattleNET_client
{
internal class Program
{
private static void Main(string[] args)
{
bool isit_ok = true;
Console.OutputEncoding = Encoding.UTF8;
Console.Title = "rotceh_dnih's DayZ servermessages";
BattlEyeLoginCredentials loginCredentials = GetLoginCredentials();
Console.Title += string.Format(" - {0}:{1}", loginCredentials.Host, loginCredentials.Port);
IBattleNET b = new BattlEyeClient(loginCredentials);
b.MessageReceivedEvent += DumpMessage;
b.DisconnectEvent += Disconnected;
b.ReconnectOnPacketLoss(true);
b.Connect();
while (true)
{
startmessages();
string cmd = Console.ReadLine();
if (cmd == "exit" || cmd == "logout" || cmd == "quit")
{
Environment.Exit(0);
}
if (cmd == "restart")
{
stopmessages();
}
if (cmd == "startstuff")
{
startmessages();
}
if (b.IsConnected())
{
if (isit_ok)
{
}
isit_ok = false;
b.SendCommandPacket(cmd);
}
else
{
Console.WriteLine("Not connected!");
}
}
}
private static BattlEyeLoginCredentials GetLoginCredentials()
{
IConfigSource source = new IniConfigSource("server/admindets.ini");
string serverip = source.Configs["rconlogin"].Get("ip");
int serverport = source.Configs["rconlogin"].GetInt("port");
string password = source.Configs["rconlogin"].Get("rconpwd");
var loginCredentials = new BattlEyeLoginCredentials
{
Host = serverip,
Port = serverport,
Password = password,
};
return loginCredentials;
}
public static void startmessages()
{
BattlEyeLoginCredentials loginCredentials = GetLoginCredentials();
IBattleNET m = new BattlEyeClient(loginCredentials);
m.MessageReceivedEvent += DumpMessage;
m.DisconnectEvent += Disconnected;
m.ReconnectOnPacketLoss(true);
m.Connect();
IConfigSource messagesource = new IniConfigSource("messages/servermessages.ini");
int messagewait = messagesource.Configs["timesettings"].GetInt("delay");
string[] messages = messagesource.Configs["rconmessages"].Get("messages1").Split('|');
// for (;;)
// {
foreach (string message in messages)
{
Console.WriteLine(message);
m.SendCommandPacket(EBattlEyeCommand.Say,message);
System.Threading.Thread.Sleep(messagewait * 60 * 1000);
}
// }
}
public static void stopmessages()
{
m.Disconnect();
}
private static void Disconnected(BattlEyeDisconnectEventArgs args)
{
Console.WriteLine(args.Message);
}
private static void DumpMessage(BattlEyeMessageEventArgs args)
{
Console.WriteLine(args.Message);
}
}
}
You need to put the declaration of m into the class scope:
internal class Program
{
// declare m as field at class level
private static IBattleNET m;
private static void Main(string[] args)
{
....
}
public static void startmessages()
{
BattlEyeLoginCredentials loginCredentials = GetLoginCredentials();
// JUST SET THE VALUE HERE
m = new BattlEyeClient(loginCredentials);
m.MessageReceivedEvent += DumpMessage;
m.DisconnectEvent += Disconnected;
m.ReconnectOnPacketLoss(true);
m.Connect();
IConfigSource messagesource = new IniConfigSource("messages/servermessages.ini");
int messagewait = messagesource.Configs["timesettings"].GetInt("delay");
string[] messages = messagesource.Configs["rconmessages"].Get("messages1").Split('|');
// for (;;)
// {
foreach (string message in messages)
{
Console.WriteLine(message);
m.SendCommandPacket(EBattlEyeCommand.Say,message);
System.Threading.Thread.Sleep(messagewait * 60 * 1000);
}
// }
}
The stopmessages() method won't be able to access m as the variable m only exists within the startmessages() method
Move the declaration of IBattleNET m
To outside the main function and make it static:
static IBattleNet b;
Then in your main you just do m = new BattlEyeClient(loginCredentials);
m is declared in scope of static method startmessages but then you are trying to use it in stopmessages, where it is not in scope. You should move the variable to class scope, and define it as static (since your methods are static).
Hopefully your client app is single-threaded, otherwise you will need to consider thread safety issues as well.
what you could do is after you declared your class, so bevore the static void main
declare your m value
internal class Program
{
IBattleNET m;
then in the startMessages method add
m = new BattlEyeClient(loginCredentials);
this will make the m value available to all the methods inside your class
I'm assuming m should refer to this:
IBattleNET m = new BattlEyeClient(loginCredentials);
in the method startmessages(). What you need to do is declare IBattleNET m outside the method body:
static IBattleNET m;
public static void startmessages()
{
//etc
I have an external dll written in C# and I studied from the assemblies documentation that it writes its debug messages to the Console using Console.WriteLine.
this DLL writes to console during my interaction with the UI of the Application, so i don't make DLL calls directly, but i would capture all console output , so i think i got to intialize in form load , then get that captured text later.
I would like to redirect all the output to a string variable.
I tried Console.SetOut, but its use to redirect to string is not easy.
As it seems like you want to catch the Console output in realtime, I figured out that you might create your own TextWriter implementation that fires an event whenever a Write or WriteLine happens on the Console.
The writer looks like this:
public class ConsoleWriterEventArgs : EventArgs
{
public string Value { get; private set; }
public ConsoleWriterEventArgs(string value)
{
Value = value;
}
}
public class ConsoleWriter : TextWriter
{
public override Encoding Encoding { get { return Encoding.UTF8; } }
public override void Write(string value)
{
if (WriteEvent != null) WriteEvent(this, new ConsoleWriterEventArgs(value));
base.Write(value);
}
public override void WriteLine(string value)
{
if (WriteLineEvent != null) WriteLineEvent(this, new ConsoleWriterEventArgs(value));
base.WriteLine(value);
}
public event EventHandler<ConsoleWriterEventArgs> WriteEvent;
public event EventHandler<ConsoleWriterEventArgs> WriteLineEvent;
}
If it's a WinForm app, you can setup the writer and consume its events in the Program.cs like this:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (var consoleWriter = new ConsoleWriter())
{
consoleWriter.WriteEvent += consoleWriter_WriteEvent;
consoleWriter.WriteLineEvent += consoleWriter_WriteLineEvent;
Console.SetOut(consoleWriter);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
static void consoleWriter_WriteLineEvent(object sender, Program.ConsoleWriterEventArgs e)
{
MessageBox.Show(e.Value, "WriteLine");
}
static void consoleWriter_WriteEvent(object sender, Program.ConsoleWriterEventArgs e)
{
MessageBox.Show(e.Value, "Write");
}
It basically amounts to the following:
var originalConsoleOut = Console.Out; // preserve the original stream
using(var writer = new StringWriter())
{
Console.SetOut(writer);
Console.WriteLine("some stuff"); // or make your DLL calls :)
writer.Flush(); // when you're done, make sure everything is written out
var myString = writer.GetStringBuilder().ToString();
}
Console.SetOut(originalConsoleOut); // restore Console.Out
So in your case you'd set this up before making calls to your third-party DLL.
You can also call SetOut with Console.OpenStandardOutput, this will restore the original output stream:
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
Or you can wrap it up in a helper method that takes some code as an argument run it and returns the string that was printed. Notice how we gracefully handle exceptions.
public string RunCodeReturnConsoleOut(Action code)
{
string result;
var originalConsoleOut = Console.Out;
try
{
using (var writer = new StringWriter())
{
Console.SetOut(writer);
code();
writer.Flush();
result = writer.GetStringBuilder().ToString();
}
return result;
}
finally
{
Console.SetOut(originalConsoleOut);
}
}
Using solutions proposed by #Adam Lear and #Carlo V. Dango I created a helper class:
public sealed class RedirectConsole : IDisposable
{
private readonly Action<string> logFunction;
private readonly TextWriter oldOut = Console.Out;
private readonly StringWriter sw = new StringWriter();
public RedirectConsole(Action<string> logFunction)
{
this.logFunction = logFunction;
Console.SetOut(sw);
}
public void Dispose()
{
Console.SetOut(oldOut);
sw.Flush();
logFunction(sw.ToString());
sw.Dispose();
}
}
which can be used in the following way:
public static void MyWrite(string str)
{
// print console output to Log/Socket/File
}
public static void Main()
{
using(var r = new RedirectConsole(MyWrite)) {
Console.WriteLine("Message 1");
Console.WriteLine("Message 2");
}
// After the using section is finished,
// MyWrite will be called once with a string containing all messages,
// which has been written during the using section,
// separated by new line characters
}
Any efficient/reliable way to expose one event?
I have a class, MultipleDocumentCopier that copies multiple documents thru an instance of SingleDocumentCopier.
SingleDocumentCopier exposes an event CopyCompleted that is fired when a file is copied.
Suppose that, I am copying 10 files, instead of raising SingleDocumentCopier.CopyCompleted 10 times,
I would like to expose an event, MultipleDocumentCopier.MultipleCopyCompleted.
But is there a standard way/technique to combine multiple events and fire it once?
I would like to raise MultipleDocumentCopier.MultipleCopyCompleted only once
within 'MultipleDocumentCopier.singleDocumentCopier_CopyCompleted', instead of 10 times.
Here is the sample code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace CombineEvents
{
public class Program
{
public static void Main(string[] args)
{
var copier = new MultipleDocumentCopier();
copier.MultipleCopyCompleted += MultipleDocumentCopyCompleted;
copier.CopyDocuments(new[] {"File1", "File2", "File3"});
}
private static void MultipleDocumentCopyCompleted(
object sender, FileNameEventArgs e)
{
Debug.Print("Following documents have been copied");
foreach (var fileName in e.FileNames)
{
Debug.Print("\t\t\"{0}\"", fileName);
}
}
}
internal class SingleDocumentCopier
{
public event EventHandler CopyCompleted;
protected virtual void OnCopyCompleted()
{
if (CopyCompleted != null) CopyCompleted(this, EventArgs.Empty);
}
public void Copy(string fileName)
{
Debug.Print("Copying = '{0}'", fileName);
OnCopyCompleted();
}
}
public class MultipleDocumentCopier
{
public event EventHandler<FileNameEventArgs> MultipleCopyCompleted;
protected virtual void OnCopyCompleted(FileNameEventArgs e)
{
EventHandler<FileNameEventArgs> completed = MultipleCopyCompleted;
if (completed != null) completed(this, e);
}
public void CopyDocuments(IEnumerable<string> fileNames)
{
var copier = new SingleDocumentCopier();
copier.CopyCompleted += singleDocumentCopier_CopyCompleted;
foreach (var fileName in fileNames)
{
copier.Copy(fileName);
}
}
public static void singleDocumentCopier_CopyCompleted(
object sender, EventArgs e)
{
// I want to raise "MultipleDocumentCopier.MultipleCopyCompleted" when
// all files, `fileNames` in "CopyDocuments" have been copied,
// not for every file being copied.
}
}
public class FileNameEventArgs : EventArgs
{
private readonly List<string> _FileNames;
public List<string> FileNames
{
get { return _FileNames; }
}
public FileNameEventArgs(IEnumerable<string> fileNames)
{
_FileNames = fileNames.ToList();
}
}
}
Why not call MultipleDocumentCopier.OnCopyCompleted from the end of CopyDocuments, and forget singleDocumentCopier_CopyCompleted entirely?
Or maybe this is pseudocode, and your real code is more complicated? Maybe you could keep a collection of outstanding file names inside MultipleDocumentCopier, and each time the singleDocumentCopier_CopyCompleted is raised, you remove one document from the collection. Once the collection becomes empty you call MultipleDocumentCopier.OnCopyCompleted.
Edit: Re 'is there a standard way?' -- not that I'm aware of in C#; F# has an interesting set of mechanisms for combining events like this, but I assume a change in programming language isn't an option.