Improved the implementation for the AMD 10h family CPU clock speeds.

This commit is contained in:
Michael Möller
2010-09-30 16:51:09 +00:00
parent 7232dcc55f
commit cc719ae7e8
6 changed files with 185 additions and 76 deletions

View File

@@ -92,7 +92,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
this, settings); this, settings);
if (hasTSC) if (HasTimeStampCounter)
ActivateSensor(coreClocks[i]); ActivateSensor(coreClocks[i]);
} }
@@ -138,7 +138,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
} }
} }
if (hasTSC) { if (HasTimeStampCounter) {
double newBusClock = 0; double newBusClock = 0;
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
@@ -151,11 +151,12 @@ namespace OpenHardwareMonitor.Hardware.CPU {
// 8-13 hold StartFID, we don't use that here. // 8-13 hold StartFID, we don't use that here.
double curMP = 0.5 * ((eax & 0x3F) + 8); double curMP = 0.5 * ((eax & 0x3F) + 8);
double maxMP = 0.5 * ((eax >> 16 & 0x3F) + 8); double maxMP = 0.5 * ((eax >> 16 & 0x3F) + 8);
coreClocks[i].Value = (float)(curMP * MaxClock / maxMP); coreClocks[i].Value =
newBusClock = (float)(MaxClock / maxMP); (float)(curMP * TimeStampCounterFrequency / maxMP);
newBusClock = (float)(TimeStampCounterFrequency / maxMP);
} else { } else {
// Fail-safe value - if the code above fails, we'll use this instead // Fail-safe value - if the code above fails, we'll use this instead
coreClocks[i].Value = (float)MaxClock; coreClocks[i].Value = (float)TimeStampCounterFrequency;
} }
} }

View File

