Skip to content

Commit

Permalink
Merge pull request #620 from WildGums/feature/nullable-fix
Browse files Browse the repository at this point in the history
Fix NullReferenceException when the native enumerator returns null
  • Loading branch information
GeertvanHorrik authored Oct 12, 2023
2 parents 897ba58 + d12f34d commit 405ec99
Show file tree
Hide file tree
Showing 14 changed files with 96 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/Orc.SystemInfo.Tests/Orc.SystemInfo.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="NUnit" Version="3.13.3" PrivateAssets="all" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" PrivateAssets="all" />
<PackageReference Include="PublicApiGenerator" Version="11.0.0" PrivateAssets="all" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[assembly: System.Resources.NeutralResourcesLanguage("en-US")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Orc.SystemInfo.Tests")]
[assembly: System.Runtime.InteropServices.ComVisible(false)]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName=".NET 6.0")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Orc.SystemInfo.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Orc.SystemInfo.Win32;
using Orc.SystemInfo.Wmi;

public class WindowsManagementObjectEnumeratorFacts
{
[Test]
public async Task Returns_False_When_Enumerator_Returns_Null_Async()
{
IWbemClassObject objects;
uint returnedCount;

var wbemClassObjectEnumeratorMock = new Mock<IWbemClassObjectEnumerator>();

wbemClassObjectEnumeratorMock.Setup(x => x.Next(It.IsAny<int>(), It.IsAny<uint>(), out objects, out returnedCount))
.Returns((int to, uint c, out IWbemClassObject o, out uint rc) =>
{
o = null;
rc = 0;
return new HResult(0);
});

using var enumerator = new WindowsManagementObjectEnumerator(wbemClassObjectEnumeratorMock.Object);

var moveNextResult = enumerator.MoveNext();

Assert.That(moveNextResult, Is.False);
}
}
}
1 change: 1 addition & 0 deletions src/Orc.SystemInfo/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
[assembly: ComVisible(false)]

[assembly: InternalsVisibleTo("Orc.SystemInfo.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
7 changes: 6 additions & 1 deletion src/Orc.SystemInfo/Win32/HResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,9 @@ public void ThrowIfFailed()
throw ex;
}
}
}

public override string ToString()
{
return $"{_value} (Failed = {Failed})";
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
namespace Orc.SystemInfo.Win32;

using System;
using System.Threading;

public static class IWbemClassObjectEnumeratorExtensions
{
internal static IWbemClassObject Next(this IWbemClassObjectEnumerator wbemClassObjectEnumerator)
internal static IWbemClassObject? Next(this IWbemClassObjectEnumerator wbemClassObjectEnumerator)
{
const uint count = 1;
var hresult = wbemClassObjectEnumerator.Next(Timeout.Infinite, count, out var current, out _);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ internal static class IWbemLocatorExtensions
/// <param name="resource"></param>
/// <param name="ctx"></param>
/// <returns></returns>
internal static IWbemServices ConnectServer(this IWbemLocator wbemLocator, string resource, IWbemContext? ctx)
internal static IWbemServices? ConnectServer(this IWbemLocator wbemLocator, string resource, IWbemContext? ctx)
{
var hr = wbemLocator.ConnectServer(resource, null, null, null, WbemConnectOption.None, null, ctx, out var services);

hr.ThrowIfFailed();

return services;
}
}
}
20 changes: 10 additions & 10 deletions src/Orc.SystemInfo/Win32/Wbem/Interfaces/IWbemClassObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,31 @@ HResult GetNames([In, MarshalAs(UnmanagedType.LPWStr)] string? qualifierName, [I
HResult EndEnumeration();

[PreserveSig]
HResult GetPropertyQualifierSet([In, MarshalAs(UnmanagedType.LPWStr)] string property, [Out] out IWbemQualifierSet qualifierSet);
HResult GetPropertyQualifierSet([In, MarshalAs(UnmanagedType.LPWStr)] string property, [Out] out IWbemQualifierSet? qualifierSet);

[PreserveSig]
HResult Clone([Out] out IWbemClassObject copy);

[PreserveSig]
HResult GetObjectText([In] int flags, [Out, MarshalAs(UnmanagedType.BStr)] out string objectText);
HResult GetObjectText([In] int flags, [Out, MarshalAs(UnmanagedType.BStr)] out string? objectText);

[PreserveSig]
HResult SpawnDerivedClass([In] int flags, [Out] out IWbemClassObject newClass);
HResult SpawnDerivedClass([In] int flags, [Out] out IWbemClassObject? newClass);

[PreserveSig]
HResult SpawnInstance([In] int flags, [Out] out IWbemClassObject newInstance);
HResult SpawnInstance([In] int flags, [Out] out IWbemClassObject? newInstance);

[PreserveSig]
HResult CompareTo([In] WbemClassObjectComparisonOptions compareOption, [In] IWbemClassObject compareTo);

[PreserveSig]
HResult GetPropertyOrigin([In, MarshalAs(UnmanagedType.LPWStr)] string name, [Out, MarshalAs(UnmanagedType.BStr)] out string className);
HResult GetPropertyOrigin([In, MarshalAs(UnmanagedType.LPWStr)] string name, [Out, MarshalAs(UnmanagedType.BStr)] out string? className);

[PreserveSig]
HResult InheritsFrom([In, MarshalAs(UnmanagedType.LPWStr)] string ancestor);

[PreserveSig]
HResult GetMethod([In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] int flags, [Out] out IWbemClassObject inSignature, [Out] out IWbemClassObject outSignature);
HResult GetMethod([In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] int flags, [Out] out IWbemClassObject? inSignature, [Out] out IWbemClassObject? outSignature);

[PreserveSig]
HResult PutMethod([In, MarshalAs(UnmanagedType.LPWStr)] string name, [In] int flags, [In] IWbemClassObject inSignature, [In] IWbemClassObject outSignature);
Expand All @@ -68,14 +68,14 @@ HResult GetNames([In, MarshalAs(UnmanagedType.LPWStr)] string? qualifierName, [I
HResult BeginMethodEnumeration([In] int enumFlags);

[PreserveSig]
HResult NextMethod([In] int flags, [Out, MarshalAs(UnmanagedType.BStr)] string name, [Out] out IWbemClassObject inSignature, [Out] out IWbemClassObject outSignature);
HResult NextMethod([In] int flags, [Out, MarshalAs(UnmanagedType.BStr)] string name, [Out] out IWbemClassObject? inSignature, [Out] out IWbemClassObject? outSignature);

[PreserveSig]
HResult EndMethodEnumeration();

[PreserveSig]
HResult GetMethodQualifierSet([In, MarshalAs(UnmanagedType.LPWStr)] string method, [Out] out IWbemQualifierSet qualifierSet);
HResult GetMethodQualifierSet([In, MarshalAs(UnmanagedType.LPWStr)] string method, [Out] out IWbemQualifierSet? qualifierSet);

[PreserveSig]
HResult GetMethodOrigin([In, MarshalAs(UnmanagedType.LPWStr)] string methodName, [Out, MarshalAs(UnmanagedType.BStr)] out string className);
}
HResult GetMethodOrigin([In, MarshalAs(UnmanagedType.LPWStr)] string methodName, [Out, MarshalAs(UnmanagedType.BStr)] out string? className);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ HResult Next(
[In]
uint count,
[Out]
out IWbemClassObject objects,
out IWbemClassObject? objects,
[Out]
out uint returnedCount);

Expand All @@ -38,4 +38,4 @@ HResult Skip(
int timeOut,
[In]
uint count);
}
}
10 changes: 5 additions & 5 deletions src/Orc.SystemInfo/Win32/Wbem/Interfaces/IWbemContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ HResult Next(
[In]
uint flags,
[Out, MarshalAs(UnmanagedType.BStr)]
out string name,
out string? name,
[Out]
out object value);
out object? value);

[PreserveSig]
HResult EndEnumeration();
Expand All @@ -43,7 +43,7 @@ HResult SetValue(
[In]
uint flags,
[In]
object value);
object? value);

