Added nullability to WsmSvc and tests

nullableenabled
David Hall 2023-11-02 22:26:49 -06:00
parent 68fc80ed2e
commit e706e3a5aa
4 changed files with 3055 additions and 305 deletions

File diff suppressed because it is too large Load Diff

View File

@ -303,7 +303,7 @@ public static partial class WsmSvc
// connection, long flags, IDispatch *connectionOptions, IDispatch **session ); // connection, long flags, IDispatch *connectionOptions, IDispatch **session );
[PreserveSig, DispId(1)] [PreserveSig, DispId(1)]
HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags, HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags,
IWSManConnectionOptions connectionOptions, out IWSManSession session); IWSManConnectionOptions? connectionOptions, out IWSManSession session);
/// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary> /// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary>
/// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param> /// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param>
@ -809,7 +809,7 @@ public static partial class WsmSvc
// connection, long flags, IDispatch *connectionOptions, IDispatch **session ); // connection, long flags, IDispatch *connectionOptions, IDispatch **session );
[PreserveSig, DispId(1)] [PreserveSig, DispId(1)]
new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags, new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags,
IWSManConnectionOptions connectionOptions, out IWSManSession session); IWSManConnectionOptions? connectionOptions, out IWSManSession session);
/// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary> /// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary>
/// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param> /// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param>
@ -1247,7 +1247,7 @@ public static partial class WsmSvc
// connection, long flags, IDispatch *connectionOptions, IDispatch **session ); // connection, long flags, IDispatch *connectionOptions, IDispatch **session );
[PreserveSig, DispId(1)] [PreserveSig, DispId(1)]
new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags, new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags,
IWSManConnectionOptions connectionOptions, out IWSManSession session); IWSManConnectionOptions? connectionOptions, out IWSManSession session);
/// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary> /// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary>
/// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param> /// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param>
@ -1700,7 +1700,7 @@ public static partial class WsmSvc
// connection, long flags, IDispatch *connectionOptions, IDispatch **session ); // connection, long flags, IDispatch *connectionOptions, IDispatch **session );
[PreserveSig, DispId(1)] [PreserveSig, DispId(1)]
new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags, new HRESULT CreateSession([MarshalAs(UnmanagedType.BStr)] string connection, [Optional] WSManSessionFlags flags,
IWSManConnectionOptions connectionOptions, out IWSManSession session); IWSManConnectionOptions? connectionOptions, out IWSManSession session);
/// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary> /// <summary>Creates an IWSManConnectionOptions object that specifies the user name and password used when creating a session.</summary>
/// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param> /// <param name="connectionOptions">A pointer to a new IWSManConnectionOptions object.</param>
@ -2572,7 +2572,7 @@ public static partial class WsmSvc
// resourceUri, BSTR filter, BSTR dialect, long flags, IDispatch **resultSet ); // resourceUri, BSTR filter, BSTR dialect, long flags, IDispatch **resultSet );
[PreserveSig, DispId(6)] [PreserveSig, DispId(6)]
HRESULT Enumerate([In, MarshalAs(UnmanagedType.Struct)] object resourceUri, [MarshalAs(UnmanagedType.BStr)] string filter, HRESULT Enumerate([In, MarshalAs(UnmanagedType.Struct)] object resourceUri, [MarshalAs(UnmanagedType.BStr)] string filter,
[MarshalAs(UnmanagedType.BStr)] string dialect, WSManEnumFlags flags, out IWSManEnumerator resultSet); [MarshalAs(UnmanagedType.BStr)] string? dialect, WSManEnumFlags flags, out IWSManEnumerator resultSet);
/// <summary> /// <summary>
/// The <c>IWSManSession::Identify</c> method queries a remote computer to determine if it supports the WS-Management protocol. /// The <c>IWSManSession::Identify</c> method queries a remote computer to determine if it supports the WS-Management protocol.

