Using PerformanceCounters to target specific drives - c#

Guys, I have the following code:
using System.Diagnostics;
private PerformanceCounter diskRead = new PerformanceCounter();
private PerformanceCounter diskWrite = new PerformanceCounter();
diskRead.CategoryName = "PhysicalDisk";
diskRead.CounterName = "Disk Reads/sec";
diskRead.InstanceName = "_Total";
diskWrite.CategoryName = "PhysicalDisk";
diskWrite.CounterName = "Disk Writes/sec";
diskWrite.InstanceName = "_Total";
This code keeps track of Disk Reads per second and Disk Writes per second and it works fine. My question is, how do I keep track of reads and writes of one specific drive? I have 3 hard drives in my computer and right now its returning a total of all 3 drives combined. How can I specifically chose which drive I want to monitor?

You should replace "_Total" with the appropriate drive number:
diskRead.InstanceName = "0";
Should've checked that. You need to specify the name like "0 C: D:". Yikes.
Edit 2:
You can get the names with
var cat = new System.Diagnostics.PerformanceCounterCategory("PhysicalDisk");
var instNames = cat.GetInstanceNames();
And it is probaly safe to filter out the names that start with a number. (_Total is also in the list).

Use a specific InstanceName, not _Total. Use Perfmon.exe to find the instance names.

Related

IconPath not exsist

When I'm searching for a IsSystemSoundsSession I can't find the IconPath provided. On my system (Windows 10 1909). I'm getting IconPath #%SystemRoot%\\System32\\AudioSrv.Dll,-203 This file doesn't exist. (yes I expanded the variable for this, but that resolves to #C:\Windows\System32\AudioSrv.dll (notice the '#').
Found a solution. The file that I'm searching for is located in C:\Windows\System32\AudioSrv.dll. Notice how there is an # part as first character? It looks like that is the problem. Not sure if this is a problem with NAudio or Windows API.
Code to retrieve the IconPath
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
foreach (var device in devices)
{
var sessions = device.AudioSessionManager.Sessions;
for (int i = 0; i < sessions.Count; i++)
{
var session = sessions[i];
var iconPath = session.IconPath;
// iconPath == '#%SystemRoot%\\System32\\AudioSrv.Dll,-203'
}
}
}
You could hardcode c:\windows\system32 instead, but actually you don't really know that location. That is where the environment variable %systemroot% comes in.
Your # string is a path name containing this environment variable. You cannot open it as a file, first the variable need be resolved, consider:
string trueIconPath = Environment.ExpandEnvironmentVariables( iconPath.Substring(1))
The outcome will probably be: c:\windows\system32\AudioSrv.dll as intended for your machine.. on my PC it would become f:\cfg1\Windows\System32\AudioSrv.dll
Refer for more info: https://learn.microsoft.com/en-us/dotnet/api/system.environment.expandenvironmentvariables?view=netcore-3.1

SharpDX XAudio2: 6 SourceVoice limit

