Windows Service installation - current directory - c#

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.

Related

Windows Service with FileSystemWatcher stops after the first event

I'm building a simple Windows Service (basically combining this tutorial with this class).
Now I have a "FROM" directory and two "TO" (TO1, TO2) directories. When I place a file into FROM, it should be copied both to TO1 and TO2. I install the service and I start it in the Service Control Manager where I see it's running. On the first run, it copies the file to TO1 and TO2 and the service is still running after that.
Then, when I place another file to FROM (with a different name), nothing happens. And refreshing the services I find that the service stopped.
Why does the service stop? It seems it stops just in the moment when I place the second file.
Here I register the file system watcher:
// File System Watcher
var fileSystemWatcher = new FileSystemWatcher();
fileSystemWatcher.Created += FileSystemWatcher_MoveOnCreate;
fileSystemWatcher.Path = this.fromPath;
fileSystemWatcher.EnableRaisingEvents = true;
And here is the event handler:
private void FileSystemWatcher_MoveOnCreate(object sender, FileSystemEventArgs e)
{
string FROM = Path.Combine(fromPath, e.Name);
string TO1 = Path.Combine(toPathOne, e.Name);
string TO2 = Path.Combine(toPathTwo, e.Name);
File.Copy(FROM, TO1);
File.Copy(FROM, TO2)
}
If the Windows Service stops there was an unhandled exception in your code somewhere. Try to surround the key point of your code ( maybe the entire body of the function FileSystemWatcher_MoveOnCreate ) with try{}catch(){} and log what's happening. In general you should add log to your windows service is the only way you can understand if things are going on anyway.

Where are the settings stored?

NOTE: The "possible duplicate" question refers to a totally and complete different theme (refering to visual studio user settings". This question is not related to that at all. Please verify before marking "possible duplicates"
I am trying to save some settings of my program between calls and I did what this tutorial says.
It works very well. A little too well...
To summarize I created settings.settings file. Then in the form closing file, I wrote code to save the settings
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.TheSetting = settingNumber;
Properties.Settings.Default.Save();
}
and in the load function code to retrieve the setting
private void Form1_Load(object sender, EventArgs e)
{
DateTime t = DateTime.Now;
if (Properties.Settings.Default.TheDate.Date == t.Date) //it is today
{
settingNumber = Properties.Settings.Default.TheSetting;
}
else
{
//we start again
settingNumber = 0;
}
textBox1.Text = settingNumber.ToString();
}
I tried and run it several times, now the setting Number is 39.
However, and this is the strange thing this value is not found anywhere. I opened the .exe.config file that is supposed to hold the setting values and they have totally different numbers. Even if I edit them (as in the tutorial) the program still runs with its number.
Where are these setting values stored?
Thanks to user swamy I found the required file.
It was in AppData folder (which is in the corresponding User Folder) then Local, and under the a folder named after the program and the file name is user.config. The path is a really long one
I read that this path can change in other versions

Use value dialog installation in windows service 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.

getting the full filenames from all top directory files in a specific directory and suppressing the occuring error

I met a not expected problem with getting just the top directory full filenames from a specific directory. C# throws an error and doesn't list anything in the specific directory.
But MS DOS has not a problem with my command: *"dir C:\windows\prefetch\*.pf"
Visual Basics 6 old "Dir Function" also does it without complaining.
The "Windows Explorer" opens it up and doesn't ask anything from me. Also "Nirsofts Tool Suit" lists it instantly without any problem. No one of this tools needs to run with special permissions, just a double click on the application icon and ready is the task.
I looked around and found nothing here, what would answer this weird problem. My user can access the directory, if I go with any other application into it, now there is the question why C# throws
an "Unauthorized Access Exception" which is totally weird, since I have access in this folder.
I don't want to elevate my application with admin permissions for it nor create extra a xml for it to run it with highest privileges. The not trustful yellow elevation box must be avoided.
Now my question: How it comes that I can not list the filenames in this folder when all other
applications can do that.
What code do I need if "Directory.GetFiles()" fails?
Is there any flag or property in the framework directory class which allows my application access to the files, whatever.
Here my code which fails (using System.IO):
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = textBox1.Text.Substring(0, 0); //clear the textBox1
//Unauthorized access exception and yellow bar in this line
foreach(string FileX in Directory.GetFiles(Path.Combine(Environment.GetEnvironmentVariable("windir"), "prefetch"), "*.pf"))
{
textBox1.Text += FileX;
}
}
Did I understand correctly that you only need the File-names with directory-names.
This code works for me, no elevations needed.
private void button1_Click(object sender, EventArgs e)
{
string folder = #"C:\windows\prefetch";
string filemask = #"*.pf";
string[] filelist = Directory.GetFiles(folder, (filemask));
//now use filelist[i] for any operations.
}

C#, working with files, "Unauthorized Access"?

I'm learning about opening and saving files with C# and it seems that vista won't let my program save to a file on the root of C:\ , unless I run it in administrator mode.
Any ideas how to allow my program to play around with whatever files it wants?
Thanks!
private string name;
private void open_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
name = openFileDialog1.FileName;
textBox1.Clear();
textBox1.Text = File.ReadAllText(name);
textBox2.Text = name;
}
}
private void save_Click(object sender, EventArgs e)
{
File.WriteAllText(name, textBox1.Text);
}
To make your program start with administrator rights, you have to change the manifest. This can be done by Add New Item -> General -> Application Manifest File. Open the manifest and set "requestedExecutionLevel" to "requireAdministrator". When this is done, open the project settings and on the 'Application' tab choose your new manifest.
The program will run with your credentials, by default.
So, these do not have the right permissions to write to the root folder.
If you want it to run with other credentials you can us the runas command line to execute the application with other credentials.
Alternatively, grant more permissions to the account the application runs as.
There are several reasons for the UnauthorizedAccess Exception. Check one of those:
path specified a file that is read-only.
This operation is not supported on the current platform.
path specified a directory.
I accidently hit the third problem today ;-)

Categories