Tuesday, September 22, 2009

Reading SpeedFan shared memory with C#

Building on what I spoke about in my previous post, lets say we want to access the data that SpeedFan provides from a C# application. As a small aside, reading information from the SMBus and other low level interfaces can only be done from the kernel. So applications like SpeedFan (HWMonitor, Everest, etc etc) generally run a driver at kernel level and then a front-end GUI to present the information.

In the case of SpeedFan, shared memory (actually its technically a memory mapped file on Windows I think) is used to communicate between the kernel driver and the userspace GUI application. Even better, the format of this file has been made public by the author of SpeedFan. So, enough talk, lets see some code!

First, we are going to need to access some Windows API functions that are not available via .net:

public const int PROCESS_ALL_ACCESS = 0x1F0FFF;
public const int FILE_MAP_READ = 0x0004;

[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr OpenFileMapping(int dwDesiredAccess,
bool bInheritHandle, StringBuilder lpName);

[DllImport("Kernel32.dll")]
internal static extern IntPtr MapViewOfFile(IntPtr hFileMapping,
int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow,
int dwNumberOfBytesToMap);

[DllImport("Kernel32.dll")]
internal static extern bool UnmapViewOfFile(IntPtr map);

[DllImport("kernel32.dll")]
internal static extern bool CloseHandle(IntPtr hObject);


No dramas there.

Now lets do the dirty and actually access the file:


StringBuilder sharedMemFile = new StringBuilder("SFSharedMemory_ALM");
IntPtr handle = OpenFileMapping(FILE_MAP_READ, false, sharedMemFile);
SpeedFanSharedMem sm;
IntPtr mem = MapViewOfFile(handle , FILE_MAP_READ, 0, 0, Marshal.SizeOf((Type)typeof(SpeedFanSharedMem)));
if (mem == IntPtr.Zero)
{
throw new Exception("Unable to read shared memory.");
}

sm = (SpeedFanSharedMem) Marshal.PtrToStructure(mem, typeof(SpeedFanSharedMem));
UnmapViewOfFile(handle);
CloseHandle(handle);


So there is a bit in there, lets break it down. First, we need to know the named of the file that has we are accessing. In this case its "SFSharedMemory_ALM". Next thing
to note is that we have a structure called 'SpeedFanSharedMem' which is specific to the file in question. So not only do you need to know the file name, but you also need to know the structure that resides there. Assuming you do, its pretty straight forward. Don't forget to tidy up once you are done!


The SpeedFan specific structure looks like this:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private class SpeedFanSharedMem
{
ushort version;
ushort flags;
Int32 size;
Int32 handle;
ushort numTemps;
ushort numFans;
ushort numVolts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] temps;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] fans;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] volts;

public SpeedFanSharedMem()
{
temps = new Int32[32];
fans = new Int32[32];
volts = new Int32[32];
}
}


Things to note here are the attribute at the start, where we define the structure layout in memory, and the way we define the arrays with their appropriate attributes.
Nothing too tricky, just ensuring they get marshalled correctly.

That's it! Well nearly. Don't forget you need appropriate permissions to access the file (in this case, normal user permissions should be fine as we are only opening the mapping for reading).

8 comments:

  1. Hello! I really liked reading your post. :) Would you mind postin the project with source or mail it to me? I'd really like to look through the code!

    ReplyDelete
  2. This is literally all the code I have written for accessing SpeedFan data so far. If you paste code section 1 and 3 into a class, and then section 2 into a member function.... and call that function from Form1_Load() you will have the entire SpeedFan part of the project.

    I am planning on doing more than this, and will post when its more useful, so stay tuned.

    ReplyDelete
  3. hi did you ever finish this? so taht it dumps the memory to a file?

    thanks, i would email you but couldn't find address.

    ReplyDelete
  4. I don't really remember to be honest. However, all that needs to be done is to call this every second and write the output to file.

    ReplyDelete
  5. If you look at the post on GPU-Z it has a link to some code which is very similar.

    ReplyDelete
  6. yeah sorry but im almost complete noob to C# as im just using this for an arduino for temp monitoring, so i dont know how to do either of those really. could you maybe do it in like 5minutes since your a pro :) ? at this time ive barely gotten past the hello world tutorials.

    if you do could you add it to: http://bluesplat.tech.googlepages.com/SpeedFan.cs and re upload or something?

    if not could you tell me where i could find this info to attempt on my own?

    i was surprised you responded after so many years.

    also that file i linked from your other post isnt C# console compatible because it doesnt have a void main()

    Thanks alot!!

    ReplyDelete
  7. i have tried googling for the saving memory to file but i cant find anything , its out there just dunno where...

    ReplyDelete
  8. Check my latest post. Note that I do not have speedfan installed currently, so I really don't know what that is going to do :)

    ReplyDelete