Use value dialog installation in windows service c# - c#

We are develop a windows service for open a specific port.
Now this port can be custom for the user during the installation in a dialog.
I want know a possibility of capture this value and pass to the code of the service
if (myServer == null)
{
int port= int.Parse(ConfigurationManager.AppSettings["port1"]);
myServer = new NHttp.HttpServer
{
EndPoint = new System.Net.IPEndPoint(0, port)
};
}
myServer.Start();
I try using a value in app.config and editing this value in the installer:
public override void Install(System.Collections.IDictionary stateSaver)
{
string portServer= this.Context.Parameters["CTPUERTO"];
System.Configuration.ConfigurationManager.AppSettings.Set("port1", portServer);
base.Install(stateSaver);
}
CTPUERTO is the name of the textbox in the dialog install

You add the optional TextBoxes(A) dialog to your setup project and the user enters that text (in EDITA1 in the docs):
https://msdn.microsoft.com/en-us/library/e04k6f53(v=vs.100).aspx
Then in your custom action you'd add the parameter with something like:
/port1=[EDITA1]
in CustomActionData, then access it using the kind of code you showed, in an installer class.
These might be useful:
.net Setup Project: How to pass multiple CustomActionData fields
https://www.codeproject.com/Articles/12780/A-Setup-and-Deployment-project-that-passes-paramet
The main issue with this (because of the way VS setup projects work) is that you cannot validate it at the time it's entered. Custom actions in VS setup projects run after the UI and after everything is installed, so if your custom finds it's incorrect then you fail the whole install and roll back.

Related

How to self-register a standalone desktop C# WPF app (distributed as an exe) for local toast notifications?

We have a desktop Windows app (written in WFP/C#) that we distribute as a single .exe file with no installer (it bundles all its dependencies via a Fody/Costura plugin).
We would like to integrate a local Action Center toast functionality where the app can display a toast and respond to it when it's clicked.
Displaying the toast is straightforward and can be done by using the Microsoft.Toolkit.Uwp.Notifications nuget package. However, in order to actually receive proper notifications when the toast is clicked in the Action Center (as opposed to the balloon tip) we need to register with notification platform.
The guide on how to do this seems to be focused on apps with an installer (e.g. Wix): https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop?fbclid=IwAR2AoHRKI88VNGRG-pTUytwhkMuovWT4bEr0RoXEayWpWsoGlghtZeq4Mo4#step-4-register-with-notification-platform
The specific task we're trying to achieve is, from the documentation:
If you're using classic Win32 (or if you support both), you have to declare your Application User Model ID (AUMID) and toast activator CLSID (the GUID from step #3) on your app's shortcut in Start.
How can we do it without writing an installer? We would like our app to do this registration on first run.
Note: the app already has provisions for elevating itself through UAC if needed by restarting itself in Administrator context.
Additional references: WPF native windows 10 toasts
[Update]
I managed to follow the instructions in https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop
and https://learn.microsoft.com/en-us/windows/win32/shell/enable-desktop-toast-with-appusermodelid to put together what should have been a working solution, but in the end, clicking on toasts in the Action Center does not trigger OnActivated() in my NotificationActivatior.
Salient points:
Sending notification
var toast = new ToastNotification(toastXml);
DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
Registration:
string shortcutPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Programs),
"Toasty.lnk");
DesktopNotificationManagerCompat.RegisterAumidAndComServer
<MyNotificationActivator>(AppName);
DesktopNotificationManagerCompat.RegisterActivator
<MyNotificationActivator>();
if (!File.Exists(shortcutPath))
{
ShortcutManager.RegisterAppForNotifications(
shortcutPath,
Assembly.GetExecutingAssembly().Location,
null,
AppName,
ActivationId);
}
Creating a shortcut
public static void RegisterAppForNotifications(
string shortcutPath,
string appExecutablePath,
string arguments,
string appName,
string activatorId)
{
var shellLinkClass = new ShellLinkCoClass();
IShellLinkW shellLink = (IShellLinkW)shellLinkClass;
shellLink.SetPath(appExecutablePath);
IPropertyStore propertyStore = (IPropertyStore)shellLinkClass;
IPersistFile persistFile = (IPersistFile)shellLinkClass;
if (arguments != null)
{
shellLink.SetArguments(arguments);
}
// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-id
propertyStore.SetValue(
new PropertyKey("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3", 5),
new PROPVARIANT(appName));
// https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-toastactivatorclsid
propertyStore.SetValue(
new PropertyKey("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3", 26),
new PROPVARIANT(new Guid(activatorId)));
propertyStore.Commit();
persistFile.Save(shortcutPath, true);
}
[Update]
Finally got it to work - not sure what was wrong before, but the final version seems to be okay. Full code: https://gist.github.com/davidair/c4ea207bf6eece4ef08b97ab29a3036f
I have the same problem with my project now.
Managed to find this repository - https://github.com/felixrieseberg/electron-windows-interactive-notifications
Here's C++ implementation for installing shortcut (InteractiveNotifications file, InstallShortcut method). I guess the problem is how we set the value to PropertyStore, string GUID is not suitable for some reason. Still, I wasn't able to solve the problem for now.
UPDATED: Finally, was able to install shortcut from code! Check my example at Github. https://github.com/romayavorskyi/WpfNotificationTest (still a lot of hardcode, but it should give you the general idea). And you were right, shortcut path matters. It seems shortcut should be in the ProgramData folder for correct work.

