I'm trying to write log in Windows event viewer. I created class and create method for catching exceptions. Here is my code :
[EventSource(Name = "Samples-EventSourceDemos-EventLog")]
public sealed class MinimalEventSource : EventSource
{
public static MinimalEventSource Log = new MinimalEventSource();
[NonEvent]
public void WriteLog(Exception exception)
{
UnhandledException(exception.Message);
}
[Event(601, Message = "Unhandled exception occurred. Details: {0}", Keywords = EventKeywords.None, Level = EventLevel.Critical)]
private void UnhandledException(string exceptionMsg)
{
this.WriteEvent(601, exceptionMsg);
}
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
throw new Exception("TestException");
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MinimalEventSource.Log.WriteLog(e.ExceptionObject as Exception);
Process.GetCurrentProcess().Kill();
}
In Windows event viewer I couldn't find this log
I installed Microsoft.Diagnostics.Tracing.EventSource from nuget. It creates manifests after rebuilding. Here is debug folder
I decided to registr it by code :
string commandOfRegistringEventSource = "";
using (Process process = new Process())
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe",
Arguments = commandOfRegistringEventSource
};
process.StartInfo = startInfo;
process.Start();
}
I tried execute using wevtutil.exe im <EtwManifestManFile> /rf:"<EtwManifestDllFile>" /mf:"<EtwManifestDllFile>", but it shows errors like The system cannot find the file specified.,... Please help me to write cmd command of registring EventSource. Here is manifest
C:\Users\dilshodk\source\repos\ETW loggiing\ETW loggiing\bin\Debug\ETW loggiing.Samples-EventSourceDemos-EventLog.etwManifest.dll
C:\Users\dilshodk\source\repos\ETW loggiing\ETW loggiing\bin\Debug\ETW loggiing.Samples-EventSourceDemos-EventLog.etwManifest.man
You need some more steps to get this working. First of all, you need to set the Channel property of the Event attribute like this:
[EventSource(Name = "Samples-EventSourceDemos-EventLog")]
public sealed class MinimalEventSource : EventSource
{
public static MinimalEventSource Log = new MinimalEventSource();
[NonEvent]
public void WriteLog(Exception exception)
{
UnhandledException(exception.Message);
}
[Event(601, Channel = EventChannel.Admin, Message = "Unhandled exception occurred. Details: {0}", Keywords = EventKeywords.None, Level = EventLevel.Critical)]
private void UnhandledException(string exceptionMsg)
{
this.IsEnabled().Dump();
this.WriteEvent(601, exceptionMsg);
}
}
The, second, your EventSource need to be registered. The steps are outline here:
One requirement introduced by channel support is the need to statically register the ETW provider manifest. The NuGet package supports generating the files needed for static registration as part of your build. After your build completes a new step is run that generates a pair of files for each of the event source types defined in the project:
..etwManifest.man and
..etwManifest.dll
The first file contains the ETW manifest while the second one contains the binary form of the ETW manifest plus any needed native resources (localization string tables in particular).
The tool that generates the above two files is “eventRegister.exe” and it performs two functions:
It ensures the registration files are generated for all event source types that need static registration, and
It performs a number of validation checks on all the event source types defined in the output assembly.
Deploying your component will need to include these files and perform one registration step at installation time and one un-registration step at un-installation time.
Registration:
wevtutil.exe im <EtwManifestManFile> /rf:"<EtwManifestDllFullPathName>" /mf:"<EtwManifestDllFullPathName>"
Unregistration:
wevtutil.exe um <EtwManifestManFile>
For static registration eventRegister.exe generates manifests that include all localization information. This is needed because the manifest is generated at build time, when there’s no information regarding the culture in which the final application will run.
Note you will see that in the .etwManfest.man file that the build generated, there are path names for the resource file and manifest file in this file. They are the paths that existed at build time. These paths are NOT used if you use the /rf and /mf options. Thus you should always specify the /rf: and /mf options (unless you hand modify the .etwManifest.man file to specify deployment-time file paths for the DLL).
Finally, it is important that you use FULLY qualified names for the /mf: and /rf: options. You can use environment variables THAT ARE AVAILABLE TO ALL PROCESSes (e.g. %SystemRoot% or %ProgramFiles%), but you should not use relative paths (it is not clear what they are relative to, probably System32, but don’t count on it).
The general recommendation is to copy your etwManifest.dll and .etwManifest.man to a directory under %ProgramFiles% and then use wevtutil to register them at that location.
The easiest way to create the files described above is to add this NuGet Package as it will create those files when building your project. It comes with the docs in .docx format.
I have done this in the past write to the event log Application:
using (EventLog eventLog = new EventLog("Application"))
{
eventLog.Source = "Application";
eventLog.WriteEntry("Log message test", EventLogEntryType.Information, 101, 1);
}
Related
Currently, I'm using EventSourceAttribute to create a hierarchy of subfolders in Application and Services log in Event Viewer. This is my code
[EventSource(Name = "Service-MacClient-EventSource")]
public sealed class MinimalEventSource : EventSource
{
public static MinimalEventSource Log = new MinimalEventSource();
[Event(1, Channel = EventChannel.Operational, Message = "{0}", Level = EventLevel.Informational)]
public void Info(string msg)
{
WriteEvent(1, msg);
}
}
In Application and Services logs, the folders came out to be Service > MacClient > EventSource.
However, what I wanted was spaces between every capital letters in the folder names e.g. Service > Mac Client > Event Source. So I tried to change it to
[EventSource(Name = "Service-Mac Client-Event Source")]
But unfortunately, I encountered this error while building my project
MSXML Schema Validation Error 0xc00ce169. At Line=4, Column=426, 'ServiceMac ClientEvent Source' violates pattern constraint of '()|([_a-zA-Z][_0-9a-zA-Z]*)'.
May I know how do I add spaces to folder names that are being created in Event Viewer > Application and Services please? Thanks in advance!
Additional notes
Microsoft's User Experience Virtualization seems to show that adding spaces for names of subfolders in Application and Services log in Event Viewer is possible.
I am trying to Re-create my BeforeTestRun step to run my setup only once per whole execution not per thread.
I had a look a Custom Deployment steps I have implemented some already but For my setup i need to bring in some values from the app.config file I am trying something like this
my Default.srprofile file contains:
<DeploymentTransformation>
<GlobalSteps>
<Custom type="Test.CustomDeploymentStep, Test"></Custom>
</GlobalSteps>
</DeploymentTransformation>
and my CustomDeploymentStep.cs:
public class CustomDeploymentStep : IDeploymentTransformationStep
{
public static string baseUrl;
public void Apply(IDeploymentContext deploymentContext)
{
baseUrl = ConfigurationManager.AppSettings["URL"];
}
public void Restore(IDeploymentContext deploymentContext)
{
DoSomething();
}
}
My app config contains the following:
<add key="URL" value="http://google.com" />
But That does not work, The ConfigurationManager.AppSettings only returns one key and one value
"key" : "TestProjectRetargetTo35Allowed" "value":"true"
How can I load my configuration from app.config into the Apply() method in CustomDeploymentStep?
Also If there is a better/more efficient way of generating pre-defined data in specflow with thread safe execution, please do let me know
I ran into the same problem once I needed to use custom deployment steps in more than one project in a large solution. This appears to be a bug within the TechTalk.SpecRun.Framework. The error is likely "Error applying global deployment step. Global steps cannot contain test assembly specific settings." and if you look inside the TestAssembly while debugging you will see the TestAssemblyConfigFilePath is null and/or swallowing another exception.
It doesn't register a project specific configuration file. My workaround was to save the config file into debug and access what I need like so:
string appConfigFilePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\App.config";
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = appConfigFilePath;
var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
var baseUrl = config.AppSettings.Settings["URL"].Value;
I have an unmanaged dll so i wrote this to save the dll to file once the program is run.
Working code :
public static void ExtractResourceToFile()
{
if (!File.Exists("loader.dll"))
try
{
using (System.IO.FileStream fs = new System.IO.FileStream("loader.dll", System.IO.FileMode.Create))
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Kraken.Resources.loader.dll").CopyTo(fs);
Thread.Sleep(5000);
}
catch ( Exception ex)
{ }
}
Problem:
if the compiled Kraken.exe name changed the DLL is not being saved.
what I've tried :
public static void ExtractResourceToFile()
{
if (!File.Exists("loader.dll"))
try
{
string file = System.Reflection.Assembly.GetExecutingAssembly().Location;;
string app = System.IO.Path.GetFileNameWithoutExtension(file);
using (System.IO.FileStream fs = new System.IO.FileStream(file, System.IO.FileMode.Create))
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(app + ".Resources.loader.dll").CopyTo(fs);
Thread.Sleep(5000);
}
catch ( Exception ex)
{ }
}
I've also tried to get the current process name and use it, but same problem occurred.
See this:
How do I get the name of the current executable in C#?
Particularly these comments are interesting:
Beware of GetExecutingAssembly(): if you call this from a library
assembly, it returns the name of the library assembly, which is
different from the name of the entry assembly (i.e. the original
executable). If you use GetEntryAssembly(), it returns the name of the
actual executable, but it throws an exception if the process is
running under WCF (admittedly a rare situation). For the most robust
code, use Process.GetCurrentProcess().ProcessName. – Contango Nov 3
'10 at 12:01
Hmm, note that the returned string won't change even if you rename
the executable file by hand using the file explorer. While
Environment.GetCommandLineArgs()[0] changes along with the actual
executable filename (of course). Coincidentally, the second method
resulted better for my specific situation as I want the data folder to
be named as the actual executable filename. – Hatoru Hansou Jan 23 '14
at 2:25
Hatoru's comment seems to support Phil1970's comment, that GetExecutingAssembly() probably use the resource properties found in the assembly instead of the actual file name.
So I'd use
Process.GetCurrentProcess().ProcessName
as Contango suggested.
And as zerkms said, don't ignore exceptions.
I'm working on a plugin for a existing C# .NET Program. It's structured in a manner where you put your custom .dll file in Program Root/Plugins/your plugin name/your plugin name.dll
This is all working well, but now I'm trying to use NAudio in my project.
I've downloaded NAudio via Nuget, and that part works fine, but the problem is that it looks for the NAudio.dll in Program Root, and not in the folder of my plugin.
This makes it hard to distribute my plugin, because it would rely on users dropping the NAudio.dll in their Program Root in addition to putting the plugin into the "Plugins" folder.
Source:
SettingsView.xaml:
<Button HorizontalAlignment="Center"
Margin="0 5"
Width="120"
Command="{Binding SoundTestCommand,
Source={StaticResource SettingsViewModel}}"
Content="Sound Test" />
SettingsViewModel.cs:
using NAudio.Wave;
.
.
.
public void SoundTest()
{
IWavePlayer waveOutDevice;
WaveStream mainOutputStream;
WaveChannel32 inputStream;
waveOutDevice = new WaveOut();
mainOutputStream = new Mp3FileReader(#"E:\1.mp3");
inputStream = new WaveChannel32(mainOutputStream);
inputStream.Volume = 0.2F;
waveOutDevice.Init(mainOutputStream);
waveOutDevice.Play();
}
How can I get C# to look for NAudio in Program Root/Plugins/my plugin name/NAudio.dll instead of looking for it in Program Root/NAudio.dll ?
I'm using VS Express 2013, Target Framework is 4.5 and Output type is Class Library.
Edit:
I found 2 ways to make this work ( I'm not sure what the pros and cons of both methods are - if anyone knows I would appreciate additional information ).
Using the NuGet Package Costura.Fody.
After installing the NuGet package, I simply had to set all other References "Copy Local" to "False" and then set "Copy Local" for NAudio to "True".
Now when I build, the NAudio.dll is compressed and added to my own DLL.
Using the AssemblyResolver outlined below.
It didn't work right away though, so here is some additional information that may help anyone facing the same issue:
I put Corey's code as he posted it into the Helpers folder.
My entry point is Plugin.cs, the class is public class Plugin : IPlugin, INotifyPropertyChanged
In there, the entry method is public void Initialize(IPluginHost pluginHost), but simply putting PluginResolver.Init(path) did not work.
The host program uses WPF and is threaded and I had to use a dispatcher helper function of the host program to get it to work: DispatcherHelper.Invoke(() => Resolver.Init(path));
As mentioned, I'm currently unsure which method to use, but I'm glad I got it to work. Thanks Corey!
You can use the PATH environment variable to add additional folders to the search path. This works for native DLLs, but I haven't tried to use it for .NET assemblies.
Another option is to add a hook to the AssemblyResolve event on the current application domain and use a custom resolver to load the appropriate assembly from wherever you find it. This can be done at the assembly level - I use it in NAudio.Lame to load an assembly from a resource.
Something along these lines:
public static class PluginResolver
{
private static bool hooked = false;
public static string PluginBasePath { get; private set; }
public static void Init(string BasePath)
{
PluginBasePath = BasePath;
if (!hooked)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolvePluginAssembly;
hooked = true;
}
}
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args)
{
var asmName = new AssemblyName(args.Name).Name + ".dll";
var assemblyFiles = Directory.EnumerateFiles(PluginBasePath, "*.dll", SearchOption.AllDirectories);
var asmFile = assemblyFiles.FirstOrDefault(fn => string.Compare(Path.GetFileName(fn), asmName, true) == 0);
if (string.IsNullOrEmpty(asmFile))
return null;
return Assembly.LoadFile(asmFile);
}
}
(Usings for the above: System.IO, System.Reflection, System.Linq)
Call Init with the base path to your plugins folder. When you try to reference an assembly that isn't loaded yet it will search for the first file that matches the base name of the assembly with dll appended. For instance, the NAudio assembly will match the first file named NAudio.dll. It will then load and return the assembly.
No checking is done in the above code on the version, etc. and no preference is given to the current plugin's folder.
I have a DLL file that contains an ActiveX control that I need to register it programmatically by code. here is the code I am using to register that DLL file but it keeps giving me "The system cannot find the file specified" when calling the Start method, And I do not know why regsvrce.exe is not found, should I change current directory or something, please help.
public static void registerDLL(string dllPath)
{
try
{
//'/s' : indicates regsvr32.exe to run silently.
string fileinfo = "\"" + dllPath + "\"";
Process reg = new Process();
reg.StartInfo.FileName = "regsvrce.exe";
reg.StartInfo.Arguments = fileinfo;
reg.StartInfo.UseShellExecute = false;
reg.Start();
reg.WaitForExit();
reg.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Windows CE does not understand or support relative paths. So first, you have to ensure regsvrce.exe exists on the platform (it's not a given that it will, in fact it's pretty common for it to not exist) and you must fully qualify the path to it:
reg.StartInfo.FileName = #"\Windows\regsvrce.exe";
If it doesn't exist (or even if it does) you could easily do the same thing regsvrce.exe does, which is to call DllRegisterServer by simply P/Invoking LoadLibrary and calling the method directly.