Release version 0.1.13. Replaced PerformanceCounter based CPU load sensors with an implementation using NtQuerySystemInformation. Fixed a temperature reading problem for W83627DHG chips (sensors with invalid value 127?C).
1.1 --- a/Hardware/CPU/AMD0FCPU.cs Wed Feb 03 22:02:58 2010 +0000
1.2 +++ b/Hardware/CPU/AMD0FCPU.cs Thu Feb 04 21:19:27 2010 +0000
1.3 @@ -58,8 +58,7 @@
1.4
1.5 private List<ISensor> active = new List<ISensor>();
1.6
1.7 - private PerformanceCounter totalLoadCounter;
1.8 - private PerformanceCounter[] coreLoadCounters;
1.9 + private CPULoad cpuLoad;
1.10
1.11 private const ushort PCI_AMD_VENDOR_ID = 0x1022;
1.12 private const ushort PCI_AMD_0FH_MISCELLANEOUS_DEVICE_ID = 0x1103;
1.13 @@ -80,37 +79,23 @@
1.14 // max two cores
1.15 coreCount = coreCount > 2 ? 2 : coreCount;
1.16
1.17 - try {
1.18 - totalLoadCounter = new PerformanceCounter();
1.19 - totalLoadCounter.CategoryName = "Processor";
1.20 - totalLoadCounter.CounterName = "% Processor Time";
1.21 - totalLoadCounter.InstanceName = "_Total";
1.22 - totalLoadCounter.NextValue();
1.23 - } catch (Exception) {
1.24 - totalLoadCounter = null;
1.25 - }
1.26 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
1.27 -
1.28 - coreLoadCounters = new PerformanceCounter[coreCount];
1.29 +
1.30 + coreTemperatures = new Sensor[coreCount];
1.31 coreLoads = new Sensor[coreCount];
1.32 - for (int i = 0; i < coreLoadCounters.Length; i++) {
1.33 - try {
1.34 - coreLoadCounters[i] = new PerformanceCounter();
1.35 - coreLoadCounters[i].CategoryName = "Processor";
1.36 - coreLoadCounters[i].CounterName = "% Processor Time";
1.37 - coreLoadCounters[i].InstanceName = i.ToString();
1.38 - coreLoadCounters[i].NextValue();
1.39 - } catch (Exception) {
1.40 - coreLoadCounters[i] = null;
1.41 - }
1.42 + for (int i = 0; i < coreCount; i++) {
1.43 + coreTemperatures[i] =
1.44 + new Sensor("Core #" + (i + 1), i, SensorType.Temperature, this);
1.45 coreLoads[i] = new Sensor("Core #" + (i + 1), i + 1,
1.46 SensorType.Load, this);
1.47 - }
1.48 + }
1.49
1.50 - coreTemperatures = new Sensor[coreCount];
1.51 - for (int i = 0; i < coreCount; i++)
1.52 - coreTemperatures[i] =
1.53 - new Sensor("Core #" + (i + 1), i, SensorType.Temperature, this);
1.54 + cpuLoad = new CPULoad(coreCount, 1);
1.55 + if (cpuLoad.IsAvailable) {
1.56 + foreach (Sensor sensor in coreLoads)
1.57 + ActivateSensor(sensor);
1.58 + ActivateSensor(totalLoad);
1.59 + }
1.60
1.61 pciAddress = WinRing0.FindPciDeviceById(PCI_AMD_VENDOR_ID,
1.62 PCI_AMD_0FH_MISCELLANEOUS_DEVICE_ID, 0);
1.63 @@ -163,16 +148,12 @@
1.64 }
1.65 }
1.66
1.67 - if (totalLoadCounter != null) {
1.68 - totalLoad.Value = totalLoadCounter.NextValue();
1.69 - ActivateSensor(totalLoad);
1.70 + if (cpuLoad.IsAvailable) {
1.71 + cpuLoad.Update();
1.72 + for (int i = 0; i < coreLoads.Length; i++)
1.73 + coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
1.74 + totalLoad.Value = cpuLoad.GetTotalLoad();
1.75 }
1.76 -
1.77 - for (int i = 0; i < coreLoads.Length; i++)
1.78 - if (coreLoadCounters[i] != null) {
1.79 - coreLoads[i].Value = coreLoadCounters[i].NextValue();
1.80 - ActivateSensor(coreLoads[i]);
1.81 - }
1.82 }
1.83
1.84 private void ActivateSensor(Sensor sensor) {
2.1 --- a/Hardware/CPU/AMD10CPU.cs Wed Feb 03 22:02:58 2010 +0000
2.2 +++ b/Hardware/CPU/AMD10CPU.cs Thu Feb 04 21:19:27 2010 +0000
2.3 @@ -55,8 +55,7 @@
2.4
2.5 private List<ISensor> active = new List<ISensor>();
2.6
2.7 - private PerformanceCounter totalLoadCounter;
2.8 - private PerformanceCounter[] coreLoadCounters;
2.9 + private CPULoad cpuLoad;
2.10
2.11 private const ushort PCI_AMD_VENDOR_ID = 0x1022;
2.12 private const ushort PCI_AMD_10H_MISCELLANEOUS_DEVICE_ID = 0x1203;
2.13 @@ -72,32 +71,19 @@
2.14 if (cpuidExtData.GetLength(0) > 8)
2.15 coreCount = (cpuidExtData[8, 2] & 0xFF) + 1;
2.16
2.17 - try {
2.18 - totalLoadCounter = new PerformanceCounter();
2.19 - totalLoadCounter.CategoryName = "Processor";
2.20 - totalLoadCounter.CounterName = "% Processor Time";
2.21 - totalLoadCounter.InstanceName = "_Total";
2.22 - totalLoadCounter.NextValue();
2.23 - } catch (Exception) {
2.24 - totalLoadCounter = null;
2.25 - }
2.26 totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
2.27
2.28 - coreLoadCounters = new PerformanceCounter[coreCount];
2.29 coreLoads = new Sensor[coreCount];
2.30 - for (int i = 0; i < coreLoadCounters.Length; i++) {
2.31 - try {
2.32 - coreLoadCounters[i] = new PerformanceCounter();
2.33 - coreLoadCounters[i].CategoryName = "Processor";
2.34 - coreLoadCounters[i].CounterName = "% Processor Time";
2.35 - coreLoadCounters[i].InstanceName = i.ToString();
2.36 - coreLoadCounters[i].NextValue();
2.37 - } catch (Exception) {
2.38 - coreLoadCounters[i] = null;
2.39 - }
2.40 + for (int i = 0; i < coreCount; i++)
2.41 coreLoads[i] = new Sensor("Core #" + (i + 1), i + 1,
2.42 SensorType.Load, this);
2.43 - }
2.44 +
2.45 + cpuLoad = new CPULoad(coreCount, 1);
2.46 + if (cpuLoad.IsAvailable) {
2.47 + foreach (Sensor sensor in coreLoads)
2.48 + ActivateSensor(sensor);
2.49 + ActivateSensor(totalLoad);
2.50 + }
2.51
2.52 // AMD family 10h processors support only one temperature sensor
2.53 coreTemperature = new Sensor(
2.54 @@ -142,16 +128,12 @@
2.55 DeactivateSensor(coreTemperature);
2.56 }
2.57
2.58 - if (totalLoadCounter != null) {
2.59 - totalLoad.Value = totalLoadCounter.NextValue();
2.60 - ActivateSensor(totalLoad);
2.61 + if (cpuLoad.IsAvailable) {
2.62 + cpuLoad.Update();
2.63 + for (int i = 0; i < coreLoads.Length; i++)
2.64 + coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
2.65 + totalLoad.Value = cpuLoad.GetTotalLoad();
2.66 }
2.67 -
2.68 - for (int i = 0; i < coreLoads.Length; i++)
2.69 - if (coreLoadCounters[i] != null) {
2.70 - coreLoads[i].Value = coreLoadCounters[i].NextValue();
2.71 - ActivateSensor(coreLoads[i]);
2.72 - }
2.73 }
2.74
2.75 private void ActivateSensor(Sensor sensor) {
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/Hardware/CPU/CPULoad.cs Thu Feb 04 21:19:27 2010 +0000
3.3 @@ -0,0 +1,161 @@
3.4 +/*
3.5 +
3.6 + Version: MPL 1.1/GPL 2.0/LGPL 2.1
3.7 +
3.8 + The contents of this file are subject to the Mozilla Public License Version
3.9 + 1.1 (the "License"); you may not use this file except in compliance with
3.10 + the License. You may obtain a copy of the License at
3.11 +
3.12 + http://www.mozilla.org/MPL/
3.13 +
3.14 + Software distributed under the License is distributed on an "AS IS" basis,
3.15 + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
3.16 + for the specific language governing rights and limitations under the License.
3.17 +
3.18 + The Original Code is the Open Hardware Monitor code.
3.19 +
3.20 + The Initial Developer of the Original Code is
3.21 + Michael Möller <m.moeller@gmx.ch>.
3.22 + Portions created by the Initial Developer are Copyright (C) 2009-2010
3.23 + the Initial Developer. All Rights Reserved.
3.24 +
3.25 + Contributor(s):
3.26 +
3.27 + Alternatively, the contents of this file may be used under the terms of
3.28 + either the GNU General Public License Version 2 or later (the "GPL"), or
3.29 + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
3.30 + in which case the provisions of the GPL or the LGPL are applicable instead
3.31 + of those above. If you wish to allow use of your version of this file only
3.32 + under the terms of either the GPL or the LGPL, and not to allow others to
3.33 + use your version of this file under the terms of the MPL, indicate your
3.34 + decision by deleting the provisions above and replace them with the notice
3.35 + and other provisions required by the GPL or the LGPL. If you do not delete
3.36 + the provisions above, a recipient may use your version of this file under
3.37 + the terms of any one of the MPL, the GPL or the LGPL.
3.38 +
3.39 +*/
3.40 +
3.41 +using System;
3.42 +using System.Collections.Generic;
3.43 +using System.Runtime.InteropServices;
3.44 +using System.Text;
3.45 +
3.46 +namespace OpenHardwareMonitor.Hardware.CPU {
3.47 + public class CPULoad {
3.48 +
3.49 + [StructLayout(LayoutKind.Sequential)]
3.50 + private struct SystemProcessorPerformanceInformation {
3.51 + public long IdleTime;
3.52 + public long KernelTime;
3.53 + public long UserTime;
3.54 + public long Reserved0;
3.55 + public long Reserved1;
3.56 + public ulong Reserved2;
3.57 + }
3.58 +
3.59 + private enum SystemInformationClass : int {
3.60 + SystemBasicInformation = 0,
3.61 + SystemCpuInformation = 1,
3.62 + SystemPerformanceInformation = 2,
3.63 + SystemTimeOfDayInformation = 3,
3.64 + SystemProcessInformation = 5,
3.65 + SystemProcessorPerformanceInformation = 8
3.66 + }
3.67 +
3.68 + [DllImport("ntdll.dll")]
3.69 + private static extern int NtQuerySystemInformation(
3.70 + SystemInformationClass informationClass,
3.71 + [Out] SystemProcessorPerformanceInformation[] informations,
3.72 + int structSize, out IntPtr returnLength);
3.73 +
3.74 + private uint coreCount;
3.75 + private uint logicalProcessorsPerCore;
3.76 +
3.77 + private long systemTime;
3.78 + private long[] idleTimes;
3.79 +
3.80 + private float totalLoad;
3.81 + private float[] coreLoads;
3.82 +
3.83 + private bool available = false;
3.84 +
3.85 + private long[] GetIdleTimes() {
3.86 + long[] result = new long[coreCount * logicalProcessorsPerCore];
3.87 + SystemProcessorPerformanceInformation[] informations = new
3.88 + SystemProcessorPerformanceInformation[result.Length];
3.89 +
3.90 + IntPtr returnLength;
3.91 + NtQuerySystemInformation(
3.92 + SystemInformationClass.SystemProcessorPerformanceInformation,
3.93 + informations, informations.Length *
3.94 + Marshal.SizeOf(typeof(SystemProcessorPerformanceInformation)),
3.95 + out returnLength);
3.96 +
3.97 + for (int i = 0; i < result.Length; i++)
3.98 + result[i] = informations[i].IdleTime;
3.99 +
3.100 + return result;
3.101 + }
3.102 +
3.103 + public CPULoad(uint coreCount, uint logicalProcessorsPerCore) {
3.104 + this.coreCount = coreCount;
3.105 + this.logicalProcessorsPerCore = logicalProcessorsPerCore;
3.106 + this.coreLoads = new float[coreCount];
3.107 + this.systemTime = DateTime.Now.Ticks;
3.108 + this.totalLoad = 0;
3.109 + try {
3.110 + this.idleTimes = GetIdleTimes();
3.111 + } catch (Exception) {
3.112 + this.idleTimes = null;
3.113 + }
3.114 + if (idleTimes != null)
3.115 + available = true;
3.116 + }
3.117 +
3.118 + public bool IsAvailable {
3.119 + get { return available; }
3.120 + }
3.121 +
3.122 + public float GetTotalLoad() {
3.123 + return totalLoad;
3.124 + }
3.125 +
3.126 + public float GetCoreLoad(int core) {
3.127 + return coreLoads[core];
3.128 + }
3.129 +
3.130 + public void Update() {
3.131 + if (this.idleTimes == null)
3.132 + return;
3.133 +
3.134 + long systemTime = DateTime.Now.Ticks;
3.135 + long[] idleTimes = GetIdleTimes();
3.136 +
3.137 + if (systemTime - this.systemTime < 10000)
3.138 + return;
3.139 +
3.140 + float total = 0;
3.141 + for (int i = 0; i < coreCount; i++) {
3.142 + float value = 0;
3.143 + for (int j = 0; j < logicalProcessorsPerCore; j++) {
3.144 + long index = i * logicalProcessorsPerCore + j;
3.145 + long delta = idleTimes[index] - this.idleTimes[index];
3.146 + value += delta;
3.147 + total += delta;
3.148 + }
3.149 + value = 1.0f - value / (logicalProcessorsPerCore *
3.150 + (systemTime - this.systemTime));
3.151 + value = value < 0 ? 0 : value;
3.152 + coreLoads[i] = value * 100;
3.153 + }
3.154 + total = 1.0f - total / (coreCount * logicalProcessorsPerCore *
3.155 + (systemTime - this.systemTime));
3.156 + total = total < 0 ? 0 : total;
3.157 + this.totalLoad = total * 100;
3.158 +
3.159 + this.systemTime = systemTime;
3.160 + this.idleTimes = idleTimes;
3.161 + }
3.162 +
3.163 + }
3.164 +}
4.1 --- a/Hardware/CPU/IntelCPU.cs Wed Feb 03 22:02:58 2010 +0000
4.2 +++ b/Hardware/CPU/IntelCPU.cs Thu Feb 04 21:19:27 2010 +0000
4.3 @@ -59,9 +59,8 @@
4.4 private uint logicalProcessorsPerCore;
4.5 private uint coreCount;
4.6
4.7 - private PerformanceCounter totalLoadCounter;
4.8 - private PerformanceCounter[] coreLoadCounters;
4.9 -
4.10 + private CPULoad cpuLoad;
4.11 +
4.12 private const uint IA32_THERM_STATUS_MSR = 0x019C;
4.13 private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
4.14
4.15 @@ -133,30 +132,7 @@
4.16 default: tjMax = 100; break;
4.17 }
4.18
4.19 - try {
4.20 - totalLoadCounter = new PerformanceCounter();
4.21 - totalLoadCounter.CategoryName = "Processor";
4.22 - totalLoadCounter.CounterName = "% Processor Time";
4.23 - totalLoadCounter.InstanceName = "_Total";
4.24 - totalLoadCounter.NextValue();
4.25 - } catch (Exception) {
4.26 - totalLoadCounter = null;
4.27 - }
4.28 - totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
4.29 -
4.30 - coreLoadCounters = new PerformanceCounter[
4.31 - coreCount * logicalProcessorsPerCore];
4.32 - for (int i = 0; i < coreLoadCounters.Length; i++) {
4.33 - try {
4.34 - coreLoadCounters[i] = new PerformanceCounter();
4.35 - coreLoadCounters[i].CategoryName = "Processor";
4.36 - coreLoadCounters[i].CounterName = "% Processor Time";
4.37 - coreLoadCounters[i].InstanceName = i.ToString();
4.38 - coreLoadCounters[i].NextValue();
4.39 - } catch (Exception) {
4.40 - coreLoadCounters[i] = null;
4.41 - }
4.42 - }
4.43 + totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
4.44
4.45 coreTemperatures = new Sensor[coreCount];
4.46 coreLoads = new Sensor[coreCount];
4.47 @@ -167,6 +143,13 @@
4.48 SensorType.Load, this);
4.49 }
4.50
4.51 + cpuLoad = new CPULoad(coreCount, logicalProcessorsPerCore);
4.52 + if (cpuLoad.IsAvailable) {
4.53 + foreach (Sensor sensor in coreLoads)
4.54 + ActivateSensor(sensor);
4.55 + ActivateSensor(totalLoad);
4.56 + }
4.57 +
4.58 Update();
4.59 }
4.60
4.61 @@ -221,27 +204,11 @@
4.62 }
4.63 }
4.64
4.65 - if (totalLoadCounter != null) {
4.66 - totalLoad.Value = totalLoadCounter.NextValue();
4.67 - ActivateSensor(totalLoad);
4.68 - }
4.69 -
4.70 - for (int i = 0; i < coreLoads.Length; i++) {
4.71 - float value = 0;
4.72 - int count = 0;
4.73 - for (int j = 0; j < logicalProcessorsPerCore; j++) {
4.74 - PerformanceCounter counter =
4.75 - coreLoadCounters[logicalProcessorsPerCore * i + j];
4.76 - if (counter != null) {
4.77 - value += counter.NextValue();
4.78 - count++;
4.79 - }
4.80 - }
4.81 - if (count > 0) {
4.82 - value /= count;
4.83 - coreLoads[i].Value = value;
4.84 - ActivateSensor(coreLoads[i]);
4.85 - }
4.86 + if (cpuLoad.IsAvailable) {
4.87 + cpuLoad.Update();
4.88 + for (int i = 0; i < coreLoads.Length; i++)
4.89 + coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
4.90 + totalLoad.Value = cpuLoad.GetTotalLoad();
4.91 }
4.92 }
4.93
5.1 --- a/Hardware/LPC/W83627DHG.cs Wed Feb 03 22:02:58 2010 +0000
5.2 +++ b/Hardware/LPC/W83627DHG.cs Thu Feb 04 21:19:27 2010 +0000
5.3 @@ -94,7 +94,7 @@
5.4 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
5.5 return WinRing0.ReadIoPortByte(
5.6 (ushort)(address + DATA_REGISTER_OFFSET));
5.7 - }
5.8 + }
5.9
5.10 public W83627DHG(Chip chip, byte revision, ushort address) {
5.11 this.chip = chip;
5.12 @@ -215,14 +215,14 @@
5.13 foreach (Sensor sensor in temperatures) {
5.14 int value;
5.15 if (sensor.Index < 2) {
5.16 - value = ReadByte((byte)(sensor.Index + 1), TEMPERATURE_BASE_REG);
5.17 + value = (sbyte)ReadByte((byte)(sensor.Index + 1), TEMPERATURE_BASE_REG);
5.18 value = (value << 1) | ReadByte((byte)(sensor.Index + 1),
5.19 (byte)(TEMPERATURE_BASE_REG + 1)) >> 7;
5.20 } else {
5.21 - value = ReadByte(0, TEMPERATURE_SYS_REG) << 1;
5.22 + value = (sbyte)ReadByte(0, TEMPERATURE_SYS_REG) << 1;
5.23 }
5.24 sensor.Value = value / 2.0f;
5.25 - if (value < 0x1FE)
5.26 + if (value < 0xFE)
5.27 ActivateSensor(sensor);
5.28 else
5.29 DeactivateSensor(sensor);
6.1 --- a/OpenHardwareMonitor.csproj Wed Feb 03 22:02:58 2010 +0000
6.2 +++ b/OpenHardwareMonitor.csproj Thu Feb 04 21:19:27 2010 +0000
6.3 @@ -13,6 +13,8 @@
6.4 <FileAlignment>512</FileAlignment>
6.5 <RootNamespace>OpenHardwareMonitor</RootNamespace>
6.6 <ApplicationIcon>Resources\icon.ico</ApplicationIcon>
6.7 + <ApplicationManifest>Resources\app.manifest</ApplicationManifest>
6.8 + <StartupObject>OpenHardwareMonitor.Program</StartupObject>
6.9 </PropertyGroup>
6.10 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
6.11 <DebugSymbols>true</DebugSymbols>
6.12 @@ -59,6 +61,7 @@
6.13 <Compile Include="GUI\TypeNode.cs" />
6.14 <Compile Include="Hardware\CPU\AMD10CPU.cs" />
6.15 <Compile Include="Hardware\CPU\AMD0FCPU.cs" />
6.16 + <Compile Include="Hardware\CPU\CPULoad.cs" />
6.17 <Compile Include="Hardware\HDD\HDD.cs" />
6.18 <Compile Include="Hardware\HDD\HDDGroup.cs" />
6.19 <Compile Include="Hardware\HDD\SMART.cs" />
6.20 @@ -139,6 +142,9 @@
6.21 <EmbeddedResource Include="Resources\voltage.png" />
6.22 <EmbeddedResource Include="Resources\nvidia.png" />
6.23 </ItemGroup>
6.24 + <ItemGroup>
6.25 + <None Include="Resources\app.manifest" />
6.26 + </ItemGroup>
6.27 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
6.28 <ProjectExtensions>
6.29 <VisualStudio AllowExistingFolder="true" />
7.1 --- a/Properties/AssemblyInfo.cs Wed Feb 03 22:02:58 2010 +0000
7.2 +++ b/Properties/AssemblyInfo.cs Thu Feb 04 21:19:27 2010 +0000
7.3 @@ -69,5 +69,5 @@
7.4 // You can specify all the values or you can default the Build and Revision Numbers
7.5 // by using the '*' as shown below:
7.6 // [assembly: AssemblyVersion("1.0.*")]
7.7 -[assembly: AssemblyVersion("0.1.12.0")]
7.8 -[assembly: AssemblyFileVersion("0.1.12.0")]
7.9 +[assembly: AssemblyVersion("0.1.13.0")]
7.10 +[assembly: AssemblyFileVersion("0.1.13.0")]
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/Resources/app.manifest Thu Feb 04 21:19:27 2010 +0000
8.3 @@ -0,0 +1,10 @@
8.4 +<?xml version="1.0" encoding="utf-8"?>
8.5 +<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
8.6 + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
8.7 + <security>
8.8 + <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
8.9 + <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
8.10 + </requestedPrivileges>
8.11 + </security>
8.12 + </trustInfo>
8.13 +</asmv1:assembly>
9.1 --- a/Utilities/HexStringArray.cs Wed Feb 03 22:02:58 2010 +0000
9.2 +++ b/Utilities/HexStringArray.cs Thu Feb 04 21:19:27 2010 +0000
9.3 @@ -47,8 +47,9 @@
9.4 public HexStringArray(string input) {
9.5 List<byte> list = new List<byte>();
9.6 foreach (string str in input.Split(' ')) {
9.7 - if (str.Trim().Length > 0)
9.8 - list.Add(Convert.ToByte(str, 16));
9.9 + string s = str.Trim();
9.10 + if (s.Length > 0)
9.11 + list.Add(Convert.ToByte(s, 16));
9.12 }
9.13 array = list.ToArray();
9.14 }