I have been playing around with SharpDX.XAudio2 for a few days now, and while things have been largely positive (the odd software quirk here and there) the following problem has me completely stuck:
I am working in C# .NET using VS2015.
I am trying to play multiple sounds simultaneously.
To do this, I have made:
- Test.cs: Contains main method
- cSoundEngine.cs: Holds XAudio2, MasteringVoice, and sound management methods.
- VoiceChannel.cs: Holds a SourceVoice, and in future any sfx/ related data.
cSoundEngine:
List<VoiceChannel> sourceVoices;
XAudio2 engine;
MasteringVoice master;
public cSoundEngine()
{
engine = new XAudio2();
master = new MasteringVoice(engine);
sourceVoices = new List<VoiceChannel>();
}
public VoiceChannel AddAndPlaySFX(string filepath, double vol, float pan)
{
/**
* Set up and start SourceVoice
*/
NativeFileStream fileStream = new NativeFileStream(filepath, NativeFileMode.Open, NativeFileAccess.Read);
SoundStream soundStream = new SoundStream(fileStream);
SourceVoice source = new SourceVoice(engine, soundStream.Format);
AudioBuffer audioBuffer = new AudioBuffer()
{
Stream = soundStream.ToDataStream(),
AudioBytes = (int)soundStream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
//Make voice wrapper
VoiceChannel voice = new VoiceChannel(source);
sourceVoices.Add(voice);
//Volume
source.SetVolume((float)vol);
//Play sound
source.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
source.Start();
return voice;
}
Test.cs:
cSoundEngine engine = new cSoundEngine();
total = 6;
for (int i = 0; i < total; i++)
{
string filepath = System.IO.Directory.GetParent(System.IO.Directory.GetCurrentDirectory()).Parent.FullName + #"\Assets\Planet.wav";
VoiceChannel sfx = engine.AddAndPlaySFX(filepath, 0.1, 0);
}
Console.Read(); //Input anything to end play.
There is currently nothing worth showing in VoiceChannel.cs - it holds 'SourceVoice source' which is the one parameter sent in the constructor!
Everything is fine and well running with up to 5 sounds (total = 5). All you hear is the blissful drone of Planet.wav. Any higher than 5 however causes the console to freeze for ~5 seconds, then close (likely a c++ error which debugger can't handle). Sadly no error message for us to look at or anything.
From testing:
- Will not crash as long as you do not have more than 5 running sourcevoices.
- Changing sample rate does not seem to help.
- Setting inputChannels for master object to a different number makes no difference.
- MasteringVoice seems to say the max number of inputvoices is 64.
- Making each sfx play from a different wav file makes no difference.
- Setting the volume for sourcevoices and/or master makes no difference.
From the XAudio2 API Documentation I found this quote: 'XAudio2 removes the 6-channel limit on multichannel sounds, and supports multichannel audio on any multichannel-capable audio card. The card does not need to be hardware-accelerated.'. This is the closest I have come to finding something that mentions this problem.
I am not well experienced with programming sfx and a lot of this is very new to me, so feel free to call me an idiot where appropriate but please try and explain things in layman terms.
Please, if you have any ideas or answers they would be greatly appreciated!
-Josh
As Chuck has suggested, I have created a databank which holds the .wav data, and I just reference the single data store with each buffer. This has improved the sound limit up to 20 - however this has not fixed the problem as a whole, likely because I have not implemented this properly.
Implementation:
class SoundDataBank
{
/**
* Holds a single byte array for each sound
*/
Dictionary<eSFX, Byte[]> bank;
string curdir => Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;
public SoundDataBank()
{
bank = new Dictionary<eSFX, byte[]>();
bank.Add(eSFX.planet, NativeFile.ReadAllBytes(curdir + #"\Assets\Planet.wav"));
bank.Add(eSFX.base1, NativeFile.ReadAllBytes(curdir + #"\Assets\Base.wav"));
}
public Byte[] GetSoundData(eSFX sfx)
{
byte[] output = bank[sfx];
return output;
}
}
In SoundEngine we create a SoundBank object (initialised in SoundEngine constructor):
SoundDataBank soundBank;
public VoiceChannel AddAndPlaySFXFromStore(eSFX sfx, double vol)
{
/**
* sourcevoice will be automatically added to MasteringVoice and engine in the constructor.
*/
byte[] buffer = soundBank.GetSoundData(sfx);
MemoryStream memoryStream = new MemoryStream(buffer);
SoundStream soundStream = new SoundStream(memoryStream);
SourceVoice source = new SourceVoice(engine, soundStream.Format);
AudioBuffer audioBuffer = new AudioBuffer()
{
Stream = soundStream.ToDataStream(),
AudioBytes = (int)soundStream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
//Make voice wrapper
VoiceChannel voice = new VoiceChannel(source, engine, MakeOutputMatrix());
//Volume
source.SetVolume((float)vol);
//Play sound
source.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
source.Start();
sourceVoices.Add(voice);
return voice;
}
Following this implementation now lets me play up to 20 sound effects - but NOT because we are playing from the soundbank. Infact, even running the old method for sound effects now gets up to 20 sfx instances.
This has improved up to 20 because we have done NativeFile.ReadAllBytes(curdir + #"\Assets\Base.wav") in the constructor for the SoundBank.
I suspect NativeFile is holding a store of loaded file data, so you regardless of whether you run the original SoundEngine.AddAndPlaySFX() or SoundEngine.AddAndPlaySFXFromStore(), they are both running from memory?
Either way, this has quadrupled the limit from before, so this has been incredibly useful - but requires further work.

trouble displaying system diagnostic information in C# console app

I'm trying to display some system diagnostic information in a console app so once I know it is displayed I can send it SMTP email.
when I call this all it displays is
system.diagnoistics.performancecounter
system.diagnoistics.performancecounter
public static void GetUsageInformation()
{
cpu = new PerformanceCounter();
cpu.CategoryName = "Processor";
cpu.CounterName = "% Processor Time";
cpu.InstanceName = "_Total";
ram = new PerformanceCounter("Memory", "Available Mbytes");
Console.WriteLine(cpu);
Console.WriteLine(ram);
}
Can you provide some assistance with that I'm doing incorrectly here? I'm sure it's super simple like everything else I've run into the last few days.
What is happening here is Console.WriteLine is displaying the string representation of your PerformanceCounter objects, obtained by Console.WriteLine() calling ctr.ToString() internally, which is indeed System.Diagnostics.PerformanceCounter. What I believe you want is the string representation of the Properties of your PerformanceCounter classes.
You can either WriteLine the properties directly, ala...
Console.WriteLine(cpu.CategoryName);
Console.WriteLine(cpu.CounterName);
// etc...
Or use reflection. This will get you started...
PropertyInfo[] properties = ctr.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
Console.Write(property.Name + ":\t");
Console.WriteLine(property.GetValue(ctr).ToString());
}

How to set a dynamic number of threadCounter variables?

I'm not really into multithreading so probably the question is stupid but it seems I cannot find a way to solve this problem (especially because I'm using C# and I've been using it for a month).
I have a dynamic number of directories (I got it from a query in the DB). Inside those queries there are a certain amount of files.
For each directory I need to use a method to transfer these files using FTP in a cuncurrent way because I have basically no limit in FTP max connections (not my word, it's written in the specifics).
But I still need to control the max amount of files transfered per directory. So I need to count the files I'm transfering (increment/decrement).
How could I do it? Should I use something like an array and use the Monitor class?
Edit: Framework 3.5
You can use the Semaphore class to throttle the number of concurrent files per directory. You would probably want to have one semaphore per directory so that the number of FTP uploads per directory can be controlled independently.
public class Example
{
public void ProcessAllFilesAsync()
{
var semaphores = new Dictionary<string, Semaphore>();
foreach (string filePath in GetFiles())
{
string filePathCapture = filePath; // Needed to perform the closure correctly.
string directoryPath = Path.GetDirectoryName(filePath);
if (!semaphores.ContainsKey(directoryPath))
{
int allowed = NUM_OF_CONCURRENT_OPERATIONS;
semaphores.Add(directoryPath, new Semaphore(allowed, allowed));
}
var semaphore = semaphores[directoryPath];
ThreadPool.QueueUserWorkItem(
(state) =>
{
semaphore.WaitOne();
try
{
DoFtpOperation(filePathCapture);
}
finally
{
semaphore.Release();
}
}, null);
}
}
}
var allDirectories = db.GetAllDirectories();
foreach(var directoryPath in allDirectories)
{
DirectoryInfo directories = new DirectoryInfo(directoryPath);
//Loop through every file in that Directory
foreach(var fileInDir in directories.GetFiles()) {
//Check if we have reached our max limit
if (numberFTPConnections == MAXFTPCONNECTIONS){
Thread.Sleep(1000);
}
//code to copy to FTP
//This can be Aync, when then transfer is completed
//decrement the numberFTPConnections so then next file can be transfered.
}
}
You can try something along the lines above. Note that It's just the basic logic and there are proberly better ways to do this.

Reading and writing dates to the registry for trial version purposes

Here's what I want to do, I want to store the date the first time the program is installed and also store a date when was the program was last run. I want the code to check to see if it was more than 30 days since the installation so I can turn off features. I also want to check if the system date is less than the last opened date and if so write the installed date to 1/1/1901 to prevent the program from running.
Keeping in mind that this is not a consumer program but a business program I don't expect hackers to crack it, they may do but that is fine I simply want to give potential customers a reason to consider purchasing the program and the end of the trial will prompt this.
Q1: Does this sound reasonable?
Q2: How should I hide the fact these are dates so it's not easily identified and changed?
Many thanks
Lee
The Microsoft.Win32 namespace is what you need. You will want to look at the two following classes: Registry and RegistryKey.
You could store the hash code of your date within the registry key you will use.
Except that I would neither place it in the registry. The AppData folder is a better place, in addition to your local installation folder. Perhaps will you want to to use binaries with the System.IO namespace so that you can write binary data. The BinaryWriter and BinaryReader classes are probably what you will need to do this.
I would suggest the hidden common application data directory instead of the registry. And write the dates in binary format:
static string appDataFile;
static void Main(string[] args)
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
appDataPath = System.IO.Path.Combine(appDataPath, "MyApplication");
if (!System.IO.Directory.Exists(appDataPath))
System.IO.Directory.CreateDirectory(appDataPath);
appDataFile = System.IO.Path.Combine(appDataPath, "History.dat");
DateTime[] dates;
if (System.IO.File.Exists(appDataFile))
dates = ReadDates();
else
dates = new DateTime[] {DateTime.Now, DateTime.Now};
Console.WriteLine("First: {0}\r\nLast: {1}", dates[0], dates[1]);
dates[1] = DateTime.Now;
WriteDates(dates);
}
static DateTime[] ReadDates()
{
System.IO.FileStream appData = new System.IO.FileStream(
appDataFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
List<DateTime> result = new List<DateTime>();
using (System.IO.BinaryReader br = new System.IO.BinaryReader(appData))
{
while (br.PeekChar() > 0)
{
result.Add(new DateTime(br.ReadInt64()));
}
br.Close();
}
return result.ToArray();
}
static void WriteDates(IEnumerable<DateTime> dates)
{
System.IO.FileStream appData = new System.IO.FileStream(
appDataFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
List<DateTime> result = new List<DateTime>();
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(appData))
{
foreach(DateTime date in dates)
bw.Write(date.Ticks);
bw.Close();
}
}
I would not store this in the registry because it's really easy to change (at least in the places you can write). I would write it in the Local Data folder in a little file and encrypt it. Probably store it in a couple of places in case someone deletes the file.

Categories

Resources