I've written a C# windows service which can write messages to a custom EventLog or to any number of files. These messages are all marked with some priority (so, for example, only ERRORs and WARNINGs get stored in the EventLog, but if desired a lot more can be stored to a file).
What I'd like to do now is create a GUI that can listen for these messages and display them in real-time. Allowing a user to watch the current messages (at whatever their desired priority level), without the need to store everything to a file. I assume this is a separate program with some form of hook into the service, but I'm unsure of where to start.
This is my first real windows service, so I seem to be missing some keywords for finding out how to do this... Are there any code samples, tutorials, references, etc. for how to do something like this?
UPDATE
A lot of helpful answers, I love it when there's many ways to solve a problem! I think I'm going to implement a self-hosting WCF based solution. I'm still very light on the details as I'm trying to learn about WCF (I believe it will prove quite useful for me in other projects)... but thus far, I've found the videos here to be the most helpful as an intro how-to.
What you can do is have the windows service have way of registering for an event (you can do this through using Windows Communication Foundation). When your error comes up, it fires that event, and your winforms app will be notified. It's called a duplex contract:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/0eb69998-0388-4731-913e-fb205528d374/
http://msdn.microsoft.com/en-us/library/ms731184.aspx
Actually the really cool thing is that you can have multiple applications listening this way too. So you can display it on screen, and have another application log it etc. without the two external apps knowing anything about each other.
I know this has already been mentioned, but use Windows Communication Foundation (WCF). Specifically, use the Publish-Subscribe Framework developed by Juval Lowy, author of Programming WCF Services. The details are described in this excellent MSDN article, and the source code is available for free at Lowy's website.
The neat thing about this framework is that it decouples the publisher, e.g., your Windows service, from any subscribers, e.g., your GUI. The publisher "publishes" events that are of interest to the Pub/Sub Service, which is always available. From the publisher's point of view, it doesn't matter if there are any subscribers or not. The Pub/Sub Service takes care of routing events to any and all registered subscribers. In this way, your Windows service publishes events as they occur, your GUI will subscribe/unsubscribe to the Pub/Sub Service when it loads/exits, and the Pub/Sub Service will notify your GUI as events occur.
I have used this setup in my project, and it works extremely well.
I've actually used the BitFactory Logger that has a socket logger that you can use for this purpose.
What you're describing is inter-process communication, which can get messy.
The easiest and most elegant, but probably least reactive, is to have the service write entries as small text files (or append to a log), and have your GUI use a FileSystemWatcher to detect new files or updates to the log file, and read the file. You have to ensure that the service opens the file for appending in a "shared" manner, allowing read-only access while it's writing. Otherwise, you'll block one process or the other, probably causing lost messages.
Processes can communicate through some built-in pipelines. if your service writes messages to its StandardOutput pipe, the GUI can remotely attach a listener and receive events when messages are written. This is probably the most elegant non-file way to do what you want. Research the Process class, especially the OutputDataReceived event. You'll have to go look for the process from your GUI by some uniquely identifying information, using GetProcess().
You need to look for "synchronization" and "inter-process communication". In your case the service would use the global event or semaphore to signal presence of data, and GUI process would check event/semaphore state and read the updates from event log or from file.
There exist more complicated scenarios, but the above is a good starting point.
Observer pattern!
Perhaps a delegate for all observable models that you can hook into with your service?
.NET remoting over IPC channel.
I've found that a Named Pipe communication with a System Tray application was the simplest way to display notifications from a Windows Service. This is because in Windows 10 services run with different permissions than the logged in user, so the notification app needs to perform IPC with the service.
Here you could put this into the server:
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleServerApp
{
class Program
{
static void Main(string[] args)
{
StartServer();
Task.Delay(1000).Wait();
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("PipesOfPiece");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
writer.WriteLine(String.Join("", line.Reverse()));
writer.Flush();
}
});
}
}
}
Then put this into your client:
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleClientApp
{
class Program
{
static void Main(string[] args)
{
//Client
var client = new NamedPipeClientStream("PipesOfPiece");
client.Connect();
StreamReader reader = new StreamReader(client);
StreamWriter writer = new StreamWriter(client);
while (true)
{
string input = Console.ReadLine();
if (String.IsNullOrEmpty(input)) break;
writer.WriteLine(input);
writer.Flush();
Console.WriteLine(reader.ReadLine());
}
}
}
}
Then change your ConsoleServerApp to a Winforms application so that it can display the notification whenever the windows service sends it a message:
public Form1()
{
InitializeComponent();
StartServer();
Task.Delay(_threadJoinTimeout).Wait();
}
public void DisplayMessage()
{
this.notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
this.notifyIcon1.BalloonTipText = "Welcomd!";
this.notifyIcon1.BalloonTipTitle = "Title";
this.notifyIcon1.ShowBalloonTip(2000);
}
void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("PipesOfPiece");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
while (true)
{
var line = reader.ReadLine();
DisplayMessage();
}
});
}
Then put the ConsoleClientApp into your Windows Service.
For details on the pipe please see Example of Named Pipes
For the System Tray application please see http://www.tutorialspanel.com/create-system-tray-icon-windows-forms-application-using-c-vb-net/#:~:text=Below%20is%20an%20example%20of%20how%20to%20create,Step%203.%20Add%20an%20icon%20to%20the%20NotifyIcon
Here are tips on using the TopShelf NuGet package which allows you to debug your Windows Service as a Console Application: https://www.codeproject.com/Articles/881511/SignalR-with-Self-hosted-Windows-Service
Trying to write a WMI class function to mount a network drive on any computer (remote or local) using the credentials of the logged in computer.
This is a class for a larger project that I wrote for help desk staff to do first line fixes on remote PC's. The tech types in the the machine name or ip address and the app connects to it and allows to tech to click a couple of buttons and fix some basic items without having to remote(VNC) into the PC.
I've read all over the internet that it is much easier ways than WMI, but due to the remote nature of the app I would rather not use local API calls, nor do I want to worry about uploading script and executing it though a process start. Also other functions are already in WMI so I'd like to keep the code base the same.
The basic idea is to mount H: to //fileserver.example.com/$username
NetFixer is already in production use so I'm trying to keep my code nice and neat
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
namespace WMIcontrols
{
public class Remote
{
public string target;
//Some code skipped here for simplicity sake...
public bool MountNetDrive(string DriveLetter, string MountLocation)
{
try
{
//Mount the network drive
return true;
}
catch
{
//Mount Failed
return false;
}
}
}
}
This is not using WMI but will accomplish what you want and is very simple
System.Diagnostics.Process.Start("cmd", "/c net use x: \\fileserver.example.com /user:Username Password");
I want to Access Information of Builds of Team Projects to use them in other Applications of our Company.
What I need is requesting all Builds for a Team Projekt.
I need Information like:
- the UNC Path the Outlut was copied to
- the Name
- Date of Build
- who started ór triggered the Build (if possible)
I did not find any discription how the Access Information on TFS via Web Services. What I learned is that TFS has a Service Interface.
I hope anyone can give me a litte hint how to reach my goal. If anyone has some sample code I surely would be delighted :-)
Would something like that be helpful:
using System;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
namespace BuildDetails
{
class Program
{
static void Main()
{
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://tfsURI"));
var buildService = (IBuildServer)teamProjectCollection.GetService(typeof(IBuildServer));
IBuildDefinition buildDefinition = buildService.GetBuildDefinition("TeamProjectName", "BuildDefinitionName");
IBuildDetail[] buildDetails = buildService.QueryBuilds(buildDefinition);
foreach (var buildDetail in buildDetails)
{
IBuildInformation buildInformation = buildDetail.Information;
Console.Write(buildDetail.BuildNumber+"\t");
Console.Write(buildDefinition.Name+"\t");
Console.Write(buildDetail.Status+"\t");
Console.Write(buildDetail.StartTime+"\t");
Console.WriteLine((buildDetail.FinishTime - buildDetail.StartTime).Minutes);
}
}
}
}
This is just a standard usage of the TFS-SDK, and I think it provides with the info you 're looking for.
I suggest using wrapper for the wrapper to access to TFS since it's much easier and may be enough for what you need.
If not, I suggest using TFS SDK.
If you still want to go lower, then please read this SO.
I am working as a trainee engineer in a networking firm and am getting annoyed by having to change the IP information from time to time.
I am in need of building a software to help me change these details easily. I have managed to set the IP information. But I still have problems.
I need to run the program as Administrator [right click], is there a way to program to prompt for it at startup?
How can I change adapter to DHCP?
The code is quite long, and I hope not to fill bore you with it. But I have been using Management
Management Class
Management Base Object
Management Object Collection in my development.
I'd prefer to make my own program to develop my programming skills. But if there is an application to do it, I don't mind knowing.
I hope this answer gives you some insisght and direction to go.
Okay, the network adapter one isn't that straight forward, but I believe you can achieve it with WMI, specially this WMI object here. The MSDN documentation tells you all the properties, methods (which there are for setting DHCP etc) and the datatypes and values it takes. This may be one approach as using WMI through C# is pretty easy. I wish I could provide you an example, but I've never used that specific WMI class before. You can also access the above WMI class through the Visual Studio Server Explorer, which you can see here. ..and it has your "EnableDHCP" method you are probably looking for.
As far as asking for your program to run with administrative priviledges, here is the code from my setup project in my framework. What this does is before it runs any sort of form or logic, requests the "runas" verb which invokes UAC (if Windows has its Vista/7, and requests admin priviledges from the user)
namespace Setup {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Setup.Forms;
using System.Security.Principal;
using System.Diagnostics;
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool administrativeMode = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!administrativeMode) {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Verb = "runas";
startInfo.FileName = Application.ExecutablePath;
try {
Process.Start(startInfo);
}
catch {
return;
}
return;
}
Application.Run(new ShellForm());
}
}
}
As far as a program to do it, Windows Network Connection Manager? I know its cumbersome because of all the dialogs, but.. its already there.
I once had to write a very similar program. I used some of the source code from these two projects to help me get started: Chameleon Project and Configuring TCP/IP Settings using WMI and C#
The Chameleon Project is a C# project to help change network settings of a particular adapter. The other project is a tutorial on how to use C# to change network settings using WMI and C#. You can look at the source code and learn from it to help you make your own software that does what you need.
I'm attempting to recycle an app pool on IIS6 programmatically through a web application. I have searched all over the net and found a bunch of solutions (Most involving impersonation) but none of them seem to work. The most common error I get is E_ACCESSDENIED despite entering a valid username and password. Can anybody point me in the right direction?
The solution I use for this sort of thing (Where you're trying to run a process from ASP.NET that needs administrative privileges) is the following:
Write whatever you need done as a Self hosted WCF service. Preferably an Http REST Service, so it's easy to call (even using just a browser for testing)
Make sure you service is run using an administrator account. You can use the task scheduler to make sure the service is running at all times as well as run using an Administrator account.
Execute methods on the service from your ASP.NET application using a WCF Client
And it works all the time no matter what "process" I'm trying to run from within an ASP.NET application.
Now as far are the details (code) is concerned let me know if you need help. The code below
is the code you'd have in a console application in order to make it a self hosted WCF Service.
In this case it's an Http service listening on port 7654.
static void Main(string[] args)
{
var webServiceHhost = new WebServiceHost(typeof(AppCmdService), new Uri("http://localhost:7654"));
ServiceEndpoint ep = webServiceHhost.AddServiceEndpoint(typeof(AppCmdService), new WebHttpBinding(), "");
var serviceDebugBehavior = webServiceHhost.Description.Behaviors.Find<ServiceDebugBehavior>();
serviceDebugBehavior.HttpHelpPageEnabled = false;
webServiceHhost.Open();
Console.WriteLine("Service is running");
Console.WriteLine("Press enter to quit ");
Console.ReadLine();
webServiceHhost.Close();
}
AppCmdService is a WCF Service class that looks like this (in my case). In your case you probably don't need a response from your service. In my case I'm getting a Json response. The actual implementation of what it is you're trying to do will be different obviously. But I'm assuming you already have that piece worked out. So simply call a method of that class from here.
[ServiceContract]
public class AppCmdService
{
[WebGet(UriTemplate = "/GetCurrentExcutingRequests/?", ResponseFormat= WebMessageFormat.Json)]
[OperationContract]
public IEnumerable<ExecutingRequestJson> GetCurrentExcutingRequests()
{
return CurrentExecutingRequestJsonProvider.GetCurrentExecutingRequests("localhost");
}
}
On your ASP.NET side, you don't really need a WCF client. All you need is a way to make an http call to the service. So you can simply use HttpWebRequest to make the call out to your service, which in turn execute your process.
Hope all of this makes sense?
Maybe this SO question helps you. There are several solutions (also for IIS6):
Restarting (Recycling) an Application Pool
IMHO the best you could do is to decide to go with a concrete approach an then when you run into an exception, to ask a concrete question with the source code of your approach. Otherwise it's just very vage to answer your question.