C# write cell to In-Sight Explorer (cognex)

I found this page where there is an example for extract cell information from an Insight-Explorer but... what about to write into the cell from a c# application?
C# extract Cell Information from In-Sight Explorer (Cognex)
If you want to control the cells in the current job file of an In-Sight camera using C#, here is the method that has worked for me. Please note, I'm using In-Sight Explorer v5.9.0, but I've tested this on earlier versions too and it still works.
NOTE: Without a Cognex In-Sight SDK license, you will not be able to run this application within Visual Studio. You will have to build the project and then run the executable directly.
Open Visual Studio
Create a new Console App (.NET Framework) project
I'm using .NET Framework 4.7.2
Right-click on the project in the solution explorer and add a reference to the Cognex.InSight.dll file (It is typically located here: C:\Program Files (x86)\Common Files\Cognex\In-Sight\5.x.x.x\Cognex.InSight.dll)
Set the target platform to x86
Paste the code, below, into your project
Change the username, password and ipAddress variables to match what's setup for your camera
Build
Go to the Debug folder and find the executable that was created after building the project
Double-click the executable to run it
using System;
using Cognex.InSight;
namespace ChangeInSightCellValue2
{
class Program
{
static void Main(string[] args)
{
string username = "admin";
string password = "";
string ipAddress = "127.0.0.1";
// Create camera object and connect to it
CvsInSight camera = LogIntoCamera(ipAddress, username, password, true, false);
// Define which cell you want to modify
CvsCellLocation cell = new CvsCellLocation(2, 'C');
// Modify the cell expression
camera.SetExpression(cell, "Filter($A$0,0,0,0,80,100,320,440,0,0,3,3,1,128,128,128,1,1,0)", true);
}
// Log into camera
private static CvsInSight LogIntoCamera(string sCamIP, string sCamUsername, string sCamPassword, bool forceConnect, bool connectAsynchronous)
{
// Create camera object
CvsInSight insight = new CvsInSight();
Console.WriteLine("Object created");
IAsyncResult result;
// Try logging into the camera on a different thread to prevent locking this one up
Action action = () =>
{
// Connect to camera
insight.Connect(sCamIP, sCamUsername, sCamPassword, forceConnect, connectAsynchronous);
};
result = action.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(5000))
return insight;
else
return insight;
}
}
}
NOTE: If you're connected to the camera with In-Sight Explorer when you run this application, the In-Sight Explorer will disconnect from the camera and then try to reconnect after your application has disconnected from the camera.
You could use Native Mode Commands to set the value of controls in the spreadsheet in In-Sight Explorer (as discussed in the question you linked to). Note that you won't be able to write data to any cell - you will only be able to write to cells containing EditInt(), EditFloat(), EditString(), CheckBox(), etc functions. Send the commands as text over a socket connection to the cameras port 23. You will need to send a username and password to the camera when the connection is established.
If you're using the Cognex SDK, use the following functions
CvsInSight.SetFloat(...) to set EditFloat control values
CvsInSight.SetInteger(...) to set EditInt control values
CvsInSight.SetListBoxIndex(...) to select items in list boxes
CvsInSight.SetString(...) to set EditString control values
CvsInSight.SetCheckBox(...) to change the state of CheckBox controls

