C# Class Library: StreamWriter writing to system32 folder - c#

I have a class library which is deployed on an ISP server to be consumed by an ASP.NET web service. I'd like to keep track of any errors and in this case the windows event log is inaccessible to me. So I thought I'd write to a txt file using the StreamWriter class. Problem is if I don't give an absolute path and just a file name it tries to write to C:\Windows\System32, and that's no good to me.
How can I tell it to use maybe the data directory or the application root? Any thoughts?

Use Server.MapPath to get a path relative to the web application.
using (FileStream fs = new FileStream(Server.MapPath("~/logs/logfile.txt"),
FileMode.Append)) {
//do logging here.
}
While some of the previous posters have suggested using reflection to get the executing assembly, I'm not sure whether or not that will net you the web application or the w3wp process. If it's the latter, you're still going to end up trying to write to the System32 folder.

Here is what I used to use, it's a little clunky but it gets the job done:
using System;
using System.Collections.Generic;
using System.Web.UI;
public static class Logger
{
private static readonly Page Pge = new Page();
private static readonly string Path = Pge.Server.MapPath("~/yourLogPath/Log.txt");
private const string LineBreaker = "\r\n\r======================================================================================= \r\n\r";
public static void LogError(string myMessage, Exception e)
{
const LogSeverity severity = LogSeverity.Error;
string messageToWrite = string.Format("{0} {1}: {2} \r\n\r {3}\r\n\r {4}{5}", DateTime.Now, severity, myMessage, e.Message, e.StackTrace, LineBreaker);
System.IO.File.AppendAllText(Path, messageToWrite);
}
}
I had this class in it's own project, separate from the website itself, and I used it in all of my other non website projects...
Edit:
Btw LogSeverity is just an enum I made up...

In my web product, in the web.config I specify an appSettings block like this:
<configuration>
<appSettings>
<add key="MyLogPath" value="LogPath" />
</appSettings>
</configuration>
which you can use from the code like
ConfigurationManager.AppSettings["MyLogPath"]
then you can have the installer configure it to wherever you want. you probably don't want the log files in your application directory.

Try checking out:
Application.StartupPath;
Here's a link to the docs
Gets the path for the executable file
that started the application, not
including the executable name.
string path = Application.StartupPath;
Note: you'll still need to add a file name.

You can find out the path of your executable by doing this:
string path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

Related

c# Register custom dll as processor to Printer++ Config file

I am trying to create a custom printer driver to generate images. For this, I have installed Printer++ which converts print files to postscripts. To convert postscript file to image, I am using ghostscript. Independently both the processes are running fine and I am able to achieve what is required.
But, I need a custom process to generate images in one go. I followed through the Printer++ tutorial but it didn't work.
This is what I have done:
I installed Printer++ and gave the name of the printer driver as- Septane.
In Visual Studio, I created a project- Test.
And the following code in Processor.cs class:
using System;
using System.Net.Mail;
using PrinterPlusPlusSDK;
namespace Test
{
public class Processor : PrinterPlusPlusSDK.IProcessor
{
public PrinterPlusPlusSDK.ProcessResult Process(string key, string psFilename)
{
//Convert PS to Png
psFilename = "b.ps";
MessageBox.Show("Rahul");
ConvertPsToTxt(psFilename);
}
public static string ConvertPsToTxt(string psFilename, string txtFilename)
{
var retVal = string.Empty;
var errorMessage = string.Empty;
var command = "C:\\PrinterPlusPlus\\gs\\gswin64.exe";
var args = string.Format("-dNOPAUSE -dBATCH -dFirstPage=1 -q -r300 -sDEVICE=png256 -sOutputFile=", psFilename, txtFilename);
retVal = Shell.ExecuteShellCommand(command, args, ref errorMessage);
return retVal;
}
}
}
This class inherits from PrinterPlusPlusSDK.IProcessor and implements the PrinterPlusPlusSDK.ProcessResult Process function. I have tested the standalone console project (without using PrinterPlusPlusSDK processor) and that converts ps to png successfully.
Now, as per the tutorial, the dll needs to be deployed to printer++ and registered as a processor. I copied Test.dll to Printer++ installation folder and added an entry to PrinterPlusPlus.exe.config file.
The config entry looks like:
<processors>
<add key="Test"
value="Test.Processor, Septane, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</processors>
That's it. Now, when I print a file, it gives an error:
Proccessor not found for: Septane
What am I doing wrong?
If anyone has better idea for achieving the same, please let me know. Actually, mine is a commercial product so can't use CutePDF/VerPDf kind options.
Edit: I now know why I was getting error- Processor not found. I renamed my printer to Test and the error disappeared. I have edited my code as well.
For testing, I have added a message box. I expected to get a popup once I give print command. But that is not the case. The ps file is getting generated without an error and that's it. I can't view pop-up message and there is no png converted file. Can someone please help me resolve this issue at least? It doesn't seem to be picking up the dll at all.
Thanks.
Remember The Printer name must be same as the you dll name
If your printer driver name is Septane. Then you must have to create a project name with "Septane". In that case project name "Test" will not work.