2712
PInvoke/WsmSvc/wsmerror.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,17 +7,17 @@ namespace Vanara.PInvoke.Tests;
public class ShellClient : IDisposable public class ShellClient : IDisposable
{ {
private SafeWSMAN_API_HANDLE m_apiHandle; private SafeWSMAN_API_HANDLE? m_apiHandle;
private WSMAN_SHELL_ASYNC m_async; private WSMAN_SHELL_ASYNC m_async;
private bool m_bExecute; private bool m_bExecute;
private bool m_bSetup; private bool m_bSetup;
private WSMAN_COMMAND_HANDLE m_command; private WSMAN_COMMAND_HANDLE m_command;
private Win32Error m_errorCode; private Win32Error m_errorCode;
private SafeEventHandle m_event; private SafeEventHandle? m_event;
private WSMAN_SHELL_ASYNC m_ReceiveAsync; private WSMAN_SHELL_ASYNC m_ReceiveAsync;
private Win32Error m_ReceiveErrorCode; private Win32Error m_ReceiveErrorCode;
private SafeEventHandle m_ReceiveEvent; private SafeEventHandle? m_ReceiveEvent;
private SafeWSMAN_SESSION_HANDLE m_session; private SafeWSMAN_SESSION_HANDLE? m_session;
private WSMAN_SHELL_HANDLE m_shell; private WSMAN_SHELL_HANDLE m_shell;
// Constructor. // Constructor.
@ -26,12 +26,13 @@ public class ShellClient : IDisposable
} }
// Clean up the used resources // Clean up the used resources
public void Dispose() void IDisposable.Dispose()
{ {
if (!m_command.IsNull) if (!m_command.IsNull)
{ {
WSManCloseCommand(m_command, 0, m_async); WSManCloseCommand(m_command, 0, m_async);
WaitForSingleObject(m_event, INFINITE); if (m_event is not null && !m_event.IsClosed)
WaitForSingleObject(m_event, INFINITE);
if (Win32Error.NO_ERROR != m_errorCode) if (Win32Error.NO_ERROR != m_errorCode)
{ {
wprintf("WSManCloseCommand failed: {0}\n", m_errorCode); wprintf("WSManCloseCommand failed: {0}\n", m_errorCode);
@ -45,7 +46,8 @@ public class ShellClient : IDisposable
if (!m_shell.IsNull) if (!m_shell.IsNull)
{ {
WSManCloseShell(m_shell, 0, m_async); WSManCloseShell(m_shell, 0, m_async);
WaitForSingleObject(m_event, INFINITE); if (m_event is not null && !m_event.IsClosed)
WaitForSingleObject(m_event, INFINITE);
if (Win32Error.NO_ERROR != m_errorCode) if (Win32Error.NO_ERROR != m_errorCode)
{ {
wprintf("WSManCloseShell failed: {0}\n", m_errorCode); wprintf("WSManCloseShell failed: {0}\n", m_errorCode);
@ -67,10 +69,12 @@ public class ShellClient : IDisposable
m_bSetup = false; m_bSetup = false;
m_bExecute = false; m_bExecute = false;
GC.SuppressFinalize(this);
} }
// Execute shell-related operations // Execute shell-related operations
public bool Execute(string resourceUri, string commandLine, byte[] sendData, uint count) public bool Execute(string resourceUri, string commandLine, byte[]? sendData, uint count)
{ {
if (!m_bSetup) if (!m_bSetup)
{ {
@ -85,8 +89,9 @@ public class ShellClient : IDisposable
m_bExecute = true; m_bExecute = true;
// WSManCreateShell // WSManCreateShell
WSManCreateShell(m_session, 0, resourceUri, default, default, default, m_async, out m_shell); WSManCreateShell(m_session!, 0, resourceUri, default, default, default, m_async, out m_shell);
WaitForSingleObject(m_event, INFINITE); if (m_event is not null && !m_event.IsClosed)
WaitForSingleObject(m_event, INFINITE);
if (Win32Error.NO_ERROR != m_errorCode) if (Win32Error.NO_ERROR != m_errorCode)
{ {
wprintf("WSManCreateShell failed: {0}\n", m_errorCode); wprintf("WSManCreateShell failed: {0}\n", m_errorCode);
@ -95,7 +100,8 @@ public class ShellClient : IDisposable
// WSManRunShellCommand // WSManRunShellCommand
WSManRunShellCommand(m_shell, 0, commandLine, default, default, m_async, out m_command); WSManRunShellCommand(m_shell, 0, commandLine, default, default, m_async, out m_command);
WaitForSingleObject(m_event, INFINITE); if (m_event is not null && !m_event.IsClosed)
WaitForSingleObject(m_event, INFINITE);
if (Win32Error.NO_ERROR != m_errorCode) if (Win32Error.NO_ERROR != m_errorCode)
{ {
wprintf("WSManRunShellCommand failed: {0}\n", m_errorCode); wprintf("WSManRunShellCommand failed: {0}\n", m_errorCode);
@ -119,7 +125,8 @@ public class ShellClient : IDisposable
} }
// Receive operation is finished // Receive operation is finished
WaitForSingleObject(m_ReceiveEvent, INFINITE); if (m_ReceiveEvent is not null && !m_ReceiveEvent.IsClosed)
WaitForSingleObject(m_ReceiveEvent, INFINITE);
if (Win32Error.NO_ERROR != m_ReceiveErrorCode) if (Win32Error.NO_ERROR != m_ReceiveErrorCode)
{ {
wprintf("WSManReceiveShellOutput failed: {0}\n", m_ReceiveErrorCode); wprintf("WSManReceiveShellOutput failed: {0}\n", m_ReceiveErrorCode);
@ -131,7 +138,7 @@ public class ShellClient : IDisposable
} }
// Initialize session for subsequent operations // Initialize session for subsequent operations
public bool Setup(string connection, WSManAuthenticationFlags authenticationMechanism, string username, string password) public bool Setup(string connection, WSManAuthenticationFlags authenticationMechanism, string? username, string? password)
{ {
if (m_bSetup) return true; if (m_bSetup) return true;
@ -216,7 +223,7 @@ public class ShellClient : IDisposable
m_ReceiveErrorCode = error.code; m_ReceiveErrorCode = error.code;
// NOTE: if the errorDetail needs to be used outside of the callback, then need to allocate memory, copy the content to that // NOTE: if the errorDetail needs to be used outside of the callback, then need to allocate memory, copy the content to that
// memory as error.errorDetail itself is owned by WSMan client stack and will be deallocated and invalid when the callback exits // memory as error.errorDetail itself is owned by WSMan client stack and will be deallocated and invalid when the callback exits
wprintf(error.errorDetail); wprintf(error.errorDetail ?? "");
} }
// Output the received data to the console // Output the received data to the console
@ -233,7 +240,7 @@ public class ShellClient : IDisposable
// for WSManReceiveShellOutput, needs to wait for state to be done before signalliing the end of the operation // for WSManReceiveShellOutput, needs to wait for state to be done before signalliing the end of the operation
if ((0 != error.code) || (pdata != default && !data.commandState.IsNull && string.Compare(data.commandState, WSMAN_COMMAND_STATE_DONE) == 0)) if ((0 != error.code) || (pdata != default && !data.commandState.IsNull && string.Compare(data.commandState, WSMAN_COMMAND_STATE_DONE) == 0))
{ {
SetEvent(m_ReceiveEvent); m_ReceiveEvent?.Set();
} }
} }
@ -246,14 +253,14 @@ public class ShellClient : IDisposable
m_errorCode = error.code; m_errorCode = error.code;
// NOTE: if the errorDetail needs to be used outside of the callback, then need to allocate memory, copy the content to that // NOTE: if the errorDetail needs to be used outside of the callback, then need to allocate memory, copy the content to that
// memory as error->errorDetail itself is owned by WSMan client stack and will be deallocated and invalid when the callback exits // memory as error->errorDetail itself is owned by WSMan client stack and will be deallocated and invalid when the callback exits
wprintf(error.errorDetail); wprintf(error.errorDetail ?? "");
} }
// for non-receieve operation, the callback simply signals the async operation is finished // for non-receieve operation, the callback simply signals the async operation is finished
SetEvent(m_event); m_event?.Set();
} }
private bool Send(byte[] sendData, bool endOfStream) private bool Send(byte[]? sendData, bool endOfStream)
{ {
// WSManSendShellInput // WSManSendShellInput
var streamData = new WSMAN_DATA var streamData = new WSMAN_DATA
@ -268,7 +275,8 @@ public class ShellClient : IDisposable
streamData.union.binaryData.data = pSendData; streamData.union.binaryData.data = pSendData;
} }
WSManSendShellInput(m_shell, m_command, 0, WSMAN_STREAM_ID_STDIN, streamData, endOfStream, m_async, out SafeWSMAN_OPERATION_HANDLE sendOperation); WSManSendShellInput(m_shell, m_command, 0, WSMAN_STREAM_ID_STDIN, streamData, endOfStream, m_async, out SafeWSMAN_OPERATION_HANDLE sendOperation);
WaitForSingleObject(m_event, INFINITE); if (m_event is not null && !m_event.IsClosed)
WaitForSingleObject(m_event, INFINITE);
if (Win32Error.NO_ERROR != m_errorCode) if (Win32Error.NO_ERROR != m_errorCode)
{ {
wprintf("WSManSendShellInput failed: {0}\n", m_errorCode); wprintf("WSManSendShellInput failed: {0}\n", m_errorCode);
@ -279,7 +287,7 @@ public class ShellClient : IDisposable
return true; return true;
} }
private void wprintf(string fmt, params object[] p) => TestContext.Write(fmt, p); private static void wprintf(string fmt, params object[] p) => TestContext.Write(fmt, p);
} }
[TestFixture] [TestFixture]