@@ -36,7 +36,10 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -47,16 +50,22 @@ namespace OpenHardwareMonitor.Hardware.CPU {
private readonly Sensor coreTemperature; private readonly Sensor coreTemperature;
private readonly Sensor[] coreClocks; private readonly Sensor[] coreClocks;
private readonly Sensor busClock; private readonly Sensor busClock;
private const uint COFVID_STATUS = 0xC0010071; private const uint PERF_CTL_0 = 0xC0010000;
private const uint PERF_CTR_0 = 0xC0010004;
private const uint P_STATE_0 = 0xC0010064; private const uint P_STATE_0 = 0xC0010064;
private const uint COFVID_STATUS = 0xC0010071;
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3; private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203; private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4; private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
private readonly uint miscellaneousControlAddress; private readonly uint miscellaneousControlAddress;
private double timeStampCounterMultiplier;
private StringBuilder debug = new StringBuilder();
public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings) public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
: base(processorIndex, cpuid, settings) : base(processorIndex, cpuid, settings)
{ {
@@ -76,15 +85,87 @@ namespace OpenHardwareMonitor.Hardware.CPU {
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
this, settings); this, settings);
if (hasTSC) if (HasTimeStampCounter)
ActivateSensor(coreClocks[i]); ActivateSensor(coreClocks[i]);
} }
// set affinity to the first thread for all frequency estimations
IntPtr thread = NativeMethods.GetCurrentThread();
UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
(UIntPtr)(1L << cpuid[0][0].Thread));
uint ctlEax, ctlEdx;
WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
uint ctrEax, ctrEdx;
WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
// restore the performance counter registers
WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
// restore the thread affinity.
NativeMethods.SetThreadAffinityMask(thread, mask);
Update(); Update();
} }
private double estimateTimeStampCounterMultiplier() {
// preload the function
estimateTimeStampCounterMultiplier(0);
estimateTimeStampCounterMultiplier(0);
// estimate the multiplier
List<double> estimate = new List<double>(3);
for (int i = 0; i < 3; i++)
estimate.Add(estimateTimeStampCounterMultiplier(0.025));
estimate.Sort();
return estimate[1];
}
private double estimateTimeStampCounterMultiplier(double timeWindow) {
uint eax, edx;
// select event "076h CPU Clocks not Halted" and enable the counter
WinRing0.Wrmsr(PERF_CTL_0,
(1 << 22) | // enable performance counter
(1 << 17) | // count events in user mode
(1 << 16) | // count events in operating-system mode
0x76, 0x00000000);
// set the counter to 0
WinRing0.Wrmsr(PERF_CTR_0, 0, 0);
long ticks = (long)(timeWindow * Stopwatch.Frequency);
uint lsbBegin, msbBegin, lsbEnd, msbEnd;
long timeBegin = Stopwatch.GetTimestamp() +
(long)Math.Ceiling(0.001 * ticks);
long timeEnd = timeBegin + ticks;
while (Stopwatch.GetTimestamp() < timeBegin) { }
WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
while (Stopwatch.GetTimestamp() < timeEnd) { }
WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx);
uint cpuDid = (eax >> 6) & 7;
uint cpuFid = eax & 0x1F;
double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
double coreFrequency = 1e-6 *
(((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
(timeEnd - timeBegin);
double busFrequency = coreFrequency / coreMultiplier;
return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
}
protected override uint[] GetMSRs() { protected override uint[] GetMSRs() {
return new uint[] { P_STATE_0, COFVID_STATUS }; return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
} }
public override string GetReport() { public override string GetReport() {
@@ -94,12 +175,18 @@ namespace OpenHardwareMonitor.Hardware.CPU {
r.Append("Miscellaneous Control Address: 0x"); r.Append("Miscellaneous Control Address: 0x");
r.AppendLine((miscellaneousControlAddress).ToString("X", r.AppendLine((miscellaneousControlAddress).ToString("X",
CultureInfo.InvariantCulture)); CultureInfo.InvariantCulture));
r.Append("Time Stamp Counter Multiplier: ");
r.AppendLine(timeStampCounterMultiplier.ToString(
CultureInfo.InvariantCulture));
r.AppendLine();
r.Append(debug);
r.AppendLine(); r.AppendLine();
return r.ToString(); return r.ToString();
} }
private double MultiplierFromIDs(uint divisorID, uint frequencyID) { private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID); return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
} }
@@ -118,35 +205,29 @@ namespace OpenHardwareMonitor.Hardware.CPU {
} }
} }
if (hasTSC) { if (HasTimeStampCounter) {
double newBusClock = 0; double newBusClock = 0;
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
Thread.Sleep(1); Thread.Sleep(1);
uint curEax, curEdx; uint curEax, curEdx;
uint maxEax, maxEdx;
if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx, if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
(UIntPtr)(1L << cpuid[i][0].Thread)) &&
WinRing0.RdmsrTx(P_STATE_0, out maxEax, out maxEdx,
(UIntPtr)(1L << cpuid[i][0].Thread))) (UIntPtr)(1L << cpuid[i][0].Thread)))
{ {
// 8:6 CpuDid: current core divisor ID // 8:6 CpuDid: current core divisor ID
// 5:0 CpuFid: current core frequency ID // 5:0 CpuFid: current core frequency ID
uint curCpuDid = (curEax >> 6) & 7; uint cpuDid = (curEax >> 6) & 7;
uint curCpuFid = curEax & 0x1F; uint cpuFid = curEax & 0x1F;
double multiplier = MultiplierFromIDs(curCpuDid, curCpuFid); double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
// we assume that the max multiplier (used for the TSC) coreClocks[i].Value =
// can be found in the P_STATE_0 MSR (float)(multiplier * TimeStampCounterFrequency /
uint maxCpuDid = (maxEax >> 6) & 7; timeStampCounterMultiplier);
uint maxCpuFid = maxEax & 0x1F; newBusClock =
double maxMultiplier = MultiplierFromIDs(maxCpuDid, maxCpuFid); (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
coreClocks[i].Value = (float)(multiplier * MaxClock / maxMultiplier);
newBusClock = (float)(MaxClock / maxMultiplier);
} else { } else {
coreClocks[i].Value = (float)MaxClock; coreClocks[i].Value = (float)TimeStampCounterFrequency;
} }
} }
@@ -155,6 +236,17 @@ namespace OpenHardwareMonitor.Hardware.CPU {
ActivateSensor(this.busClock); ActivateSensor(this.busClock);
} }
} }
} }
private static class NativeMethods {
private const string KERNEL = "kernel32.dll";
[DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
public static extern UIntPtr
SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
[DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr GetCurrentThread();
}
} }
} }