Get relative file path in a class library project that is being referenced by a web project

I have an ASP.Net website that references a class library. In the class library I need to read a file into memory.
At the top level of my class library there is a folder called EmailTemplateHtml containing the file MailTemplate.html that I want to read in.
How can I do this?
In Visual Studio, you can configure your library such that the file is copied into the build directory of any project that depends upon it. Then you can get the path to the build directory at runtime in order to read your file.
Step by step instructions, starting from a fresh solution:
Create your application project and your class library project.
Add a reference to the class library project from the application project via Properties->Add->Reference from the application's context menu in Solution Explorer:
Create the file in your class library project that you need to read, then set its Copy to Output Directory property to either Copy always or Copy if newer via the Properties pane in Solution Explorer:
From within either the class library project or your application (either will work with exactly the same code), reference your file relative to Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location). For example:
using System.Reflection;
using System.IO;
namespace MyLibrary
{
public class MyClass
{
public static string ReadFoo()
{
var buildDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var filePath = buildDir + #"\foo.txt";
return File.ReadAllText(filePath);
}
}
}
(Note that back before .NET Core, you could use a file path relative to System.IO.Directory.GetCurrentDirectory() instead, but this doesn't work in a .NET Core application because the initial working directory for .NET Core apps is the source directory instead of the build directory, apparently because this was needed by ASP.NET Core.)
Go ahead and call your library code from your application code, and everything will work fine. e.g.:
using Microsoft.AspNetCore.Mvc;
using MyLibrary;
namespace AspCoreAppWithLib.Controllers
{
public class HelloWorldController : Controller
{
[HttpGet("/read-file")]
public string ReadFileFromLibrary()
{
return MyClass.ReadFoo();
}
}
}
using System.IO;
using System.Reflection;
public static string ExecutionDirectoryPathName()
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return Path.GetFullPath(Path.Combine(dirPath, "\EmailTemplateHtml\MailTemplate.html"));
}
If you want to find the path where the assembly is located; from within the assembly then use the following code:
public static string ExecutionDirectoryPathName
{
get
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return dirPath + #"\";
}
}
I am not sure what you mean by a folder in the class library but you can use the current working directory if you wish to build a path as follows:
System.IO.Directory.GetCurrentDirectory()
You can then use the Path.Combine() method to build file paths.
You can add a Static class in your class library with some static methods/properties to set. Set the values from Global.ascx.cs on start app method. Now you can get the values of class library.
Hope this makes clear.
Happy coding

ConfigurationManager Save() wants to create a tmp-File

