Windows service working directory

Relative paths are resolved relative to the current working directory. When you’re running a Windows Service, the default working directory is C:\Windows\system32 or C:\Windows\SysWOW64. Therefore relative paths are resolved from these system folders, which can lead to problems when read/writing files.

Here are the most common problems you’ll run into:

  • File or Directory cannot be found.

System.IO.DirectoryNotFoundException: Could not find a part of the path ‘C:\WINDOWS\system32\Configs\Test.ini’.

  • Access error. Your service is trying to access a system folder and doesn’t have permission.

System.UnauthorizedAccessException: Access to the path ‘C:\WINDOWS\system32\makolyteService.log‘ is denied.

  • When writing a file, it doesn’t appear in the expected directory. Instead, it shows up in the system folder, like this:
C:\Windows\System32\makolyteService.log

This problem is not specific to Windows Services. It happens anytime anytime you’re trying to use relative paths from an unexpected working directory.

You have two options for using relative paths correctly:

  • Change the current working directory.
  • Resolving the relative paths from where the executable is located.

Example

I created a Windows Service that does the following:

  • Reads text from relative path: .\Configs\Test.ini
  • Writes the text to relative path: makolyteService.log

First, get your current working directory

If you are having problems with relative paths, the first step is to figure out where your code is running from. For this, you can use System.IO.Directory.GetCurrentDirectory(), like this:

EventLog.WriteEntry($"Current working directory: {System.IO.Directory.GetCurrentDirectory()}");
Code language: C# (cs)

This is telling me that the current working directory is C:\Windows\system32.

Option 1 – Changing the current working directory

Before calling any file read/write operations, change the current working directory to where the executable is located on disk.

System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
Code language: C# (cs)

This is the best option. It fixes all file read/write operations without any additional code changes.

Option 2 – Resolve paths from the executable’s location on disk

If you can’t use option 1, then your other option is to resolve the file paths before reading/writing.

  • Create a method that resolves paths from the executable’s location on disk.
private string ResolvePath(string filePath)
{
	if(Path.IsPathRooted(filePath))
	{
		return filePath;
	}
	else
	{
		return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath);
	}
}

Code language: C# (cs)

Note: IsPathRoot(…) is a heuristic approach to finding out if a path is absolute. If a path is rooted, it is almost always an absolute path. There are some path formats that are rooted and relative, but I’ve never seen these used in practice.

  • Use the resolved paths when reading/write. For example, this is reading a file using the resolved path:
private string ReadFile(string filePath)
{
	return File.ReadAllText(ResolvePath(filePath));
}
Code language: C# (cs)

Code

For reference, here is my Windows Service. This is showing how to fully use option 2.

protected override void OnStart(string[] args)
{
	//This commented out line is for option 1. Use this if you can. 
	//System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

	EventLog.WriteEntry($"Current working directory: {System.IO.Directory.GetCurrentDirectory()}");

	string readFromFilePath = @".\Configs\Test.ini";
	string writeToFilePath = "makolyteService.log";

	string fileContents = "";

	try
	{
		fileContents = ReadFile(readFromFilePath);
	}
	catch(Exception ex)
	{
		fileContents = $"Exception while trying to read the file. {ex}";
	}

	WriteFile(writeToFilePath, fileContents);

   
}
private string ResolvePath(string filePath)
{
	if(Path.IsPathRooted(filePath))
	{
		return filePath;
	}
	else
	{
		return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath);
	}
}

private string ReadFile(string filePath)
{
	return File.ReadAllText(ResolvePath(filePath));
}
private void WriteFile(string filePath, string fileContents)
{
	File.WriteAllText(ResolvePath(filePath), fileContents);
}
Code language: C# (cs)

Related Articles

I’ve been developing a windows NT service for the last week and I hit an interesting issue today. The configuration for the service contains a relative path which is used in the construction of a class. When this constructor is called, the code checks if the directory exists and creates it if it doesn’t.

The service in question logged errors indicating that there were insufficient privileges for creating the directory. This told me two things. Firstly the directory didn’t exist and secondly there is a permissions problem. I verified that the directory was created by the installer so that means that there is an issue with relative paths.

