I need to convert a C# windows service (.NET Framework 4.6.1) into a console application. So this application doesn't have a real interactive interface.
In the windows service application I have the OnStop() method to do the things I need before terminate... and exit.
Of course, I can create a file with a well-known name and in the console application periodically check for this file, but it seems to me an old style solution.
Is there a “best practice” to ask a console application to terminate gracefully having the time to complete the current processing?
So the solution I found has the following code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
private static bool keepRunning = true;
public static void Main(string[] args)
{
Console.WriteLine("Main started");
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) {
e.Cancel = true;
keepRunning = false;
};
while (keepRunning)
{
Console.WriteLine("Doing really evil things...");
System.Threading.Thread.Sleep(3000);
}
Console.WriteLine("Exited gracefully");
}
}
}
I have been trying to diagnose a memory leak in a service which only appears on Windows 7/Server 2008 R2. I narrowed it down to where we are using Microsoft.Web.Administration.ServerManager to gather info about the apps in our site. I whittled it down to the console app below, which exhibits the same behavior. It might still be more complex than it needs to be, but I wanted to emulate the behavior of the service as much as possible.
I found a previous question here that was very similar and made the changes suggested in the answers. This appeared to reduce the rate of growth, but it still leaks significantly (under the comments "Original Test" I have commented out code that I changed based on those answers. the "Modified Test" comments indicate the changes I made. I didn't initially have the GC.Collect call in, and when I ran this on a Windows 10 system, it grew for quite some time before the garbage collection kicked in. With the GC.Collect call in place, it ran without growing on Win 10, but on Win 7 it made no difference.
I ran it under a profiler that indicated the memory being leaked was native, and that the leak was coming from nativerd.dll.
Has anyone encountered a problem like this? I'm new to C# and am still learning how Garbage Collection works, so I'm wondering if there is something I'm doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Web.Administration;
namespace ServerManagerLeakTest
{
class Program
{
static void Main(string[] args)
{
Console.Write("Working.");
var me = new MyClass();
me.Run();
}
}
internal class MyClass
{
ServerManagerWrapper _smw = new ServerManagerWrapper();
public void Run()
{
while (true)
{
var t = Task.Run(async delegate
{
DoWork();
await Task.Delay(1000);
});
try
{
t.Wait();
}
catch (Exception e)
{
Console.Write("Main Exception: " + e.Message);
}
Console.Write(".");
}
}
public void DoWork()
{
try
{
var data = _smw.GetWebApps().ToList();
data.Clear();
}
catch (Exception e)
{
Console.Write("DoWork Exception: " + e.Message);
}
}
}
internal class ServerManagerWrapper
{
public List<int> GetWebApps()
{
List<int> result = new List<int>() { };
// Original Test
//
// using (var serverManager = new ServerManager())
// {
// foreach (var site in serverManager.Sites)
// {
// result.AddRange(GetWebApps(site));
// }
//}
// Modified Test
var serverManager = new ServerManager();
foreach (var site in serverManager.Sites)
{
result.AddRange(GetWebApps(site));
}
serverManager.Dispose();
serverManager = null;
System.GC.Collect();
return result;
}
private IEnumerable<int> GetWebApps(Site site)
{
// Original Test
//
//for (var application in site.Applications)
//{
// yield return application.GetHashCode();
//}
// Modified Test
List<int> result = new List<int>() { };
for (int i = 0; i < site.Applications.Count; i++)
{
result.Add(site.Applications[i].GetHashCode());
}
return result;
}
}
}
Answer provided in comments from #Lex Li.
Move the check to a separate process. Calling IIS REST API, PowerShell, or even appcmd and parse the result. Let the leak be out of your own service.
I'm writing a library in C# that allows me to convert HTML to PDF. Obviously the idea is that it is cross-platform and why I'm doing with mono. To do this I have to load Seller fonts with System.Drawing.Text.PrivateFontCollection class.
When the application finishes executing all the code, the application unexpectedly quits. After many tests, I realized that the problem is when it is called the Dispose method System.Drawing.Text.PrivateFontCollection or when Dispose() of System.Drawing.FontFamily is called.
This problem is in Windows (I have Windows 7 32 bit), in linux I have no problem.
This is the test code
using System;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
namespace FORM
{
class MainClass
{
public static void Main (string[] args)
{
PrivateFontCollection pf = new PrivateFontCollection ();
IntPtr fontBuffer = IntPtr.Zero;
pf.AddFontFile ("C:\\Users\\James\\Downloads\\open sans\\open-sans.regular.ttf");
Font f = new Font (pf.Families[0],12,FontStyle.Regular);
try {
pf.Dispose ();
}
catch{
}
pf = null;
Console.WriteLine ("Hello World!");
Console.ReadLine ();
//pf.Dispose ();
}
}
}
Are you always calling Dispose?
You need to always call Dispose when using unmanaged resources.
Another way of calling Dispose is with the using keyword...
Example (before running this, do a restart on your pc to make sure all resources have been freed):
using System;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
namespace FORM
{
class MainClass
{
public static void Main (string[] args)
{
using (PrivateFontCollection pf = new PrivateFontCollection())
{
IntPtr fontBuffer = IntPtr.Zero;
pf.AddFontFile("C:\\Windows\\Fonts\\times.ttf");
Font f = new Font(pf.Families[0], 12, FontStyle.Regular);
}
Console.WriteLine("Hello World!");
Console.ReadLine();
}
}
}
So I'm just trying to explore the intricacies of C# and I wanted to make a simple program that would just kill a process. Yes I know, that is what Task Manager is for but this is supposed to be a learning experience, this is what I have so far.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace endProcess
{
class Program
{
private Process GetaProcess(string processname)
{
Process[] aProc = Process.GetProcessesByName(processname);
if (aProc.Length > 0)
return aProc[0];
else return null;
}
string selectProcess = "";
static void Main(string[] args)
{
Console.WriteLine("What process do you want to kill?");
selectProcess = Console.ReadLine();
Process myprc = Call GetAProcess(selectProcess);
myprc.Kill();
Console.ReadLine();
}
}
}
The issue comes where I made the comment. It says there should be a semicolon after GetAProcess and I have no idea why. Any help would be much appreciated.
You don't say Call GetaProcess you simply say GetaProcess
The line should look like this : Process myprc = GetaProcess(selectProcess);
I need to gather some system information for the application I'm developing. The memory available and the CPU load are easy to get using C#. Unfortunately, the CPU temperature it's not that easy. I have tried using WMI, but I couldn't get anything using
Win32_TemperatureProbe
or
MSAcpi_ThermalZoneTemperature
How can I do this? I'm wondering how monitoring programs, as SiSoftware Sandra, can get that information...
Here is the code of the class:
public class SystemInformation
{
private System.Diagnostics.PerformanceCounter m_memoryCounter;
private System.Diagnostics.PerformanceCounter m_CPUCounter;
public SystemInformation()
{
m_memoryCounter = new System.Diagnostics.PerformanceCounter();
m_memoryCounter.CategoryName = "Memory";
m_memoryCounter.CounterName = "Available MBytes";
m_CPUCounter = new System.Diagnostics.PerformanceCounter();
m_CPUCounter.CategoryName = "Processor";
m_CPUCounter.CounterName = "% Processor Time";
m_CPUCounter.InstanceName = "_Total";
}
public float GetAvailableMemory()
{
return m_memoryCounter.NextValue();
}
public float GetCPULoad()
{
return m_CPUCounter.NextValue();
}
public float GetCPUTemperature()
{
//...
return 0;
}
}
For others who may come by here, maybe take a look at : http://openhardwaremonitor.org/
Follow that link and at first you might think, "Hey, that's an application, and that is why it was removed. The question was how to do this from C# code, not to find an application that can tell me the temperature..." This is where it shows you are not willing to invest enough time in reading what "Open Hardware Monitor" also is.
They also include a data interface. Here is the description:
Data Interface
The Open Hardware Monitor publishes all sensor data to
WMI (Windows Management Instrumentation). This allows other
applications to read and use the sensor information as well. A
preliminary documentation of the interface can be found here (click).
When you download it, it contains the OpenHardwareMonitor.exe application, and you're not looking for that one. It also contains the OpenHardwareMonitorLib.dll, and you're looking for that one.
It is mostly, if not 100%, just a wrapper around the WinRing0 API, which you could choose to wrap yourself if you feel like it.
I have tried this out from a C# application myself, and it works. Although it was still in beta, it seemed rather stable. It is also open source, so it could be a good starting point instead.
You can indeed read the CPU temperature very easily in C# by using a WMI approach.
To get the temperature in Celsius, I have created a wrapper that converts the value returned by WMI and wraps it into an easy-to-use object.
Please remember to add a reference to the System.Management.dll in Visual Studio.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
namespace RCoding.Common.Diagnostics.SystemInfo
{
public class Temperature
{
public double CurrentValue { get; set; }
public string InstanceName { get; set; }
public static List<Temperature> Temperatures
{
get
{
List<Temperature> result = new List<Temperature>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher(#"root\WMI", "SELECT * FROM MSAcpi_ThermalZoneTemperature");
foreach (ManagementObject obj in searcher.Get())
{
Double temperature = Convert.ToDouble(obj["CurrentTemperature"].ToString());
temperature = (temperature - 2732) / 10.0;
result.Add(new Temperature { CurrentValue = temperature, InstanceName = obj["InstanceName"].ToString() });
}
return result;
}
}
}
}
I'm pretty sure it's manufacturer dependent, since they will be accessed through an I/O port. If you have a specific board you're trying to work with, try looking through the manuals and/or contacting the manufacturer.
If you want to do this for a lot of different boards, I'd recommend contacting someone at something like SiSoftware or be prepared to read a lot of motherboard manuals.
As another note, not all boards have temperature monitors.
You also might run into problems getting privileged access from the kernel.
You can give the Open Hardware Monitor a go, although it lacks support for the latest processors.
internal sealed class CpuTemperatureReader : IDisposable
{
private readonly Computer _computer;
public CpuTemperatureReader()
{
_computer = new Computer { CPUEnabled = true };
_computer.Open();
}
public IReadOnlyDictionary<string, float> GetTemperaturesInCelsius()
{
var coreAndTemperature = new Dictionary<string, float>();
foreach (var hardware in _computer.Hardware)
{
hardware.Update(); //use hardware.Name to get CPU model
foreach (var sensor in hardware.Sensors)
{
if (sensor.SensorType == SensorType.Temperature && sensor.Value.HasValue)
coreAndTemperature.Add(sensor.Name, sensor.Value.Value);
}
}
return coreAndTemperature;
}
public void Dispose()
{
try
{
_computer.Close();
}
catch (Exception)
{
//ignore closing errors
}
}
}
Download the ZIP file from the official source, extract and add a reference to file OpenHardwareMonitorLib.dll in your project.
I extracted the CPU part from Open Hardware Monitor into a separate library, exposing sensors and members normally hidden into OHM.
It also includes many updates (like the support for Ryzen and Xeon) because on Open Hardware Monitor (OHM) they haven't accepted pull requests since 2015.
https://www.nuget.org/packages/HardwareProviders.CPU.Standard/
It's depends on if your computer supports WMI. My computer can't run this WMI demo either.
But I successfully get the CPU temperature via Open Hardware Monitor. Add the Openhardwaremonitor reference in Visual Studio. It's easier. Try this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenHardwareMonitor.Hardware;
namespace Get_CPU_Temp5
{
class Program
{
public class UpdateVisitor : IVisitor
{
public void VisitComputer(IComputer computer)
{
computer.Traverse(this);
}
public void VisitHardware(IHardware hardware)
{
hardware.Update();
foreach (IHardware subHardware in hardware.SubHardware) subHardware.Accept(this);
}
public void VisitSensor(ISensor sensor) { }
public void VisitParameter(IParameter parameter) { }
}
static void GetSystemInfo()
{
UpdateVisitor updateVisitor = new UpdateVisitor();
Computer computer = new Computer();
computer.Open();
computer.CPUEnabled = true;
computer.Accept(updateVisitor);
for (int i = 0; i < computer.Hardware.Length; i++)
{
if (computer.Hardware[i].HardwareType == HardwareType.CPU)
{
for (int j = 0; j < computer.Hardware[i].Sensors.Length; j++)
{
if (computer.Hardware[i].Sensors[j].SensorType == SensorType.Temperature)
Console.WriteLine(computer.Hardware[i].Sensors[j].Name + ":" + computer.Hardware[i].Sensors[j].Value.ToString() + "\r");
}
}
}
computer.Close();
}
static void Main(string[] args)
{
while (true)
{
GetSystemInfo();
}
}
}
}
You need to run this demo as an administrator.
You can see the tutorial in Using Open Hardware Monitor to get CPU temperature in C# and make a fan cooling system.
The mentioned WMI classes were not working for me in the latest version of Windows 10.
On my Dell laptop I could get the CPU temperature in Celsius like this via PowerShell:
$data = Get-WMIObject -Query "SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation" -Namespace "root/CIMV2"
#($data)[0].HighPrecisionTemperature
It can be done in your code via WMI. I've found a tool (WMI Code Creator v1.0) from Microsoft that creates code for it.
The WMI Code Creator tool allows you to generate VBScript, C#, and VB
.NET code that uses WMI to complete a management task such as querying
for management data, executing a method from a WMI class, or receiving
event notifications using WMI.