[PreserveSig]
HResult GetValue(
Expand All @@ -52,7 +52,7 @@ HResult GetValue(
[In]
uint flags,
[Out]
out object value);
out object? value);

[PreserveSig]
HResult DeleteValue(
Expand All @@ -63,4 +63,4 @@ HResult DeleteValue(

[PreserveSig]
HResult DeleteAll();
}
}
4 changes: 2 additions & 2 deletions src/Orc.SystemInfo/Win32/Wbem/Interfaces/IWbemLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ HResult ConnectServer(
[In, MarshalAs(UnmanagedType.Interface)]
IWbemContext? ctx,
[Out, MarshalAs(UnmanagedType.Interface)]
out IWbemServices wbemServices);
}
out IWbemServices? wbemServices);
}
10 changes: 5 additions & 5 deletions src/Orc.SystemInfo/Win32/Wbem/Interfaces/IWbemQualifierSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ HResult Get(
[In]
int flags,
[Out]
out object value,
out object? value,
[Out]
out int flavor);

Expand All @@ -42,9 +42,9 @@ HResult Next(
[In]
int flags,
[Out, MarshalAs(UnmanagedType.BStr)]
out string name,
out string? name,
[Out]
out object value,
out object? value,
[Out]
out int flavor);

Expand All @@ -53,7 +53,7 @@ HResult Put(
[In, MarshalAs(UnmanagedType.LPWStr)]
string name,
[In]
object value,
object? value,
[In]
int flavor);
}
}
14 changes: 11 additions & 3 deletions src/Orc.SystemInfo/Wmi/WindowsManagementConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ public void Open()
const WbemAuthenticationLevel authLevel = WbemAuthenticationLevel.PacketIntegrity;

_wbemServices = locator.ConnectServer(DefaultLocalRootPath, _context);
_wbemServices.SetProxy(WbemImpersonationLevel.Impersonate, authLevel);

_connected = true;
if (_wbemServices is not null)
{
_wbemServices.SetProxy(WbemImpersonationLevel.Impersonate, authLevel);

_connected = true;
}
}
}
catch (Exception ex)
Expand All @@ -61,7 +65,11 @@ protected override void DisposeUnmanaged()
{
if (_wbemServices is not null)
{
Marshal.ReleaseComObject(_wbemServices);
if (Marshal.IsComObject(_wbemServices))
{
Marshal.ReleaseComObject(_wbemServices);
}

_wbemServices = null;
}

Expand Down
10 changes: 9 additions & 1 deletion src/Orc.SystemInfo/Wmi/WindowsManagementObjectEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ public bool MoveNext()
ThrowIfDisposed();

var currentWmiObject = _wbemClassObjectEnumerator.Next();
if (currentWmiObject is null)
{
return false;
}

#pragma warning disable IDISP003 // Dispose previous before re-assigning
Current = new WindowsManagementObject(currentWmiObject);
#pragma warning restore IDISP003 // Dispose previous before re-assigning

return true;
}

Expand All @@ -70,7 +75,10 @@ public void Dispose()
return;
}

Marshal.ReleaseComObject(_wbemClassObjectEnumerator);
if (Marshal.IsComObject(_wbemClassObjectEnumerator))
{
Marshal.ReleaseComObject(_wbemClassObjectEnumerator);
}

_disposed = true;
}
Expand Down

0 comments on commit 405ec99

Please sign in to comment.