I quickly realised that the current working directory of a Windows service is probably not the path of the service assembly. Most likely the current directory for a Windows service is C:\Windows\System32. This meant that any work with relative paths was likely to cause grief.

The simple fix for my service is to set the current directory for the process to the directory containing the executing assembly when the service is started.

using System;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
    
internal static class Program
{
    private static void Main()
    {
    	Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    
    	MyService service = new MyService();
    
    	ServiceBase.Run(service);
    }
}

Now any configured relative path will be relative to the service assembly. Problem solved.

A warning for the unwary – you might expect that your Windows service is started with its current working directory set to the location of the service executable but it isn’t – it’s set to the Windows System32 directory.

There’s no reason why you shouldn’t change the working directory if it makes sense to do so. As an example of why you might want to, this is how I stumbled across this issue:

The project I’m currently working on uses a Windows service written in C# that communicates with a proprietary medical data store via a Win32 DLL. The .NET Framework successfully locates the DLL when my code calls the functions in the DLL via P/Invoke. I’m guessing that the framework is locating the DLL using a technique similar to the one it uses when probing for referenced assemblies, i.e. it looks in the directory where the entry assembly was loaded from.

The problem I encountered is that the code in the DLL (PASData.dll) looks for its configuration file (PASData.inf) in the current working directory, i.e. C:\Windows\System32 and fails to find it there.

I added the following code to the OnStart() method of the service to set the current working directory:

string assemblyLocationFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (string.Compare(Environment.CurrentDirectory, assemblyLocationFolder, StringComparison.OrdinalIgnoreCase) != 0)
{
    Environment.CurrentDirectory = assemblyLocationFolder;
}

Skip to content



Navigation Menu

Provide feedback

Saved searches

Use saved searches to filter your results more quickly

Sign up

Appearance settings

Description

winsw has an optional parameter called workingdirectory which sets the current directory of the service.

How can it be set in node-windows?

Metadata

Metadata

Development

No branches or pull requests

Issue actions

    Change current directory for Windows Service to application directory

    By default current directory for Windows Service is c:\windows\System32

    To change it to point to your application directory execute the following in the initialization section of your windows service

    Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

    Popular posts from this blog

    Multiple TeamCity Build Agents on one Server

    multiple-teamcity-build-agents-on-one-server

    Tibco Rendezvous (tibrv) C# .Net example

    This is an example of broadcast messaging using TIBCO Rendezvous . You can find more information about queue, dispatcher, transport and other rendezvous concepts in the TIBCO Rendezvous® Concepts pdf documentation. Command Line: Receiving messages tibrvlisten –service <port> -network «<ip-address>» –daemon <port> «<subject>» tibrvlisten –service <port> -network «;<multicast-ip-address>» –daemon <port> «<subject>» tibrvlisten -service 7500 -network «192.168.xx.xxx» -daemon 7500 «ME.TEST» tibrvlisten -service 7500 -network «192.168.xx.xxx» -daemon 7500 «ME.>» Publishing messages tibrvsend –service <port> -network «<ip-address>» –daemon <port> «<subject>» «<your-message>» tibrvsend -service 7500 -network «192.168.xx.xxx» -daemon 7500 «ME.TEST» «He…

    Parse XML to dynamic object in C#

    <? xml version=»1.0″ encoding=»utf-8″ ?> < contacts > < contact id = ‘1’ > < firstName > Michael </ firstName > < lastName > Jordan </ lastName > < age > 40 </ age > < dob > 1965 </ dob > < salary > 100.35 </ salary > </ contact > < contact id = ‘2’ > < firstName > Scottie </ firstName > < lastName > Pippen </ lastName > < age > 38 </ age > < dob > 1967 </ dob > < salary > 55.28 </ salary > </ contact > </ contacts > public class XmlToDynamic { public static void Parse(dynamic parent, XElement node) { if (node.HasElements) { if (node.Elements(node.Elements().First().Name.LocalName).Count() > 1) { //list var item = new ExpandoObject(); var list = new List<dynamic…

    Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии
  • Windows network disk mount
  • Pip install cv2 windows
  • Как установить на виртуал бокс windows server
  • Ошибка при установке windows xp 0xc000007b
  • Saflok windows cs650003rt комплект для стойки размещения