Skip to content

Commit 240570b

Browse files
committed
Add in C#-based method to retrieve domain information as part of CIM code removal.
1 parent 07ac396 commit 240570b

7 files changed

Lines changed: 191 additions & 1 deletion

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace PSADT.LibraryInterfaces
2+
{
3+
/// <summary>
4+
/// Specifies the status of a computer's membership in a workgroup or domain.
5+
/// </summary>
6+
/// <remarks>This enumeration is typically used to indicate whether a computer is joined to a domain, a
7+
/// workgroup, or is unjoined. The values correspond to the possible states returned by network management APIs when
8+
/// querying the join status of a system.</remarks>
9+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "This is named as per the Win32 API.")]
10+
public enum NETSETUP_JOIN_STATUS
11+
{
12+
/// <summary>
13+
/// Domain join status of the machine is unknown.
14+
/// </summary>
15+
NetSetupUnknownStatus = Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS.NetSetupUnknownStatus,
16+
17+
/// <summary>
18+
/// Machine is not joined to a domain or to a workgroup.
19+
/// </summary>
20+
NetSetupUnjoined = Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS.NetSetupUnjoined,
21+
22+
/// <summary>
23+
/// Machine is joined to a workgroup.
24+
/// </summary>
25+
NetSetupWorkgroupName = Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS.NetSetupWorkgroupName,
26+
27+
/// <summary>
28+
/// Machine is joined to a domain.
29+
/// </summary>
30+
NetSetupDomainName = Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS.NetSetupDomainName,
31+
}
32+
}

src/PSADT/PSADT.LibraryInterfaces/NativeMethods.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ MsiGetLastErrorRecord
155155
MsiGetSummaryInformation
156156
MsiOpenDatabase
157157
MsiSummaryInfoGetProperty
158+
NERR_Success
159+
NetApiBufferFree
160+
NetGetJoinInformation
161+
NETSETUP_JOIN_STATUS
158162
NtQueryInformationProcess
159163
NtQueryObject
160164
NtQuerySystemInformation
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using PSADT.LibraryInterfaces.SafeHandles;
3+
using PSADT.LibraryInterfaces.Utilities;
4+
using Windows.Win32;
5+
using Windows.Win32.Foundation;
6+
7+
namespace PSADT.LibraryInterfaces
8+
{
9+
/// <summary>
10+
/// Provides interop methods for querying network join information on Windows systems.
11+
/// </summary>
12+
/// <remarks>This class contains static methods that wrap native Windows networking APIs. It is intended
13+
/// for internal use and is not thread-safe. Callers are responsible for managing any unmanaged resources returned
14+
/// by these methods, such as releasing buffer handles to prevent memory leaks.</remarks>
15+
internal static class NetApi32
16+
{
17+
/// <summary>
18+
/// Retrieves the join status and name of the domain or workgroup for the specified computer.
19+
/// </summary>
20+
/// <remarks>The caller must release the buffer referenced by lpNameBuffer to avoid memory leaks.
21+
/// This method throws an exception if the underlying Windows API call fails.</remarks>
22+
/// <param name="lpServer">The name of the remote server to query, or null to specify the local computer. The name must begin with \\
23+
/// if specified.</param>
24+
/// <param name="lpNameBuffer">When this method returns, contains a handle to a buffer that receives the name of the domain or workgroup.
25+
/// The caller is responsible for releasing this handle.</param>
26+
/// <param name="BufferType">When this method returns, contains a value that indicates the join status of the computer.</param>
27+
/// <returns>A WIN32_ERROR value that indicates the result of the operation. Returns NERR_Success if successful.</returns>
28+
internal static WIN32_ERROR NetGetJoinInformation(string? lpServer, out SafeNetApiBufferFreeHandle lpNameBuffer, out Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS BufferType)
29+
{
30+
WIN32_ERROR res = (WIN32_ERROR)PInvoke.NetGetJoinInformation(lpServer, out PWSTR lpNameBufferLocal, out BufferType);
31+
if (res != PInvoke.NERR_Success)
32+
{
33+
throw ExceptionUtilities.GetExceptionForLastWin32Error(res);
34+
}
35+
unsafe
36+
{
37+
lpNameBuffer = new((IntPtr)lpNameBufferLocal.Value, lpNameBufferLocal.Length, true);
38+
}
39+
return res;
40+
}
41+
42+
/// <summary>
43+
/// Retrieves information about the join status of the local computer to a domain or workgroup.
44+
/// </summary>
45+
/// <param name="lpNameBuffer">When this method returns, contains a handle to a buffer that receives the name of the domain or workgroup.
46+
/// The caller is responsible for freeing this buffer.</param>
47+
/// <param name="BufferType">When this method returns, contains a value that specifies the join status of the local computer.</param>
48+
/// <returns>A WIN32_ERROR value that indicates the result of the operation. Returns ERROR_SUCCESS if the information is
49+
/// retrieved successfully; otherwise, returns a system error code.</returns>
50+
internal static WIN32_ERROR NetGetJoinInformation(out SafeNetApiBufferFreeHandle lpNameBuffer, out Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS BufferType)
51+
{
52+
return NetGetJoinInformation(null, out lpNameBuffer, out BufferType);
53+
}
54+
}
55+
}