I am having problems with a application that wants to write to the .exe.config.
See following code:
using System;
using System.Configuration;
using System.IO;
namespace ConfigurationManagerError
{
class Program
{
static void Main(string[] args)
{
try
{
// set Config-File to persistent folder
ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap();
exeMap.ExeConfigFilename = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"ConfigError\\ConfigurationManagerError.exe.config");
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(exeMap, ConfigurationUserLevel.None);
config.AppSettings.Settings.Add(
new KeyValueConfigurationElement("TestKey", "TestValue"));
config.Save(ConfigurationSaveMode.Modified);
Console.WriteLine("Successfully write of Configuration-File to " + config.FilePath);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}
}
}
As long as I run as a user with read and write access on the folder everything works.
If I have a user that has no write permission in the folder there is a exception that says that permission to write the .exe.config is not allowed.
Until now everything is as expected.
But if I now have a user that has the right to write to existing files but not to create new files an exception is thrown with something like
Access to the path 'C:\ProgramData\ConfigError\ila2ba_0.tmp' is denied
It seems that ConfigurationManager wants to create a tmp-File.
How can this be solved?
Thanks a lot!
Best regards,
Joerg
A user specific configuration setting which need to be rewritten multiple times should be saved under user's application data directory.
You can have a generic application configuration installed along with your application which can be merged with user specific configuration and required config setting can be overridden as per your requirement.

Starting an .exe from the same folder of my C# program

I made a small program in c# with a button that is supposed to open another .exe file.
It works fine if I use:
private void start_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process.Start(#"path to file");
}
But not if I want it to run an .exe from the same folder, i basically wanted something like:
private void start_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process.Start(#"program.exe");
}
What am I missing, I've tried a solution from this website:
var startIngo = new ProcessStartInfo();
startIngo.WorkingDirectory = // working directory
// set additional properties
Process proc = Process.Start(startIngo);
But Visual c# doesn't recognize "ProcessStartInfo" at all...
What your looking for is:
Application.StartupPath
It will return the startup path that your executable was started in.
If you are using WPF, try this instead:
String appStartPath = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
You can do:
var startupPath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetEntryAssembly().Location);
var programPath = System.IO.Path.Combine(startupPath, "program.exe");
System.Diagnostics.Process.Start(programPath);
ProcessStartInfo is in the System.Diagnostics namespace - you need to import that namespace at the top of your cs file using a using System.Diagnostics; statement for the compiler to recognise ProcessStartInfo without specifying the namespace explicitly where you use the class.
You could also try System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
To get your local path. For example
//in your imports/using section
using System.IO
using System.Reflection
using System.Diagnostics;
//in your code to execute
Process.start(Path.GetDirectoryName(Aseembly.GetExecutingAssembly().GetName().CodeBase) + "\\program.exe")
There are two cases:
The application was started directly - start up path can be extracted from the command-line.
The application was started indirectly - e.g. from a unit-test, start up path can not be extracted from the command-line, however you can read it from the current directory into a static variable during the start-up (before the user has a chance to change it (e.g. using a file open/save dialog)).

Accessing App.config in a location different from the binary

In a .NET Win console application, I would like to access an App.config file in a location different from the console application binary. For example, how can C:\bin\Text.exe get its settings from C:\Test.exe.config?
using System.Configuration;
Configuration config =
ConfigurationManager.OpenExeConfiguration("C:\Test.exe");
You can then access the app settings, connection strings, etc from the config instance. This assumes of course that the config file is properly formatted and your app has read access to the directory. Notice the path is not "C:\Test.exe.config" The method looks for a config file associated with the file you specify. If you specify "C:\Test.exe.config" it will look for "C:\Test.exe.config.config" Kinda lame, but understandable, I guess.
Reference here: http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
It appears that you can use the AppDomain.SetData method to achieve this. The documentation states:
You cannot insert or modify system entries with this method.
Regardless, doing so does appear to work. The documentation for the AppDomain.GetData method lists the system entries available, of interest is the "APP_CONFIG_FILE" entry.
If we set the "APP_CONFIG_FILE" before any application settings are used, we can modify where the app.config is loaded from. For example:
public class Program
{
public static void Main()
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", #"C:\Temp\test.config");
//...
}
}
I found this solution documented in this blog and a more complete answer (to a related question) can be found here.
Use the following (remember to include System.Configuration assembly)
ConfigurationManager.OpenExeConfiguration(exePath)
You can set it by creating a new app domain:
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ConfigurationFile = fileLocation;
AppDomain add = AppDomain.CreateDomain("myNewAppDomain", securityInfo, domainSetup);
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ConfigurationFile = #"D:\Mine\Company\";
string browserName = ConfigurationManager.AppSettings["browser"];

Categories