mirror of
https://github.com/rawhide-kobayashi/ryzen-smu-cli.git
synced 2025-04-16 12:51:51 -05:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
3ecb82affa | |||
78e45c6e83 | |||
b38dc16cc1 | |||
ef97c0c290 | |||
2543c6d236 | |||
![]() |
85d2aeb680 | ||
697af89273 | |||
![]() |
298526378c | ||
fe26e9dc75 | |||
9ebbdfc47f | |||
f177ffda80 | |||
b2bf2571c6 | |||
ddcdd4f9b5 | |||
5fff12d1fc | |||
ba192c9061 |
40
README.md
40
README.md
@ -1,3 +1,41 @@
|
||||
# ryzen-smu-cli
|
||||
|
||||
A CLI tool for the ZenStates SMU library.
|
||||
A CLI tool for the ZenStates SMU library. See [ZenStates-Core](https://github.com/irusanov/ZenStates-Core) for compatibility.
|
||||
|
||||
Requires [.NET Framework 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0).
|
||||
|
||||
All credit to irusanov, who wrote [ZenStates-Core](https://github.com/irusanov/ZenStates-Core) on which this depends for all meaningful functionality, and [SMUDebugTool](https://github.com/irusanov/SMUDebugTool) for usage examples.
|
||||
|
||||
Usage:
|
||||
```
|
||||
PS G:\win-x64> .\ryzen-smu-cli.exe --help
|
||||
Description:
|
||||
A CLI for the Ryzen SMU.
|
||||
|
||||
Usage:
|
||||
ryzen-smu-cli [options]
|
||||
|
||||
Options:
|
||||
--offset <offset> Specify a zero-indexed logical core, or list of logical cores, and their PBO
|
||||
offset(s), in a fashion similar to taskset. e.g. 0:-10,1:5,2:-20,14:-25. These are
|
||||
the logical core IDs as they appear in your system, not the true IDs according to
|
||||
fused hardware disabled cores. Alternatively, you may supply a simpler
|
||||
comma-separated list of offset values - e.g. 0,-14,-30,5,-10,-22 - but, obviously,
|
||||
this can only set the value on up to X core that you define.
|
||||
--disable-cores <disable-cores> Specify a zero-indexed list of logical cores to disable. e.g. 0,1,4,7,12,15. This
|
||||
setting does not take into account any current core disablement. All cores you
|
||||
wish to disable must be specified. Any that are unspecified will be enabled. This
|
||||
option requires a reboot.
|
||||
--enable-all-cores Enable all cores.
|
||||
--get-offsets-terse Print a list of all PBO offsets on logical cores in a simple, comma-separated
|
||||
format, without core identifiers. e.g. -15,0,2,-20. Note that you cannot retrieve
|
||||
the offsets from disabled cores.
|
||||
--get-physical-cores Print a list of physical cores, to find out which ones are disabled in
|
||||
<8-core-per-CCD SKUs.
|
||||
--get-enabled-cores Print a list of logically enabled/disabled cores, and their relationship to the
|
||||
physical cores, inclusive of factory-fused disabled cores.
|
||||
--set-pbo-scalar <set-pbo-scalar> Sets the PBO scalar. This is a whole number between 1 and 10.
|
||||
--get-pbo-scalar Get the current PBO scalar.
|
||||
--version Show version information
|
||||
-?, -h, --help Show help and usage information
|
||||
```
|
||||
|
@ -1,48 +1,357 @@
|
||||
using System.CommandLine;
|
||||
using System.Management;
|
||||
using ZenStates.Core;
|
||||
using System.Security.Principal;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
class Program
|
||||
namespace ryzen_smu_cli
|
||||
{
|
||||
private static readonly Cpu ryzen = new();
|
||||
|
||||
static int Main(string[] args)
|
||||
class Program
|
||||
{
|
||||
|
||||
|
||||
var rootCommand = new RootCommand("A CLI for the Ryzen SMU.");
|
||||
|
||||
var pboOffset = new Option<string>("--offset", "Specify a core, or list of cores, and their PBO offset(s), in a fashion similar to taskset. e.g. 0:-10,1:5,2:-20,14:-25");
|
||||
|
||||
rootCommand.AddOption(pboOffset);
|
||||
|
||||
rootCommand.SetHandler((offsetArgs) =>
|
||||
private static readonly Cpu ryzen;
|
||||
private static readonly Dictionary<int, int> mappedCores;
|
||||
static Program()
|
||||
{
|
||||
RunPBOOffset(offsetArgs);
|
||||
}, pboOffset);
|
||||
if (!IsAdministrator())
|
||||
{
|
||||
Console.Error.WriteLine("This application must be run as an administrator.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
private static void RunPBOOffset(string offsetArgs)
|
||||
{
|
||||
string[] arg = offsetArgs.Split(',');
|
||||
|
||||
for (int i = 0; i < arg.Length; i++)
|
||||
{
|
||||
ApplySingleCorePBOOffset(Convert.ToInt32(arg[i].Split(':')[0]), Convert.ToInt32(arg[i].Split(':')[1]));
|
||||
try
|
||||
{
|
||||
ryzen = new Cpu();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
Console.Error.WriteLine("If the previous message was unclear, for some reason, ZenStates-Core failed to initialize an instance of the CPU control object.");
|
||||
Environment.Exit(2);
|
||||
}
|
||||
mappedCores = MapLogicalCoresToPhysical();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplySingleCorePBOOffset(int coreNumber, int value)
|
||||
{
|
||||
// Magic numbers from SMUDebugTool
|
||||
// This does some bitshifting calculations to get the mask for individual cores for chips with up to two CCDs
|
||||
// I'm not sure if it would work with more, in theory. It's unclear to me based on the github issues.
|
||||
int mapIndex = coreNumber < 8 ? 0 : 1;
|
||||
if ((~ryzen.info.topology.coreDisableMap[mapIndex] >> coreNumber % 8 & 1) == 1)
|
||||
private static readonly string wmiAMDACPI = "AMD_ACPI";
|
||||
private static readonly string wmiScope = "root\\wmi";
|
||||
private static ManagementObject? classInstance;
|
||||
private static string? instanceName;
|
||||
private static ManagementBaseObject? pack;
|
||||
|
||||
private static List<WmiCmdListItem> availableCommands = [];
|
||||
|
||||
private static bool wmiPopulated = false;
|
||||
private static bool rebootFlag = false;
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
ryzen.SetPsmMarginSingleCore((uint)(((mapIndex << 8) | coreNumber % 8 & 0xF) << 20), value);
|
||||
Console.WriteLine($"Set core {coreNumber} to offset {value}!");
|
||||
var rootCommand = new RootCommand("A CLI for the Ryzen SMU.");
|
||||
|
||||
var pboOffset = new Option<string>("--offset", "Specify a zero-indexed logical core, or list of logical cores, and their PBO offset(s), in a fashion similar to taskset. e.g. 0:-10,1:5,2:-20,14:-25. These are the logical core IDs as they appear in your system, not the true IDs according to fused hardware disabled cores. Alternatively, you may supply a simpler comma-separated list of offset values - e.g. 0,-14,-30,5,-10,-22 - but, obviously, this can only set the value on up to X core that you define.");
|
||||
var disableCores = new Option<string>("--disable-cores", "Specify a zero-indexed list of logical cores to disable. e.g. 0,1,4,7,12,15. This setting does not take into account any current core disablement. All cores you wish to disable must be specified. Any that are unspecified will be enabled. This option requires a reboot.");
|
||||
var enableCores = new Option<bool>("--enable-all-cores", "Enable all cores.");
|
||||
//var toggleJson = new Option<bool>("--json", "Enable json format for informational outputs.");
|
||||
var getCurrentPBOTerse = new Option<bool>("--get-offsets-terse", "Print a list of all PBO offsets on logical cores in a simple, comma-separated format, without core identifiers. e.g. -15,0,2,-20. Note that you cannot retrieve the offsets from disabled cores.");
|
||||
var getPhysicalCores = new Option<bool>("--get-physical-cores", "Print a list of physical cores, to find out which ones are disabled in <8-core-per-CCD SKUs.");
|
||||
var getEnabledCores = new Option<bool>("--get-enabled-cores", "Print a list of logically enabled/disabled cores, and their relationship to the physical cores, inclusive of factory-fused disabled cores.");
|
||||
var setPBOScalar = new Option<string>("--set-pbo-scalar", "Sets the PBO scalar. This is a whole number between 1 and 10.");
|
||||
var getPBOScalar = new Option<bool>("--get-pbo-scalar", "Get the current PBO scalar.");
|
||||
|
||||
rootCommand.AddOption(pboOffset);
|
||||
rootCommand.AddOption(disableCores);
|
||||
rootCommand.AddOption(enableCores);
|
||||
rootCommand.AddOption(getCurrentPBOTerse);
|
||||
rootCommand.AddOption(getPhysicalCores);
|
||||
rootCommand.AddOption(getEnabledCores);
|
||||
rootCommand.AddOption(setPBOScalar);
|
||||
rootCommand.AddOption(getPBOScalar);
|
||||
|
||||
rootCommand.SetHandler((offsetArgs, coreDisableArgs, enableAll, getCurrentPBOTerse, getEnabledCores, getPhysicalCores,
|
||||
setPBOScalar, getPBOScalar) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(offsetArgs)) ApplyPBOOffset(offsetArgs);
|
||||
|
||||
if (!string.IsNullOrEmpty(coreDisableArgs)) ApplyDisableCores(coreDisableArgs);
|
||||
|
||||
if (enableAll) ApplyDisableCores();
|
||||
|
||||
if (getCurrentPBOTerse)
|
||||
{
|
||||
Console.WriteLine("Current PBO offsets:");
|
||||
string offsetLine = "";
|
||||
bool flagNotifyDisabledCCD = false;
|
||||
for (int i = 0; i < mappedCores.Count; i++)
|
||||
{
|
||||
int mapIndex = i < 8 ? 0 : 1;
|
||||
try
|
||||
{
|
||||
offsetLine += Convert.ToDecimal((int)ryzen.GetPsmMarginSingleCore((uint)(((mapIndex << 8) | ((mappedCores[i] % 8) & 0xF)) << 20))!);
|
||||
offsetLine += ",";
|
||||
}
|
||||
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// This will occur if a CCD is disabled through BIOS e.g. with TURBO GAMER MODE.
|
||||
// But it's not a real problem.
|
||||
flagNotifyDisabledCCD = true;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(offsetLine.TrimEnd(','));
|
||||
|
||||
if (flagNotifyDisabledCCD) Console.WriteLine("You have manually disabled cores. It is not possible to retrieve offset values while they are disabled.");
|
||||
}
|
||||
|
||||
if (getPhysicalCores)
|
||||
{
|
||||
Console.WriteLine("Fused status of physical cores:");
|
||||
for (var i = 0; i < ryzen.info.topology.physicalCores; i++)
|
||||
{
|
||||
int mapIndex = i < 8 ? 0 : 1;
|
||||
if ((~ryzen.info.topology.coreDisableMap[mapIndex] >> i % 8 & 1) == 0) Console.WriteLine($"Core {i}: Disabled");
|
||||
else Console.WriteLine($"Core {i}: Enabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (getEnabledCores)
|
||||
{
|
||||
for (var i = 0; i < ryzen.info.topology.physicalCores; i++)
|
||||
{
|
||||
if (mappedCores.ContainsValue(i)) Console.WriteLine($"Core {i}: Enabled");
|
||||
else Console.WriteLine($"Core {i}: Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(setPBOScalar)) ryzen.SetPBOScalar(Convert.ToUInt32(setPBOScalar));
|
||||
if (getPBOScalar) Console.WriteLine($"Current PBO scalar: {ryzen.GetPBOScalar()}");
|
||||
}, pboOffset, disableCores, enableCores, getCurrentPBOTerse, getEnabledCores, getPhysicalCores, setPBOScalar, getPBOScalar);
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
rootCommand.Invoke("--help");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
ryzen.Dispose();
|
||||
|
||||
if (rebootFlag) Console.WriteLine("A reboot is required for changes to take effect.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Dictionary<int, int> MapLogicalCoresToPhysical()
|
||||
{
|
||||
Dictionary<int, int> mappedCores = [];
|
||||
|
||||
int maxTries = 3;
|
||||
|
||||
for (int currentTry = 1; currentTry <= maxTries; currentTry++)
|
||||
{
|
||||
mappedCores.Clear();
|
||||
int logicalCoreIter = 0;
|
||||
|
||||
for (var i = 0; i < ryzen.info.topology.physicalCores; i++)
|
||||
{
|
||||
int mapIndex = i < 8 ? 0 : 1;
|
||||
if (ryzen.GetPsmMarginSingleCore((uint) (((mapIndex << 8) | ((i % 8) & 0xF)) << 20)) != null)
|
||||
{
|
||||
mappedCores.Add(logicalCoreIter, i);
|
||||
logicalCoreIter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The mapped cores should match the amount of physical cores
|
||||
if (mappedCores.Count == ryzen.info.topology.cores)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Something weird is happening if we cannot find the logical cores for all the physical ones even after three attempts
|
||||
if (currentTry >= maxTries)
|
||||
{
|
||||
Console.Error.WriteLine($"Did not find the expected amount of cores for mapping ({ryzen.info.topology.cores} expected but found only {mappedCores.Count})!");
|
||||
Environment.Exit(8);
|
||||
}
|
||||
}
|
||||
|
||||
return mappedCores;
|
||||
}
|
||||
|
||||
private static string GetWmiInstanceName()
|
||||
{
|
||||
try
|
||||
{
|
||||
instanceName = WMI.GetInstanceName(wmiScope, wmiAMDACPI);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return instanceName!;
|
||||
}
|
||||
|
||||
private static void PopulateWmiFunctions()
|
||||
{
|
||||
try
|
||||
{
|
||||
instanceName = GetWmiInstanceName();
|
||||
classInstance = new ManagementObject(wmiScope,
|
||||
$"{wmiAMDACPI}.InstanceName='{instanceName}'",
|
||||
null);
|
||||
|
||||
// Get function names with their IDs
|
||||
string[] functionObjects = { "GetObjectID", "GetObjectID2" };
|
||||
var index = 1;
|
||||
|
||||
foreach (var functionObject in functionObjects)
|
||||
{
|
||||
try
|
||||
{
|
||||
pack = WMI.InvokeMethodAndGetValue(classInstance, functionObject, "pack", null, 0);
|
||||
|
||||
if (pack != null)
|
||||
{
|
||||
var ID = (uint[])pack.GetPropertyValue("ID");
|
||||
var IDString = (string[])pack.GetPropertyValue("IDString");
|
||||
var Length = (byte)pack.GetPropertyValue("Length");
|
||||
|
||||
for (var i = 0; i < Length; ++i)
|
||||
{
|
||||
if (IDString[i] == "")
|
||||
break;
|
||||
|
||||
WmiCmdListItem item = new($"{IDString[i] + ": "}{ID[i]:X8}", ID[i], !IDString[i].StartsWith("Get"));
|
||||
availableCommands.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
rebootFlag = true;
|
||||
wmiPopulated = true;
|
||||
}
|
||||
|
||||
private static void ApplyPBOOffset(string offsetArgs)
|
||||
{
|
||||
// This checks if the current SKU has a known register for writing PBO offsets
|
||||
if (ryzen.smu.Rsmu.SMU_MSG_SetDldoPsmMargin == 0)
|
||||
{
|
||||
Console.Error.WriteLine("You have attempted to enable PBO offsets on a CPU that does not support them.");
|
||||
Environment.Exit(3);
|
||||
}
|
||||
|
||||
|
||||
string validArgFormat = @"^(-?\d{1,2}(,-?\d{1,2})*|\d{1,2}:-?\d{1,2}(,\d{1,2}:-?\d{1,2})*)$";
|
||||
|
||||
if (!Regex.IsMatch(offsetArgs, validArgFormat))
|
||||
{
|
||||
Console.Error.WriteLine("Malformed input format for offsets. Please check and try again.");
|
||||
Environment.Exit(4);
|
||||
}
|
||||
|
||||
|
||||
string[] arg = offsetArgs.Split(',');
|
||||
|
||||
if (arg.Length > mappedCores.Count)
|
||||
{
|
||||
Console.Error.WriteLine("Specified a greater number of offsets than logical cores active in the system. Please check and try again.");
|
||||
Environment.Exit(5);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < arg.Length; i++)
|
||||
{
|
||||
// Magic numbers from SMUDebugTool
|
||||
// This does some bitshifting calculations to get the mask for individual cores for chips with up to two CCDs
|
||||
// Support for threadrippers/epyc is theoretically available, if the calculations were expanded, but are untested
|
||||
try
|
||||
{
|
||||
if (arg[i].Contains(':'))
|
||||
{
|
||||
int core = Convert.ToInt32(arg[i].Split(':')[0]);
|
||||
int offset = Convert.ToInt32(arg[i].Split(':')[1]);
|
||||
int mapIndex = mappedCores[core] < 8 ? 0 : 1;
|
||||
ryzen.SetPsmMarginSingleCore((uint)(((mapIndex << 8) | mappedCores[core] % 8 & 0xF) << 20), offset);
|
||||
Console.WriteLine($"Set logical core {core}, physical core {mappedCores[core]} offset to {offset}!");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int mapIndex = mappedCores[i] < 8 ? 0 : 1;
|
||||
ryzen.SetPsmMarginSingleCore((uint)(((mapIndex << 8) | mappedCores[i] % 8 & 0xF) << 20), Convert.ToInt32(arg[i]));
|
||||
Console.WriteLine($"Set logical core {i}, physical core {mappedCores[i]} offset to {arg[i]}!");
|
||||
}
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Console.Error.WriteLine($"Tried to set an offset on logical core {Convert.ToInt32(arg[i].Split(':')[0])}, but there are only {mappedCores.Count} (zero-indexed, as a reminder) logical cores active in the system.");
|
||||
Environment.Exit(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyDisableCores(string coreArgs = "Enable")
|
||||
{
|
||||
if (!wmiPopulated) PopulateWmiFunctions();
|
||||
|
||||
// More magic from SMUDebugTool...
|
||||
// uintccd2 = 0x8200; ? :)
|
||||
uint[] ccds = [0x8000, 0x8100];
|
||||
|
||||
var cmdItem = availableCommands.FirstOrDefault(item => item.text.Contains("Software Downcore Config"));
|
||||
|
||||
if ( cmdItem == null ) {
|
||||
Console.Error.WriteLine("Something has gone terribly wrong, the downcore config option is not present.");
|
||||
Environment.Exit(7);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ccds.Length; i++)
|
||||
{
|
||||
if (coreArgs != "Enable")
|
||||
{
|
||||
int[] arg = [.. coreArgs.Split(',').Select(int.Parse)];
|
||||
|
||||
for (int x = 0; x < 8; x++)
|
||||
{
|
||||
if (arg.Contains(x + (i * 8)))
|
||||
{
|
||||
ccds[i] = Utils.SetBits(ccds[i], x, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Unreadable garbage... But it's my unreadable garbage. It just prints the bitmaps in the expected,
|
||||
// human order.
|
||||
Console.WriteLine($"New core disablement bitmap for CCD{i} (reversed lower half): {new string([.. Convert.ToString((int)(ccds[i] & 0xFF), 2).PadLeft(8, '0').Reverse()])}");
|
||||
WMI.RunCommand(classInstance!, cmdItem.value, ccds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAdministrator()
|
||||
{
|
||||
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
WindowsPrincipal principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
ryzen-smu-cli/Utils/WmiCmdListItem.cs
Normal file
22
ryzen-smu-cli/Utils/WmiCmdListItem.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace ryzen_smu_cli
|
||||
{
|
||||
public class WmiCmdListItem
|
||||
{
|
||||
public uint value { get; }
|
||||
public string text { get; }
|
||||
|
||||
public bool isSet { get; }
|
||||
|
||||
public WmiCmdListItem(string text, uint value, bool isSet = false)
|
||||
{
|
||||
this.text = text;
|
||||
this.value = value;
|
||||
this.isSet = isSet;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.text;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
<RootNamespace>ryzen_smu_cli</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.1</Version>
|
||||
<Version>0.1.2</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -14,25 +14,27 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Set build configuration to Release by default -->
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
|
||||
|
||||
<!-- Enable single-file publishing -->
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
|
||||
<!-- Target runtime -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
<!-- Disable self-contained deployment -->
|
||||
<SelfContained>false</SelfContained>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="include\ZenStates-Core\External\WinRing0\WinRing0.sys">
|
||||
<LogicalName>ZenStates.Core.WinRing0.sys</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="include\ZenStates-Core\External\WinRing0\WinRing0x64.sys">
|
||||
<LogicalName>ZenStates.Core.WinRing0x64.sys</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<!-- Included for the ZenStates-Core submodule...-->
|
||||
<PackageReference Include="System.Management" Version="6.0.0" />
|
||||
<!-- Included for the ZenStates-Core submodule...-->
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Threading.AccessControl" Version="6.0.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user