View File

@@ -55,13 +55,13 @@ namespace OpenHardwareMonitor.Hardware.CPU {
protected readonly int coreCount; protected readonly int coreCount;
protected readonly string name; protected readonly string name;
protected readonly bool hasTSC; private readonly bool hasTimeStampCounter;
protected readonly bool invariantTSC; private readonly bool isInvariantTimeStampCounter;
private readonly double estimatedMaxClock; private readonly double estimatedTimeStampCounterFrequency;
private ulong lastTimeStampCount; private ulong lastTimeStampCount;
private long lastTime; private long lastTime;
private double maxClock; private double timeStampCounterFrequency;
private readonly Vendor vendor; private readonly Vendor vendor;
@@ -89,19 +89,19 @@ namespace OpenHardwareMonitor.Hardware.CPU {
this.coreCount = cpuid.Length; this.coreCount = cpuid.Length;
this.name = cpuid[0][0].Name; this.name = cpuid[0][0].Name;
// check if processor has TSC // check if processor has a TSC
if (cpuid[0][0].Data.GetLength(0) > 1 if (cpuid[0][0].Data.GetLength(0) > 1
&& (cpuid[0][0].Data[1, 3] & 0x10) != 0) && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
hasTSC = true; hasTimeStampCounter = true;
else else
hasTSC = false; hasTimeStampCounter = false;
// check if processor supports invariant TSC // check if processor supports an invariant TSC
if (cpuid[0][0].ExtData.GetLength(0) > 7 if (cpuid[0][0].ExtData.GetLength(0) > 7
&& (cpuid[0][0].ExtData[7, 3] & 0x100) != 0) && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
invariantTSC = true; isInvariantTimeStampCounter = true;
else else
invariantTSC = false; isInvariantTimeStampCounter = false;
if (coreCount > 1) if (coreCount > 1)
totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings); totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
@@ -119,30 +119,30 @@ namespace OpenHardwareMonitor.Hardware.CPU {
ActivateSensor(totalLoad); ActivateSensor(totalLoad);
} }
if (hasTSC) if (hasTimeStampCounter)
estimatedMaxClock = EstimateMaxClock(); estimatedTimeStampCounterFrequency = EstimateTimeStampCounterFrequency();
else else
estimatedMaxClock = 0; estimatedTimeStampCounterFrequency = 0;
maxClock = estimatedMaxClock; timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
lastTimeStampCount = 0; lastTimeStampCount = 0;
lastTime = 0; lastTime = 0;
} }
private static double EstimateMaxClock() { private static double EstimateTimeStampCounterFrequency() {
// preload the function // preload the function
EstimateMaxClock(0); EstimateTimeStampCounterFrequency(0);
EstimateMaxClock(0); EstimateTimeStampCounterFrequency(0);
// estimate the max clock in MHz // estimate the frequency in MHz
List<double> estimatedMaxClocks = new List<double>(3); List<double> estimatedFrequency = new List<double>(3);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025)); estimatedFrequency.Add(1e-6 * EstimateTimeStampCounterFrequency(0.025));
estimatedMaxClocks.Sort(); estimatedFrequency.Sort();
return estimatedMaxClocks[1]; return estimatedFrequency[1];
} }
private static double EstimateMaxClock(double timeWindow) { private static double EstimateTimeStampCounterFrequency(double timeWindow) {
long ticks = (long)(timeWindow * Stopwatch.Frequency); long ticks = (long)(timeWindow * Stopwatch.Frequency);
uint lsbBegin, msbBegin, lsbEnd, msbEnd; uint lsbBegin, msbBegin, lsbEnd, msbEnd;
@@ -195,12 +195,13 @@ namespace OpenHardwareMonitor.Hardware.CPU {
Environment.NewLine); Environment.NewLine);
r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length, r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
Environment.NewLine); Environment.NewLine);
r.AppendLine("TSC: " +
(hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
r.AppendLine(string.Format(CultureInfo.InvariantCulture, r.AppendLine(string.Format(CultureInfo.InvariantCulture,
"Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6)); "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
r.AppendLine(string.Format(CultureInfo.InvariantCulture, r.AppendLine(string.Format(CultureInfo.InvariantCulture,
"Max Clock: {0} MHz", Math.Round(maxClock * 100) * 0.01)); "Time Stamp Counter Frequency: {0} MHz",
Math.Round(timeStampCounterFrequency * 100) * 0.01));
r.AppendLine(); r.AppendLine();
uint[] msrArray = GetMSRs(); uint[] msrArray = GetMSRs();
@@ -239,22 +240,27 @@ namespace OpenHardwareMonitor.Hardware.CPU {
get { return HardwareType.CPU; } get { return HardwareType.CPU; }
} }
protected double MaxClock { public bool HasTimeStampCounter {
get { return maxClock; } get { return hasTimeStampCounter; }
}
public double TimeStampCounterFrequency {
get { return timeStampCounterFrequency; }
} }
public override void Update() { public override void Update() {
if (hasTSC) { if (hasTimeStampCounter) {
uint lsb, msb; uint lsb, msb;
WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1); WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
long time = Stopwatch.GetTimestamp(); long time = Stopwatch.GetTimestamp();
ulong timeStampCount = ((ulong)msb << 32) | lsb; ulong timeStampCount = ((ulong)msb << 32) | lsb;
double delta = ((double)(time - lastTime)) / Stopwatch.Frequency; double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
if (delta > 0.5) { if (delta > 0.5) {
if (invariantTSC) if (isInvariantTimeStampCounter)
maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta); timeStampCounterFrequency =
(timeStampCount - lastTimeStampCount) / (1e6 * delta);
else else
maxClock = estimatedMaxClock; timeStampCounterFrequency = estimatedTimeStampCounterFrequency;
lastTimeStampCount = timeStampCount; lastTimeStampCount = timeStampCount;
lastTime = time; lastTime = time;

View File

@@ -145,7 +145,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
coreClocks[i] = coreClocks[i] =
new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings); new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (hasTSC) if (HasTimeStampCounter)
ActivateSensor(coreClocks[i]); ActivateSensor(coreClocks[i]);
} }
@@ -182,7 +182,7 @@ namespace OpenHardwareMonitor.Hardware.CPU {
} }
} }
if (hasTSC) { if (HasTimeStampCounter) {
double newBusClock = 0; double newBusClock = 0;
uint eax, edx; uint eax, edx;
for (int i = 0; i < coreClocks.Length; i++) { for (int i = 0; i < coreClocks.Length; i++) {
@@ -191,9 +191,10 @@ namespace OpenHardwareMonitor.Hardware.CPU {
(UIntPtr)(1L << cpuid[i][0].Thread))) { (UIntPtr)(1L << cpuid[i][0].Thread))) {
if (maxNehalemMultiplier > 0) { // Core i3, i5, i7 if (maxNehalemMultiplier > 0) { // Core i3, i5, i7
uint nehalemMultiplier = eax & 0xff; uint nehalemMultiplier = eax & 0xff;
coreClocks[i].Value = coreClocks[i].Value =(float)(nehalemMultiplier *
(float)(nehalemMultiplier * MaxClock / maxNehalemMultiplier); TimeStampCounterFrequency / maxNehalemMultiplier);
newBusClock = (float)(MaxClock / maxNehalemMultiplier); newBusClock =
(float)(TimeStampCounterFrequency / maxNehalemMultiplier);
} else { // Core 2 } else { // Core 2
uint multiplier = (eax >> 8) & 0x1f; uint multiplier = (eax >> 8) & 0x1f;
uint maxMultiplier = (edx >> 8) & 0x1f; uint maxMultiplier = (edx >> 8) & 0x1f;
@@ -201,13 +202,15 @@ namespace OpenHardwareMonitor.Hardware.CPU {
uint factor = (multiplier << 1) | ((eax >> 14) & 1); uint factor = (multiplier << 1) | ((eax >> 14) & 1);
uint maxFactor = (maxMultiplier << 1) | ((edx >> 14) & 1); uint maxFactor = (maxMultiplier << 1) | ((edx >> 14) & 1);
if (maxFactor > 0) { if (maxFactor > 0) {
coreClocks[i].Value = (float)(factor * MaxClock / maxFactor); coreClocks[i].Value =
newBusClock = (float)(2 * MaxClock / maxFactor); (float)(factor * TimeStampCounterFrequency / maxFactor);
newBusClock =
(float)(2 * TimeStampCounterFrequency / maxFactor);
} }
} }
} else { // Intel Pentium 4 } else { // Intel Pentium 4
// if IA32_PERF_STATUS is not available, assume maxClock // if IA32_PERF_STATUS is not available, assume TSC frequency
coreClocks[i].Value = (float)MaxClock; coreClocks[i].Value = (float)TimeStampCounterFrequency;
} }
} }
if (newBusClock > 0) { if (newBusClock > 0) {

View File

@@ -90,10 +90,13 @@ namespace OpenHardwareMonitor.Hardware {
public delegate bool ReadPciConfigDwordExDelegate(uint pciAddress, public delegate bool ReadPciConfigDwordExDelegate(uint pciAddress,
uint regAddress, out uint value); uint regAddress, out uint value);
public delegate bool WritePciConfigDwordExDelegate(uint pciAddress, public delegate bool WritePciConfigDwordExDelegate(uint pciAddress,
uint regAddress, uint value); uint regAddress, uint value);
public delegate bool RdtscTxDelegate(out uint eax, out uint edx,
UIntPtr threadAffinityMask);
public delegate bool RdtscDelegate(out uint eax, out uint edx); public delegate bool RdtscDelegate(out uint eax, out uint edx);
public delegate bool RdtscTxDelegate(out uint eax, out uint edx,
UIntPtr threadAffinityMask);
public delegate bool WrmsrDelegate(uint index, uint eax, uint edx);
public delegate bool WrmsrTxDelegate(uint index, uint eax, uint edx,
UIntPtr threadAffinityMask);
private static readonly InitializeOlsDelegate InitializeOls = private static readonly InitializeOlsDelegate InitializeOls =
CreateDelegate<InitializeOlsDelegate>("InitializeOls"); CreateDelegate<InitializeOlsDelegate>("InitializeOls");
@@ -116,10 +119,14 @@ namespace OpenHardwareMonitor.Hardware {
CreateDelegate<ReadPciConfigDwordExDelegate>("ReadPciConfigDwordEx"); CreateDelegate<ReadPciConfigDwordExDelegate>("ReadPciConfigDwordEx");
public static readonly WritePciConfigDwordExDelegate WritePciConfigDwordEx = public static readonly WritePciConfigDwordExDelegate WritePciConfigDwordEx =
CreateDelegate<WritePciConfigDwordExDelegate>("WritePciConfigDwordEx"); CreateDelegate<WritePciConfigDwordExDelegate>("WritePciConfigDwordEx");
public static readonly RdtscTxDelegate RdtscTx =
CreateDelegate<RdtscTxDelegate>("RdtscTx");
public static readonly RdtscDelegate Rdtsc = public static readonly RdtscDelegate Rdtsc =
CreateDelegate<RdtscDelegate>("Rdtsc"); CreateDelegate<RdtscDelegate>("Rdtsc");
public static readonly RdtscTxDelegate RdtscTx =
CreateDelegate<RdtscTxDelegate>("RdtscTx");
public static readonly WrmsrDelegate Wrmsr =
CreateDelegate<WrmsrDelegate>("Wrmsr");
public static readonly WrmsrTxDelegate WrmsrTx =
CreateDelegate<WrmsrTxDelegate>("WrmsrTx");
private static T CreateDelegate<T>(string entryPoint) where T : class { private static T CreateDelegate<T>(string entryPoint) where T : class {
DllImportAttribute attribute = new DllImportAttribute(GetDllName()); DllImportAttribute attribute = new DllImportAttribute(GetDllName());

View File

@@ -37,5 +37,5 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("0.1.37.17")] [assembly: AssemblyVersion("0.1.37.23")]
[assembly: AssemblyInformationalVersion("0.1.37.17 Alpha")] [assembly: AssemblyInformationalVersion("0.1.37.23 Alpha")]