Clicking a button on a windows from from a webform

I have created a windows form program that does some Business Intelligence but have subsequently needed to access this via a webform. I am fairly new to programming so I don't know what can be achieved here. But essentially I am wanting someone to go on to the net and when the user presses a button it sends a message to the windows form executable in a file and tells it to run and then press a button on the form, which runs a method to generate images of graphs. Is this possible?
Here is the relevant code.
In the webform I have this button.
protected void rolloutSmartSheets(object sender, EventArgs e)
{
string message = string.Format("Starting processes");
ltMessage.Text = message;
Process process = new Process();
process.StartInfo.FileName = #"P:\Visual Studio 2013\Projects\Smartsheet\SmartsheetAPI\obj\Debug\SmartSheetAPI.exe";
process.Start();
message = string.Format("Ended all processes");
ltMessage.Text = message;
}
That runs the executable but it opens the windows form and I imagine if the executable is sitting on another computer wouldn't that open on that computer? In which case i want it to tell it to press this button on the windows form which runs the method I need and then the user doesn't need to worry about it.
public void commitToDatabase_Click(object sender, EventArgs e)
{
commitToDataBase();
}
If you are able to have the clients install something in advance, you can provide this functionality using a custom protocol handler.
Example protocol handler from MSDN Article
HKEY_CLASSES_ROOT
alert
(Default) = "URL:Alert Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "alert.exe,1"
shell
open
command
(Default) = "C:\Program Files\Alert\alert.exe" "%1"
Then add a link onto your webform like this
href="alert://test"
As long as the client has the handler installed, both in the registry and the executable file, it will run C:\Program Files\Alert\alert.exe, passing "test" to it as the first paramater.
You can easily change this to provide the ability to run the local graph generator, and pass any parameters from the webform you might need.

Windows Service installation - current directory

this question is related to my previous one . I've written a service in C# and I need to make it's name dynamic and load the name from configuration file. The problem is that current directory while the service installer is invoked is the net framework 4 directory instead of the one that my assembly sits in.
Using the line (which helps with the same problem, but while the service is already running)
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
sets the directory to
C:\Windows\Microsoft.NET\Framework\v4.0.30319
which was also the initial value.
How to get the right path?
try this one:
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
You can also try
Assembly.GetExecutingAssembly( ).Location
That also works if you're not referencing winforms or wpf
We had the same problem in a project i was working on but we took a different approach. Instead of using App.config files that has to be in the same path as the executable, we changed both the installer class and the Main entry point of the service.
We did this because we didn't want the same project files in different locations. The idea was to use the same distribution files, but with different service names.
So what we did was inside our ProjectInstaller:
private void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e)
{
string keyPath = #"SYSTEM\CurrentControlSet\Services\" + this.serviceInstaller1.ServiceName;
RegistryKey ckey = Registry.LocalMachine.OpenSubKey(keyPath, true);
// Pass the service name as a parameter to the service executable
if (ckey != null && ckey.GetValue("ImagePath")!= null)
ckey.SetValue("ImagePath", (string)ckey.GetValue("ImagePath") + " " + this.serviceInstaller1.ServiceName);
}
private void ProjectInstaller_BeforeInstall(object sender, InstallEventArgs e)
{
// Configura ServiceName e DisplayName
if (!String.IsNullOrEmpty(this.Context.Parameters["ServiceName"]))
{
this.serviceInstaller1.ServiceName = this.Context.Parameters["ServiceName"];
this.serviceInstaller1.DisplayName = this.Context.Parameters["ServiceName"];
}
}
private void ProjectInstaller_BeforeUninstall(object sender, InstallEventArgs e)
{
if (!String.IsNullOrEmpty(this.Context.Parameters["ServiceName"]))
this.serviceInstaller1.ServiceName = this.Context.Parameters["ServiceName"];
}
We used InstallUtil to instal our service like this:
[FramerokPath]\installutil /ServiceName=[name] [ExeServicePath]
Then, inside the Main entry point of your application, we checked the args attribute to get what was the installation name of the service that we setted inside the AfterInstall event.
This approach has some issues, like:
We had to create a default name for the service that was installed without the parameter. For instance, if no name was passed to our service, then we use the default one;
You can change the service name passed to our application to be different from the one that was installed.

