using System; using System.IO; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; using System.Threading; namespace GpuFanControl.MsiAfterburnerWrap { using ShareMemoryStructs; namespace ShareMemoryStructs { [Serializable] public struct MACM_SHARED_MEMORY_HEADER { public uint signature; public uint version; public uint headerSize; public uint gpuEntryCount; public uint gpuEntrySize; public uint masterGpu; public MACM_SHARED_MEMORY_FLAG flags; public uint time; public MACM_SHARED_MEMORY_COMMAND command; } [Flags] public enum MACM_SHARED_MEMORY_FLAG : uint { None = 0, LINK = 1, SYNC = 2, THERMAL = 4, } [Flags] public enum MACM_SHARED_MEMORY_COMMAND : uint { None = 0, INIT = 11206656, // 0x00AB0000 FLUSH = 11206657, // 0x00AB0001 } [Serializable] public struct MACM_SHARED_MEMORY_GPU_ENTRY { public MACM_SHARED_MEMORY_GPU_ENTRY_FLAG flags; public uint coreClockCur; public uint coreClockMin; public uint coreClockMax; public uint coreClockDef; public uint shaderClockCur; public uint shaderClockMin; public uint shaderClockMax; public uint shaderClockDef; public uint memoryClockCur; public uint memoryClockMin; public uint memoryClockMax; public uint memoryClockDef; public uint fanSpeedCur; public MACM_SHARED_MEMORY_GPU_ENTRY_FAN_FLAG fanFlagsCur; public uint fanSpeedMin; public uint fanSpeedMax; public uint fanSpeedDef; public MACM_SHARED_MEMORY_GPU_ENTRY_FAN_FLAG fanFlagsDef; public uint coreVoltageCur; public uint coreVoltageMin; public uint coreVoltageMax; public uint coreVoltageDef; public uint memoryVoltageCur; public uint memoryVoltageMin; public uint memoryVoltageMax; public uint memoryVoltageDef; public uint auxVoltageCur; public uint auxVoltageMin; public uint auxVoltageMax; public uint auxVoltageDef; public int coreVoltageBoostCur; public int coreVoltageBoostMin; public int coreVoltageBoostMax; public int coreVoltageBoostDef; public int memoryVoltageBoostCur; public int memoryVoltageBoostMin; public int memoryVoltageBoostMax; public int memoryVoltageBoostDef; public int auxVoltageBoostCur; public int auxVoltageBoostMin; public int auxVoltageBoostMax; public int auxVoltageBoostDef; public int powerLimitCur; public int powerLimitMin; public int powerLimitMax; public int powerLimitDef; public int coreClockBoostCur; public int coreClockBoostMin; public int coreClockBoostMax; public int coreClockBoostDef; public int memoryClockBoostCur; public int memoryClockBoostMin; public int memoryClockBoostMax; public int memoryClockBoostDef; public int thermalLimitCur; public int thermalLimitMin; public int thermalLimitMax; public int thermalLimitDef; public uint thermalPrioritizeCur; public uint thermalPrioritizeDef; } [Flags] public enum MACM_SHARED_MEMORY_GPU_ENTRY_FLAG : uint { None = 0, CORE_CLOCK = 1, SHADER_CLOCK = 2, MEMORY_CLOCK = 4, FAN_SPEED = 8, CORE_VOLTAGE = 16, // 0x00000010 MEMORY_VOLTAGE = 32, // 0x00000020 AUX_VOLTAGE = 64, // 0x00000040 CORE_VOLTAGE_BOOST = 128, // 0x00000080 MEMORY_VOLTAGE_BOOST = 256, // 0x00000100 AUX_VOLTAGE_BOOST = 512, // 0x00000200 POWER_LIMIT = 1024, // 0x00000400 CORE_CLOCK_BOOST = 2048, // 0x00000800 MEMORY_CLOCK_BOOST = 4096, // 0x00001000 THERMAL_LIMIT = 8192, // 0x00002000 THERMAL_PRIORITIZE = 16384, // 0x00004000 SYNCHRONIZED_WITH_MASTER = 2147483648, // 0x80000000 } [Flags] public enum MACM_SHARED_MEMORY_GPU_ENTRY_FAN_FLAG : uint { None = 0, AUTO = 1, } } internal class MsiAfterBurnControlWrap { //const private const string ShareMemoryName = "MACMSharedMemory"; private const string MutexName = "Global\\Access_MACMSharedMemory"; private const uint HeaderSignature = ('M' << 24 | ('A' << 16) | ('C' << 8) | ('M')); private MemoryMappedFile _hMapFile; private MemoryMappedViewStream _pMapAddr; private byte[] _bufSnap; public void Connect() { //connect , failure throws FileNotFoundException _hMapFile = MemoryMappedFile.OpenExisting(ShareMemoryName, MemoryMappedFileRights.FullControl); _pMapAddr = _hMapFile.CreateViewStream(); //var x_pMapAddr = _hMapFile.CreateViewAccessor(); } public void Disconnect() { _pMapAddr?.Close(); _hMapFile?.Dispose(); } public void Refresh() { using (var m = new Mutex(false, MutexName)) { m.WaitOne(); var lenHeader = Marshal.SizeOf(typeof(MACM_SHARED_MEMORY_HEADER)); var bufHeader = new byte[lenHeader]; _pMapAddr.Seek(0, SeekOrigin.Begin); _pMapAddr.Read(bufHeader, 0, lenHeader); var stHeader = ByteArrayToStruct(bufHeader); if (stHeader.signature == HeaderSignature) { var lenSnap = stHeader.headerSize + stHeader.gpuEntryCount * stHeader.gpuEntrySize; var bufSnap = new byte[lenSnap]; _pMapAddr.Seek(0, SeekOrigin.Begin); _pMapAddr.Read(bufSnap, 0, (int)lenSnap); _bufSnap = bufSnap; } m.ReleaseMutex(); } } public void Commit() { using (var m = new Mutex(false, MutexName)) { m.WaitOne(); _pMapAddr.Seek(0, SeekOrigin.Begin); _pMapAddr.Write(_bufSnap, 0, _bufSnap.Length); _pMapAddr.Flush(); _pMapAddr.Seek(0, SeekOrigin.Begin); var header = ByteArrayToStruct(_bufSnap); header.command = MACM_SHARED_MEMORY_COMMAND.FLUSH; StructToByteArray(header, _bufSnap); _pMapAddr.Seek(0, SeekOrigin.Begin); _pMapAddr.Write(_bufSnap, 0, _bufSnap.Length); _pMapAddr.Flush(); m.ReleaseMutex(); } Refresh(); } public int GetNumberOfGpu() { var st = ByteArrayToStruct(_bufSnap); return (int)st.gpuEntryCount; } public MACM_SHARED_MEMORY_GPU_ENTRY? GetGpuEntry(int index, out string error) { var header = ByteArrayToStruct(_bufSnap); if (index > header.gpuEntryCount || index < 0) { error = "index out of range"; return null; } var entry = ByteArrayToStruct(_bufSnap, (int)header.headerSize + index * (int)header.gpuEntrySize); error = null; return entry; } public string SetGpuEntry(MACM_SHARED_MEMORY_GPU_ENTRY entry, int index, bool flush = false) { var header = ByteArrayToStruct(_bufSnap); if (index > header.gpuEntryCount || index < 0) return "index out of range"; StructToByteArray(entry, _bufSnap, (int)(header.headerSize + index * header.gpuEntrySize)); if (flush) Commit(); return null; } private T ByteArrayToStruct(byte[] bytes, int offset = 0) { GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); T stuff; try { stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject() + offset, typeof(T)); } finally { handle.Free(); } return stuff; } private static void StructToByteArray(T str, byte[] snap, int offset = 0) { var h = default(GCHandle); try { h = GCHandle.Alloc(snap, GCHandleType.Pinned); Marshal.StructureToPtr(str, h.AddrOfPinnedObject() + offset, false); } finally { if (h.IsAllocated) { h.Free(); } } } } internal class TryingSandBox { public static void Go() { var ctl = new MsiAfterBurnControlWrap(); ctl.Connect(); ctl.Refresh(); var numberOfGpu = ctl.GetNumberOfGpu(); for (int i = 0; i < numberOfGpu; i++) { Console.WriteLine($"Get entry of GPU #{i}"); MACM_SHARED_MEMORY_GPU_ENTRY? g; { g = ctl.GetGpuEntry(i, out string err); if (g.HasValue == false) { Console.WriteLine($"Err:{err}"); continue; } } var entry = g.Value; if (entry.flags.HasFlag(MACM_SHARED_MEMORY_GPU_ENTRY_FLAG.FAN_SPEED)) { Console.WriteLine($"Updating fan speed of GPU #{i}"); //entry.fanFlagsCur = MACM_SHARED_MEMORY_GPU_ENTRY_FAN_FLAG.AUTO; entry.fanFlagsCur = MACM_SHARED_MEMORY_GPU_ENTRY_FAN_FLAG.None; entry.fanSpeedCur = entry.fanSpeedMax; var err=ctl.SetGpuEntry(entry, i); if(err!=null) Console.WriteLine($"Err: {err}"); } else { Console.WriteLine($"Err: No support fan speed"); } } Console.WriteLine("Commiting..."); ctl.Commit(); Console.WriteLine("Success"); ctl.Disconnect(); } } }