src/PSADT/PSADT.LibraryInterfaces/SafeHandles/SafeMemoryHandle.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ internal TSelf FromStructure<T>(T structure, bool fDeleteOld, int offset = 0) wh
6565
internal string? ToStringUni(int offset = 0)
6666
{
6767
ConfirmStateValidity(offset);
68-
return Marshal.PtrToStringUni(handle + offset);
68+
string? ret = Marshal.PtrToStringUni(handle + offset);
69+
return !string.IsNullOrWhiteSpace(ret) ? ret : null;
6970
}
7071

7172
/// <summary>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using PSADT.LibraryInterfaces.Utilities;
3+
using Windows.Win32;
4+
using Windows.Win32.Foundation;
5+
6+
namespace PSADT.LibraryInterfaces.SafeHandles
7+
{
8+
/// <summary>
9+
/// Provides a safe handle for buffers allocated by Windows network management APIs that must be freed using
10+
/// NetApiBufferFree.
11+
/// </summary>
12+
/// <remarks>This handle ensures that the associated unmanaged memory is released using NetApiBufferFree
13+
/// when the handle is disposed or finalized. Use this class to safely manage buffers returned by Windows network
14+
/// management functions that require explicit deallocation.</remarks>
15+
/// <param name="handle">The pointer to the unmanaged buffer to be managed by the handle.</param>
16+
/// <param name="length">The length, in bytes, of the buffer referenced by the handle.</param>
17+
/// <param name="ownsHandle">true to reliably release the handle during finalization; otherwise, false.</param>
18+
internal sealed class SafeNetApiBufferFreeHandle(IntPtr handle, int length, bool ownsHandle) : SafeMemoryHandle<SafeNetApiBufferFreeHandle>(handle, length, ownsHandle)
19+
{
20+
/// <summary>
21+
/// Releases the handle associated with the unmanaged resource.
22+
/// </summary>
23+
/// <remarks>This method is called by the runtime to free the unmanaged memory buffer when the
24+
/// handle is no longer needed. If the handle is already invalid or zero, the method returns true without
25+
/// performing any operation. An exception is thrown if the underlying buffer cannot be freed
26+
/// successfully.</remarks>
27+
/// <returns>true if the handle is released successfully; otherwise, false.</returns>
28+
protected override bool ReleaseHandle()
29+
{
30+
if (handle == default || IntPtr.Zero == handle)
31+
{
32+
return true;
33+
}
34+
WIN32_ERROR res;
35+
unsafe
36+
{
37+
res = (WIN32_ERROR)PInvoke.NetApiBufferFree((void*)handle);
38+
}
39+
if (res != PInvoke.NERR_Success)
40+
{
41+
throw ExceptionUtilities.GetExceptionForLastWin32Error(res);
42+
}
43+
handle = default;
44+
return true;
45+
}
46+
}
47+
}

src/PSADT/PSADT/DeviceManagement/DeviceUtilities.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using PSADT.LibraryInterfaces;
44
using PSADT.LibraryInterfaces.Extensions;
5+
using PSADT.LibraryInterfaces.SafeHandles;
56
using PSADT.ProcessManagement;
67
using Windows.Win32;
78
using Windows.Win32.Foundation;
@@ -88,5 +89,19 @@ public static DateTime GetSystemBootTime()
8889
{
8990
return DateTime.Now - GetSystemUptime();
9091
}
92+
93+
/// <summary>
94+
/// Retrieves the current domain join status and associated domain or workgroup name of the local computer.
95+
/// </summary>
96+
/// <returns>A <see cref="DomainStatus"/> object containing the join status and the name of the domain or workgroup the
97+
/// computer is joined to.</returns>
98+
public static DomainStatus GetDomainStatus()
99+
{
100+
_ = NetApi32.NetGetJoinInformation(out SafeNetApiBufferFreeHandle? nameBuffer, out Windows.Win32.NetworkManagement.NetManagement.NETSETUP_JOIN_STATUS bufferType);
101+
using (nameBuffer)
102+
{
103+
return new((NETSETUP_JOIN_STATUS)bufferType, nameBuffer.ToStringUni());
104+
}
105+
}
91106
}
92107
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using PSADT.LibraryInterfaces;
2+
3+
namespace PSADT.DeviceManagement
4+
{
5+
/// <summary>
6+
/// Represents the domain or workgroup join status of a computer, including the associated domain or workgroup name
7+
/// if applicable.
8+
/// </summary>
9+
/// <remarks>Use this type to determine whether a computer is joined to a domain, a workgroup, or is
10+
/// unjoined, and to retrieve the corresponding domain or workgroup name when available. This record is
11+
/// immutable.</remarks>
12+
public sealed record DomainStatus
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the DomainStatus class with the specified join status and domain or workgroup
16+
/// name.
17+
/// </summary>
18+
/// <param name="joinStatus">The status indicating whether the computer is joined to a domain, a workgroup, or is unjoined.</param>
19+
/// <param name="domainOrWorkgroupName">The name of the domain or workgroup associated with the current join status, or null if not applicable.</param>
20+
public DomainStatus(NETSETUP_JOIN_STATUS joinStatus, string? domainOrWorkgroupName)
21+
{
22+
JoinStatus = joinStatus;
23+
DomainOrWorkgroupName = domainOrWorkgroupName;
24+
}
25+
26+
/// <summary>
27+
/// Gets the status of the computer's domain or workgroup join operation.
28+
/// </summary>
29+
public NETSETUP_JOIN_STATUS JoinStatus { get; }
30+
31+
/// <summary>
32+
/// Gets the name of the domain or workgroup to which the computer belongs.
33+
/// </summary>
34+
public string? DomainOrWorkgroupName { get; }
35+
}
36+
}

0 commit comments

Comments
 (0)