What is the most reliable way to create a custom event log and event source during the installation of a .Net Service

I am having difficulty reliably creating / removing event sources during the installation of my .Net Windows Service.
Here is the code from my ProjectInstaller class:
// Create Process Installer
ServiceProcessInstaller spi = new ServiceProcessInstaller();
spi.Account = ServiceAccount.LocalSystem;
// Create Service
ServiceInstaller si = new ServiceInstaller();
si.ServiceName = Facade.GetServiceName();
si.Description = "Processes ...";
si.DisplayName = "Auto Checkout";
si.StartType = ServiceStartMode.Automatic;
// Remove Event Source if already there
if (EventLog.SourceExists("AutoCheckout"))
EventLog.DeleteEventSource("AutoCheckout");
// Create Event Source and Event Log
EventLogInstaller log = new EventLogInstaller();
log.Source = "AutoCheckout";
log.Log = "AutoCheckoutLog";
Installers.AddRange(new Installer[] { spi, si, log });
The facade methods referenced just return the strings for the name of the log, service, etc.
This code works most of the time, but recently after installing I started getting my log entries showing up in the Application Log instead of the custom log. And the following errors are in the log as well:
The description for Event ID ( 0 ) in Source ( AutoCheckout ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details.
For some reason it either isn't properly removing the source during the uninstall or it isn't creating it during the install.
Any help with best practices here is appreciated.
Thanks!
In addition, here is a sample of how I am writing exceptions to the log:
// Write to Log
EventLog.WriteEntry(Facade.GetEventLogSource(), errorDetails, EventLogEntryType.Error, 99);
Regarding stephbu's answer: The recommended path is an installer script and installutil, or a Windows Setup routine.
I am using a Setup Project, which performs the installation of the service and sets up the log. Whether I use the installutil.exe or the windows setup project I believe they both call the same ProjectInstaller class I show above.
I see how the state of my test machine could be causing the error if the log isn't truly removed until rebooting. I will experiment more to see if that solves the issue.
Edit:
I'm interested in a sure fire way to register the source and the log name during the installation of the service. So if the service had previously been installed, it would remove the source, or reuse the source during subsequent installations.
I haven't yet had an opportunity to learn WiX to try that route.
The ServiceInstaller class automatically creates an EventLogInstaller and puts it inside its own Installers collection.
Try this code:
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Password = null;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
// serviceInstaller
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceInstaller.ServiceName = "MyService";
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "My Service Description";
// kill the default event log installer
serviceInstaller.Installers.Clear();
// Create Event Source and Event Log
EventLogInstaller logInstaller = new EventLogInstaller();
logInstaller.Source = "MyService"; // use same as ServiceName
logInstaller.Log = "MyLog";
// Add all installers
this.Installers.AddRange(new Installer[] {
serviceProcessInstaller, serviceInstaller, logInstaller
});
Couple of things here
Creating Event Logs and Sources on the fly is pretty frowned upon. primarily because of the rights required to perform the action - you don't really want to bless your applications with that power.
Moreover if you delete an event log or source the entry is only truely deleted when the server reboots, so you can get into wierd states if you delete and recreate entries without bouncing the box. There are also a bunch of unwritten rules about naming conflicts due to the way the metadata is stored in the registry.
The recommended path is an installer script and installutil, or a Windows Setup routine.
The best recommendation would be to not use the Setup Project in Visual Studio. It has very severe limitations.
I had very good results with WiX
I have to agree with stephbu about the "weird states" that the event log gets into, I've run into that before. If I were to guess, some of your difficulties lie there.
However, the best way that I know of to do event logging in the application is actually with a TraceListener. You can configure them via the service's app.config:
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogtracelistener.aspx
There is a section near the middle of that page that describes how to use the EventLog property to specify the EventLog you wish to write to.
Hope that helps.
I also followed helb's suggestion, except that I basically used the standard designer generated classes (the default objects "ServiceProcessInstaller1" and "ServiceInstaller1"). I decided to post this since it is a slightly simpler version; and also because I am working in VB and people sometimes like to see the VB-way.
As tartheode said, you should not modify the designer-generated ProjectInstaller class in the ProjectInstaller.Designer.vb file, but you can modify the code in the ProjectInstaller.vb file. After creating a normal ProjectInstaller (using the standard 'Add Installer' mechanism), the only change I made was in the New() of the ProjectInstaller class. After the normal "InitializeComponent()" call, I inserted this code:
' remove the default event log installer
Me.ServiceInstaller1.Installers.Clear()
' Create an EventLogInstaller, and set the Event Source and Event Log
Dim logInstaller As New EventLogInstaller
logInstaller.Source = "MyServiceName"
logInstaller.Log = "MyCustomEventLogName"
' Add the event log installer
Me.ServiceInstaller1.Installers.Add(logInstaller)
This worked as expected, in that the installer did not create the Event Source in the Application log, but rather created in the new custom log file.
However, I had screwed around enough that I had a bit of a mess on one server. The problem with the custom logs is that if the event source name exists associated to the wrong log file (e.g. the 'Application' log instead of your new custom log), then the source name must first be deleted; then the machine rebooted; then the source can be created with association to the correct log. The Microsoft Help clearly states (in the EventLogInstaller class description):
The Install method throws an exception
if the Source property matches a
source name that is registered for a
different event log on the computer.
Therefore, I also have this function in my service, which is called when the service starts:
Private Function EventLogSourceNameExists() As Boolean
'ensures that the EventSource name exists, and that it is associated to the correct Log
Dim EventLog_SourceName As String = Utility.RetrieveAppSetting("EventLog_SourceName")
Dim EventLog_LogName As String = Utility.RetrieveAppSetting("EventLog_LogName")
Dim SourceExists As Boolean = EventLog.SourceExists(EventLog_SourceName)
If Not SourceExists Then
' Create the source, if it does not already exist.
' An event log source should not be created and immediately used.
' There is a latency time to enable the source, it should be created
' prior to executing the application that uses the source.
'So pass back a False to cause the service to terminate. User will have
'to re-start the application to make it work. This ought to happen only once on the
'machine on which the service is newly installed
EventLog.CreateEventSource(EventLog_SourceName, EventLog_LogName) 'create as a source for the SMRT event log
Else
'make sure the source is associated with the log file that we want
Dim el As New EventLog
el.Source = EventLog_SourceName
If el.Log <> EventLog_LogName Then
el.WriteEntry(String.Format("About to delete this source '{0}' from this log '{1}'. You may have to kill the service using Task Manageer. Then please reboot the computer; then restart the service two times more to ensure that this event source is created in the log {2}.", _
EventLog_SourceName, el.Log, EventLog_LogName))
EventLog.DeleteEventSource(EventLog_SourceName)
SourceExists = False 'force a close of service
End If
End If
Return SourceExists
End Function
If the function returns False, the service startup code simply stops the service. This function pretty much ensures that you will eventually get the correct Event Source name associated to the correct Event Log file. You may have to reboot the machine once; and you may have to try starting the service more than once.
I am having the same problems. In my case it seems that Windows installer is adding the event source which is of the same name as my service automatically and this seems to cause problems. Are you using the same name for the windows service and for the log source? Try changing it so that your event log source is called differently then the name of the service.
I just posted a solution to this over on MSDN forums which was to that I managed to get around this using a standard setup MSI project. What I did was to add code to the PreInstall and Committed events which meant I could keep everything else exactly as it was:
SortedList<string, string> eventSources = new SortedList<string, string>();
private void serviceProcessInstaller_BeforeInstall(object sender, InstallEventArgs e)
{
RemoveServiceEventLogs();
}
private void RemoveServiceEventLogs()
{
foreach (Installer installer in this.Installers)
if (installer is ServiceInstaller)
{
ServiceInstaller serviceInstaller = installer as ServiceInstaller;
if (EventLog.SourceExists(serviceInstaller.ServiceName))
{
eventSources.Add(serviceInstaller.ServiceName, EventLog.LogNameFromSourceName(serviceInstaller.ServiceName, Environment.MachineName));
EventLog.DeleteEventSource(serviceInstaller.ServiceName);
}
}
}
private void serviceProcessInstaller_Committed(object sender, InstallEventArgs e)
{
RemoveServiceEventLogs();
foreach (KeyValuePair<string, string> eventSource in eventSources)
{
if (EventLog.SourceExists(eventSource.Key))
EventLog.DeleteEventSource(eventSource.Key);
EventLog.CreateEventSource(eventSource.Key, eventSource.Value);
}
}
The code could be modified a bit further to only remove the event sources that didn't already exist or create them (though the logname would need to be stored somewhere against the installer) but since my application code actually creates the event sources as it runs then there's no point for me. If there are already events then there should already be an event source. To ensure that they are created, you can just automatically start the service.
I experienced some similar weird behaviour because I tried to register an event source with the same name as the service I was starting.
I notice that you also have the DisplayName set to the same name as your event Source.
On starting the service up, we found that Windows logged a "Service started successfully" entry in the Application log, with source as the DisplayName. This seemed to have the effect of registering Application Name with the application log.
In my event logger class I later tried to register Application Name as the source with a different event log, but when it came to adding new event log entries they always got added to the Application log.
I also got the "The description for Event ID ( 0 ) in Source" message several times.
As a work around I simply registered the message source with a slightly different name to the DisplayName, and it's worked ever since. It would be worth trying this if you haven't already.
The problem comes from installutil which by default registers an event source with your services name in the "Application" EventLog. I'm still looking for a way to stop it doing this crap. It would be really nice if one could influence the behaviour of installutil :(
Following helb's suggestion resolved the problem for me. Killing the default event log installer, at the point indicated in his example, prevented the installer from automatically registering my Windows Service under the Application Event log.
Far too much time was lost attempting to resolve this frustrating quirk. Thanks a million!
FWIW, I could not modify the code within my designer-generated ProjectInstaller class without causing VS to carp about the mods. I scrapped the designer-generated code and manually entered the class.
Adding an empty registry key to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application\MY_CUSTOM_SOURCE_NAME_HERE seems to work fine.
An easy way to change the default behavior (that is, that the project installer creates an event log source with the name of your service in the application log) is to easily modify the constructor of the project installer as following:
[RunInstaller( true )]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
//Skip through all ServiceInstallers.
foreach( ServiceInstaller ThisInstaller in Installers.OfType<ServiceInstaller>() )
{
//Find the first default EventLogInstaller.
EventLogInstaller ThisLogInstaller = ThisInstaller.Installers.OfType<EventLogInstaller>().FirstOrDefault();
if( ThisLogInstaller == null )
continue;
//Modify the used log from "Application" to the same name as the source name. This creates a source in the "Applications and Services log" which separates your service logs from the default application log.
ThisLogInstaller.Log = ThisLogInstaller.Source;
}
}
}

Categories