Added nullability to PortableDeviceApi and tests

nullableenabled
David Hall 2023-09-24 20:16:38 -06:00
parent 718d241007
commit 99e7fa0a78
6 changed files with 8884 additions and 8864 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -3,265 +3,265 @@ using static Vanara.PInvoke.SetupAPI;
namespace Vanara.PInvoke;
public static partial class PortableDeviceApi
{
/// <summary>
/// The <c>IConnectionRequestCallback</c> interface defines a single callback method. A Windows Portable Devices (WPD) application
/// implements this optional Component Object Model (COM) interface to receive notifications about completed requests and to cancel
/// pending requests. The requests are sent using the <c>IPortableDeviceConnector::Connect</c> and
/// <c>IPortableDeviceConnector::Disconnect</c> methods.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/iconnectionrequestcallback
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("272C9AE0-7161-4AE0-91BD-9F448EE9C427"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IConnectionRequestCallback
{
/// <summary>
/// The <c>OnComplete</c> method notifies an application that a previously scheduled Connect or Disconnect request to the
/// MTP/Bluetooth device has completed
/// </summary>
/// <param name="hrStatus">The status of the request to connect or disconnect a given device.</param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// </list>
/// </returns>
/// <remarks>
/// <para>
/// An application implements the <c>IConnectionRequestCallback</c> interface to receive notifications about completed requests
/// and to cancel pending requests.
/// </para>
/// <para>
/// Windows Portable Devices (WPD) calls this method to notify an application that a previously scheduled request has completed.
/// Each request can be tracked and canceled by its application-supplied callback. Therefore, if the application needs to send
/// multiple requests at the same time using the same <c>IPortableDeviceConnector</c> object, each request should be passed a
/// unique <c>IConnectionRequestCallback</c> object as the input parameter to the <c>IPortableDeviceConnector::Connect</c> and
/// <c>IPortableDeviceConnector::Disconnect</c> methods.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/iconnectionrequestcallback-oncomplete HRESULT OnComplete( [in] HRESULT
// hrStatus );
[PreserveSig]
HRESULT OnComplete(HRESULT hrStatus);
}
{
/// <summary>
/// The <c>IConnectionRequestCallback</c> interface defines a single callback method. A Windows Portable Devices (WPD) application
/// implements this optional Component Object Model (COM) interface to receive notifications about completed requests and to cancel
/// pending requests. The requests are sent using the <c>IPortableDeviceConnector::Connect</c> and
/// <c>IPortableDeviceConnector::Disconnect</c> methods.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/iconnectionrequestcallback
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("272C9AE0-7161-4AE0-91BD-9F448EE9C427"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IConnectionRequestCallback
{
/// <summary>
/// The <c>OnComplete</c> method notifies an application that a previously scheduled Connect or Disconnect request to the
/// MTP/Bluetooth device has completed
/// </summary>
/// <param name="hrStatus">The status of the request to connect or disconnect a given device.</param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// </list>
/// </returns>
/// <remarks>
/// <para>
/// An application implements the <c>IConnectionRequestCallback</c> interface to receive notifications about completed requests
/// and to cancel pending requests.
/// </para>
/// <para>
/// Windows Portable Devices (WPD) calls this method to notify an application that a previously scheduled request has completed.
/// Each request can be tracked and canceled by its application-supplied callback. Therefore, if the application needs to send
/// multiple requests at the same time using the same <c>IPortableDeviceConnector</c> object, each request should be passed a
/// unique <c>IConnectionRequestCallback</c> object as the input parameter to the <c>IPortableDeviceConnector::Connect</c> and
/// <c>IPortableDeviceConnector::Disconnect</c> methods.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/iconnectionrequestcallback-oncomplete HRESULT OnComplete( [in] HRESULT
// hrStatus );
[PreserveSig]
HRESULT OnComplete(HRESULT hrStatus);
}
/// <summary>
/// The <c>IEnumPortableDeviceConnectors</c> interface supports the enumeration of <c>IPortableDeviceConnector</c> interfaces,
/// representing MTP/Bluetooth devices that were paired with the PC. Note that this will retrieve all previously-paired devices,
/// including devices that are paired but disconnected. To determine if a device is still connected, query the
/// <c>DEVPKEY_MTPBTH_IsConnected</c> property for that device.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("BFDEF549-9247-454F-BD82-06FE80853FAA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(EnumBthMtpConnectors))]
public interface IEnumPortableDeviceConnectors : Vanara.Collections.ICOMEnum<IPortableDeviceConnector>
{
/// <summary>
/// The <c>Next</c> method retrieves the next one or more <c>IPortableDeviceConnector</c> objects in the enumeration sequence.
/// </summary>
/// <param name="cRequested">
/// The number of requested devices. This value also indicates the number of elements in the caller-allocated array specified by
/// the pConnectors parameter.
/// </param>
/// <param name="pConnectors">
/// An array of <c>IPortableDeviceConnector</c> pointers, each specifying a paired MTP Bluetooth device. The caller must
/// allocate an array of <c>IPortableDeviceConnector</c> pointers, with the array length specified by the cRequested parameter.
/// On successful return, the caller must free both the array and the returned pointers. The <c>IPortableDeviceConnector</c>
/// interfaces are freed by calling the <c>IUnknown::Release</c> method.
/// </param>
/// <param name="pcFetched">
/// The number of <c>IPortableDeviceConnector</c> interfaces that are actually retrieved. If no <c>IPortableDeviceConnector</c>
/// interfaces are retrieved and the return value is <c>S_FALSE</c>, there are no more <c>IPortableDeviceConnector</c>
/// interfaces to enumerate.
/// </param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// <item>
/// <term>S_FALSE</term>
/// <term>There are no more MTP Bluetooth devices to enumerate.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-next HRESULT Next( [in] UINT32
// cRequested, [out] IPortableDeviceConnector **pConnectors, [in, out] UINT32 *pcFetched );
[PreserveSig]
HRESULT Next(uint cRequested, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)] IPortableDeviceConnector[] pConnectors,
[In, Out] ref uint pcFetched);
/// <summary>
/// The <c>IEnumPortableDeviceConnectors</c> interface supports the enumeration of <c>IPortableDeviceConnector</c> interfaces,
/// representing MTP/Bluetooth devices that were paired with the PC. Note that this will retrieve all previously-paired devices,
/// including devices that are paired but disconnected. To determine if a device is still connected, query the
/// <c>DEVPKEY_MTPBTH_IsConnected</c> property for that device.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("BFDEF549-9247-454F-BD82-06FE80853FAA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(EnumBthMtpConnectors))]
public interface IEnumPortableDeviceConnectors : Vanara.Collections.ICOMEnum<IPortableDeviceConnector>
{
/// <summary>
/// The <c>Next</c> method retrieves the next one or more <c>IPortableDeviceConnector</c> objects in the enumeration sequence.
/// </summary>
/// <param name="cRequested">
/// The number of requested devices. This value also indicates the number of elements in the caller-allocated array specified by
/// the pConnectors parameter.
/// </param>
/// <param name="pConnectors">
/// An array of <c>IPortableDeviceConnector</c> pointers, each specifying a paired MTP Bluetooth device. The caller must
/// allocate an array of <c>IPortableDeviceConnector</c> pointers, with the array length specified by the cRequested parameter.
/// On successful return, the caller must free both the array and the returned pointers. The <c>IPortableDeviceConnector</c>
/// interfaces are freed by calling the <c>IUnknown::Release</c> method.
/// </param>
/// <param name="pcFetched">
/// The number of <c>IPortableDeviceConnector</c> interfaces that are actually retrieved. If no <c>IPortableDeviceConnector</c>
/// interfaces are retrieved and the return value is <c>S_FALSE</c>, there are no more <c>IPortableDeviceConnector</c>
/// interfaces to enumerate.
/// </param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// <item>
/// <term>S_FALSE</term>
/// <term>There are no more MTP Bluetooth devices to enumerate.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-next HRESULT Next( [in] UINT32
// cRequested, [out] IPortableDeviceConnector **pConnectors, [in, out] UINT32 *pcFetched );
[PreserveSig]
HRESULT Next(uint cRequested, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)] IPortableDeviceConnector[] pConnectors,
[In, Out] ref uint pcFetched);
/// <summary>The <c>Skip</c> method skips the specified number of devices in the enumeration sequence.</summary>
/// <param name="cConnectors">The number of devices to skip.</param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// <item>
/// <term>S_FALSE</term>
/// <term>
/// The specified number of devices could not be skipped. One possible cause: The cConnectors parameter specifies more devices
/// than actually remain in the enumeration sequence.
/// </term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-skip HRESULT Skip( [in] UINT32
// cConnectors );
[PreserveSig]
HRESULT Skip(uint cConnectors);
/// <summary>The <c>Skip</c> method skips the specified number of devices in the enumeration sequence.</summary>
/// <param name="cConnectors">The number of devices to skip.</param>
/// <returns>
/// <para>The method returns an <c>HRESULT</c>. Possible values include, but are not limited to, those in the following table.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>The method succeeded.</term>
/// </item>
/// <item>
/// <term>S_FALSE</term>
/// <term>
/// The specified number of devices could not be skipped. One possible cause: The cConnectors parameter specifies more devices
/// than actually remain in the enumeration sequence.
/// </term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-skip HRESULT Skip( [in] UINT32
// cConnectors );
[PreserveSig]
HRESULT Skip(uint cConnectors);
/// <summary>The <c>Reset</c> method resets the enumeration sequence to the beginning.</summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-reset HRESULT Reset();
void Reset();
/// <summary>The <c>Reset</c> method resets the enumeration sequence to the beginning.</summary>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-reset HRESULT Reset();
void Reset();
/// <summary>The <c>Clone</c> method creates a copy of the current <c>IEnumPortableDeviceConnectors</c> interface.</summary>
/// <returns>
/// The address of a pointer to an <c>IEnumPortableDeviceConnectors</c> interface. The calling application must release this
/// interface when it is done with it.
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-clone HRESULT Clone( [out]
// IEnumPortableDeviceConnectors **ppEnum );
IEnumPortableDeviceConnectors Clone();
}
/// <summary>The <c>Clone</c> method creates a copy of the current <c>IEnumPortableDeviceConnectors</c> interface.</summary>
/// <returns>
/// The address of a pointer to an <c>IEnumPortableDeviceConnectors</c> interface. The calling application must release this
/// interface when it is done with it.
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/wpd_sdk/ienumportabledeviceconnectors-clone HRESULT Clone( [out]
// IEnumPortableDeviceConnectors **ppEnum );
IEnumPortableDeviceConnectors Clone();
}
/// <summary>
/// The <c>IPortableDeviceConnector</c> interface defines methods used for connection-management and property-retrieval for a paired
/// MTP/Bluetooth device.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nn-portabledeviceconnectapi-iportabledeviceconnector
[PInvokeData("portabledeviceconnectapi.h", MSDNShortId = "NN:portabledeviceconnectapi.IPortableDeviceConnector")]
[ComImport, Guid("625E2DF8-6392-4CF0-9AD1-3CFA5F17775C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPortableDeviceConnector
{
/// <summary>The <c>Connect</c> method sends an asynchronous connection request to the MTP/Bluetooth device.</summary>
/// <param name="pCallback">
/// A pointer to a IConnectionRequestCallback interface if the application wishes to be notified when the request is complete;
/// otherwise, <c>NULL</c>. If multiple requests are being sent simultaneously using the same IPortableDeviceConnector object, a
/// different instance of the callback object must be used.
/// </param>
/// <remarks>
/// <para>
/// This method will queue a connect request and then return immediately. The connection request will result in a no-op if a
/// device is already connected.
/// </para>
/// <para>
/// To be notified when the request is complete, applications should provide a pointer to the IConnectionRequestCallback interface.
/// </para>
/// <para>
/// If a previously paired MTP/Bluetooth device is within range, applications can call this method to instantiate the Windows
/// Portable Devices (WPD) class driver stack for that device, so that the device can be communicated to using the WPD API.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-connect
// HRESULT Connect( [in, optional] IConnectionRequestCallback *pCallback );
void Connect([Optional] IConnectionRequestCallback pCallback);
/// <summary>
/// The <c>IPortableDeviceConnector</c> interface defines methods used for connection-management and property-retrieval for a paired
/// MTP/Bluetooth device.
/// </summary>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nn-portabledeviceconnectapi-iportabledeviceconnector
[PInvokeData("portabledeviceconnectapi.h", MSDNShortId = "NN:portabledeviceconnectapi.IPortableDeviceConnector")]
[ComImport, Guid("625E2DF8-6392-4CF0-9AD1-3CFA5F17775C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPortableDeviceConnector
{
/// <summary>The <c>Connect</c> method sends an asynchronous connection request to the MTP/Bluetooth device.</summary>
/// <param name="pCallback">
/// A pointer to a IConnectionRequestCallback interface if the application wishes to be notified when the request is complete;
/// otherwise, <c>NULL</c>. If multiple requests are being sent simultaneously using the same IPortableDeviceConnector object, a
/// different instance of the callback object must be used.
/// </param>
/// <remarks>
/// <para>
/// This method will queue a connect request and then return immediately. The connection request will result in a no-op if a
/// device is already connected.
/// </para>
/// <para>
/// To be notified when the request is complete, applications should provide a pointer to the IConnectionRequestCallback interface.
/// </para>
/// <para>
/// If a previously paired MTP/Bluetooth device is within range, applications can call this method to instantiate the Windows
/// Portable Devices (WPD) class driver stack for that device, so that the device can be communicated to using the WPD API.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-connect
// HRESULT Connect( [in, optional] IConnectionRequestCallback? *pCallback );
void Connect([Optional] IConnectionRequestCallback? pCallback);
/// <summary>The <c>Disconnect</c> method sends an asynchronous disconnect request to the MTP/Bluetooth device.</summary>
/// <param name="pCallback">A pointer to an IConnectionRequestCallback interface.</param>
/// <remarks>
/// <para>This method will queue a disconnect request and then return immediately.</para>
/// <para>
/// To be notified when the request is complete, applications should provide a pointer to the IConnectionRequestCallback
/// interface. This will disconnect the MTP/Bluetooth link and remove the Windows Portable Devices (WPD) class driver stack for
/// that device.
/// </para>
/// <para>Once the disconnection completes, the WPD API will no longer enumerate this device.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-disconnect
// HRESULT Disconnect( [in] IConnectionRequestCallback *pCallback );
void Disconnect(IConnectionRequestCallback pCallback);
/// <summary>The <c>Disconnect</c> method sends an asynchronous disconnect request to the MTP/Bluetooth device.</summary>
/// <param name="pCallback">A pointer to an IConnectionRequestCallback interface.</param>
/// <remarks>
/// <para>This method will queue a disconnect request and then return immediately.</para>
/// <para>
/// To be notified when the request is complete, applications should provide a pointer to the IConnectionRequestCallback
/// interface. This will disconnect the MTP/Bluetooth link and remove the Windows Portable Devices (WPD) class driver stack for
/// that device.
/// </para>
/// <para>Once the disconnection completes, the WPD API will no longer enumerate this device.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-disconnect
// HRESULT Disconnect( [in] IConnectionRequestCallback *pCallback );
void Disconnect(IConnectionRequestCallback pCallback);
/// <summary>
/// The <c>Cancel</c> method cancels a pending request to connect or disconnect an MTP/Bluetooth device. The callback object is
/// used to identify the request. This method returns immediately, and the request will be cancelled asynchronously.
/// </summary>
/// <param name="pCallback">A pointer to an IConnectionRequestCallback interface. This value cannot be <c>NULL</c>.</param>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-cancel
// HRESULT Cancel( [in] IConnectionRequestCallback *pCallback );
void Cancel(IConnectionRequestCallback pCallback);
/// <summary>
/// The <c>Cancel</c> method cancels a pending request to connect or disconnect an MTP/Bluetooth device. The callback object is
/// used to identify the request. This method returns immediately, and the request will be cancelled asynchronously.
/// </summary>
/// <param name="pCallback">A pointer to an IConnectionRequestCallback interface. This value cannot be <c>NULL</c>.</param>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-cancel
// HRESULT Cancel( [in] IConnectionRequestCallback *pCallback );
void Cancel(IConnectionRequestCallback pCallback);
/// <summary>The <c>GetProperty</c> method retrieves a property for the given MTP/Bluetooth Bus Enumerator device.</summary>
/// <param name="pPropertyKey">A pointer to a property key for the requested property.</param>
/// <param name="pPropertyType">A pointer to a property type.</param>
/// <param name="ppData">The address of a pointer to the property data.</param>
/// <param name="pcbData">A pointer to the size (in bytes) of the property data.</param>
/// <remarks>
/// <para>
/// The properties retrieved by this method are set on the device node. An example property key is
/// <c>DEVPKEY_MTPBTH_IsConnected</c>, which indicates whether the device is currently connected.
/// </para>
/// <para>
/// Valid values for the pPropertyType parameter are system-defined base data types of the unified device property model. The
/// data-type names start with the prefix <c>DEVPROP_TYPE_</c>.
/// </para>
/// <para>
/// Once the application no longer needs the property data specified by the ppData parameter, it must call <c>CoTaskMemAlloc</c>
/// to free this data.
/// </para>
/// <para>Examples</para>
/// <para>The following example shows how to read the DEVPKEY_MTPBTH_IsConnected property for a paired MTP/Bluetooth device.</para>
/// <para>
/// <code>#include &lt;devpkey.h&gt; #include &lt;PortableDeviceConnectAPI.h&gt; HRESULT IsDeviceConnected( __in IPortableDeviceConnector* pDevice, __out BOOL* pIsConnected) { DEVPROPTYPE typeGet; BYTE* pDataGet; UINT32 cbDataGet; *pbIsConnected = FALSE; HRESULT hr = pDevice -&gt;GetProperty(&amp;DEVPKEY_MTPBTH_IsConnected, &amp;typeGet, &amp;pDataGet, &amp;cbDataGet); if (SUCCEEDED(hr)) { DEVPROP_BOOLEAN bIsConnected = *((DEVPROP_BOOLEAN*)pDataGet); if (bIsConnected == DEVPROP_TRUE) { * pIsConnected = TRUE; } // Release memory allocated by GetProperty CoTaskMemFree(pDataGet); } return hr; }</code>
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-getproperty
// HRESULT GetProperty( [in] const DEVPROPKEY *pPropertyKey, [out] DEVPROPTYPE *pPropertyType, [out] BYTE **ppData, [out] UINT32
// *pcbData );
void GetProperty(in DEVPROPKEY pPropertyKey, out DEVPROPTYPE pPropertyType, out SafeCoTaskMemHandle ppData, out uint pcbData);
/// <summary>The <c>GetProperty</c> method retrieves a property for the given MTP/Bluetooth Bus Enumerator device.</summary>
/// <param name="pPropertyKey">A pointer to a property key for the requested property.</param>
/// <param name="pPropertyType">A pointer to a property type.</param>
/// <param name="ppData">The address of a pointer to the property data.</param>
/// <param name="pcbData">A pointer to the size (in bytes) of the property data.</param>
/// <remarks>
/// <para>
/// The properties retrieved by this method are set on the device node. An example property key is
/// <c>DEVPKEY_MTPBTH_IsConnected</c>, which indicates whether the device is currently connected.
/// </para>
/// <para>
/// Valid values for the pPropertyType parameter are system-defined base data types of the unified device property model. The
/// data-type names start with the prefix <c>DEVPROP_TYPE_</c>.
/// </para>
/// <para>
/// Once the application no longer needs the property data specified by the ppData parameter, it must call <c>CoTaskMemAlloc</c>
/// to free this data.
/// </para>
/// <para>Examples</para>
/// <para>The following example shows how to read the DEVPKEY_MTPBTH_IsConnected property for a paired MTP/Bluetooth device.</para>
/// <para>
/// <code>#include &lt;devpkey.h&gt; #include &lt;PortableDeviceConnectAPI.h&gt; HRESULT IsDeviceConnected( __in IPortableDeviceConnector* pDevice, __out BOOL* pIsConnected) { DEVPROPTYPE typeGet; BYTE* pDataGet; UINT32 cbDataGet; *pbIsConnected = FALSE; HRESULT hr = pDevice -&gt;GetProperty(&amp;DEVPKEY_MTPBTH_IsConnected, &amp;typeGet, &amp;pDataGet, &amp;cbDataGet); if (SUCCEEDED(hr)) { DEVPROP_BOOLEAN bIsConnected = *((DEVPROP_BOOLEAN*)pDataGet); if (bIsConnected == DEVPROP_TRUE) { * pIsConnected = TRUE; } // Release memory allocated by GetProperty CoTaskMemFree(pDataGet); } return hr; }</code>
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-getproperty
// HRESULT GetProperty( [in] const DEVPROPKEY *pPropertyKey, [out] DEVPROPTYPE *pPropertyType, [out] BYTE **ppData, [out] UINT32
// *pcbData );
void GetProperty(in DEVPROPKEY pPropertyKey, out DEVPROPTYPE pPropertyType, out SafeCoTaskMemHandle ppData, out uint pcbData);
/// <summary>The <c>SetProperty</c> method sets the given property on the MTP/Bluetooth Bus Enumerator device.</summary>
/// <param name="pPropertyKey">A pointer to a property key for the given property.</param>
/// <param name="PropertyType">The property type.</param>
/// <param name="pData">A pointer to the property data.</param>
/// <param name="cbData">The size (in bytes) of the property data.</param>
/// <remarks>Before calling this method, an application must verify that it has Administrator user rights.</remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-setproperty
// HRESULT SetProperty( [in] const DEVPROPKEY *pPropertyKey, [in] DEVPROPTYPE PropertyType, [in] const BYTE *pData, [in] UINT32
// cbData );
void SetProperty(in DEVPROPKEY pPropertyKey, DEVPROPTYPE PropertyType, [In] IntPtr pData, [In] uint cbData);
/// <summary>The <c>SetProperty</c> method sets the given property on the MTP/Bluetooth Bus Enumerator device.</summary>
/// <param name="pPropertyKey">A pointer to a property key for the given property.</param>
/// <param name="PropertyType">The property type.</param>
/// <param name="pData">A pointer to the property data.</param>
/// <param name="cbData">The size (in bytes) of the property data.</param>
/// <remarks>Before calling this method, an application must verify that it has Administrator user rights.</remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-setproperty
// HRESULT SetProperty( [in] const DEVPROPKEY *pPropertyKey, [in] DEVPROPTYPE PropertyType, [in] const BYTE *pData, [in] UINT32
// cbData );
void SetProperty(in DEVPROPKEY pPropertyKey, DEVPROPTYPE PropertyType, [In] IntPtr pData, [In] uint cbData);
/// <summary>The <c>GetPnPID</c> method retrieves the connector's Plug and Play (PnP) device identifier.</summary>
/// <returns>The PnP device identifier.</returns>
/// <remarks>
/// <para>
/// The identifier retrieved by this method corresponds to a handle to the MTP/Bluetooth Bus Enumerator device node that
/// receives connect and disconnect IOCTL requests for a paired MTP/Bluetooth device. Applications can use this identifier with
/// the SetupAPI functions to access the device node.
/// </para>
/// <para>
/// Once the application no longer needs the identifier specified by the ppwszPnPID parameter, it must call the
/// <c>CoTaskMemAlloc</c> function to free the identifier.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-getpnpid
// HRESULT GetPnPID( [out] LPWSTR *ppwszPnPID );
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetPnPID();
}
/// <summary>The <c>GetPnPID</c> method retrieves the connector's Plug and Play (PnP) device identifier.</summary>
/// <returns>The PnP device identifier.</returns>
/// <remarks>
/// <para>
/// The identifier retrieved by this method corresponds to a handle to the MTP/Bluetooth Bus Enumerator device node that
/// receives connect and disconnect IOCTL requests for a paired MTP/Bluetooth device. Applications can use this identifier with
/// the SetupAPI functions to access the device node.
/// </para>
/// <para>
/// Once the application no longer needs the identifier specified by the ppwszPnPID parameter, it must call the
/// <c>CoTaskMemAlloc</c> function to free the identifier.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/portabledeviceconnectapi/nf-portabledeviceconnectapi-iportabledeviceconnector-getpnpid
// HRESULT GetPnPID( [out] LPWSTR *ppwszPnPID );
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetPnPID();
}
/// <summary>EnumBthMtpConnectors Class</summary>
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("a1570149-e645-4f43-8b0d-409b061db2fc"), ClassInterface(ClassInterfaceType.None)]
public class EnumBthMtpConnectors { }
}
/// <summary>EnumBthMtpConnectors Class</summary>
[PInvokeData("portabledeviceconnectapi.h")]
[ComImport, Guid("a1570149-e645-4f43-8b0d-409b061db2fc"), ClassInterface(ClassInterfaceType.None)]
public class EnumBthMtpConnectors { }
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using static Vanara.PInvoke.Ole32;
@ -8,142 +9,142 @@ namespace Vanara.Extensions;
/// <summary>Extension methods for classes in Vanara.PInvoke.PortableDeviceApi.</summary>
public static class PortableDeviceExtensions
{
private const BindingFlags BindStPub = BindingFlags.Static | BindingFlags.Public;
{
private const BindingFlags BindStPub = BindingFlags.Static | BindingFlags.Public;
private static Dictionary<Type, Dictionary<PROPERTYKEY, PropertyInfo>> gReversePKLookup = new();
private static Dictionary<Type, Dictionary<PROPERTYKEY, PropertyInfo>> gReversePKLookup = new();
/// <summary>Extracts command results from an <see cref="IPortableDeviceValues"/> for a documented <see cref="PROPERTYKEY"/>.</summary>
/// <param name="results">The results from a call to <c>IPortableDevice.SendCommand</c>.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="parentType">The type in which <paramref name="cmd"/> is defined, if not other than <see cref="Vanara.PInvoke.PortableDeviceApi"/>.</param>
/// <returns>A dictionary containing the result values.</returns>
/// <exception cref="System.InvalidOperationException">Supplied PROPERTYKEY is not a recognized WPD command.</exception>
public static IReadOnlyDictionary<PROPERTYKEY, object> ExtractResults(this IPortableDeviceValues results, in PROPERTYKEY cmd, Type parentType = null)
{
if (!cmd.TryGetCommandInfo(out _, out _, out var rAttrs, parentType))
throw new InvalidOperationException("Supplied PROPERTYKEY is not a recognized WPD command.");
var ret = new Dictionary<PROPERTYKEY, object>();
foreach (var a in rAttrs)
{
PROPVARIANT pv = new();
try { pv = results.GetValue(a.Property); } catch { }
ret.Add(a.Property, pv.Value);
}
return (IReadOnlyDictionary<PROPERTYKEY, object>)ret;
}
/// <summary>Extracts command results from an <see cref="IPortableDeviceValues"/> for a documented <see cref="PROPERTYKEY"/>.</summary>
/// <param name="results">The results from a call to <c>IPortableDevice.SendCommand</c>.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="parentType">The type in which <paramref name="cmd"/> is defined, if not other than <see cref="Vanara.PInvoke.PortableDeviceApi"/>.</param>
/// <returns>A dictionary containing the result values.</returns>
/// <exception cref="System.InvalidOperationException">Supplied PROPERTYKEY is not a recognized WPD command.</exception>
public static IReadOnlyDictionary<PROPERTYKEY, object?> ExtractResults(this IPortableDeviceValues results, in PROPERTYKEY cmd, Type? parentType = null)
{
if (!cmd.TryGetCommandInfo(out _, out _, out var rAttrs, parentType))
throw new InvalidOperationException("Supplied PROPERTYKEY is not a recognized WPD command.");
var ret = new Dictionary<PROPERTYKEY, object?>();
foreach (var a in rAttrs)
{
PROPVARIANT pv = new();
try { pv = results.GetValue(a.Property); } catch { }
ret.Add(a.Property, pv.Value);
}
return ret;
}
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="pkResult">The PROPERTYKEY of the result value to return.</param>
/// <returns>The value returned in <paramref name="pkResult"/>.</returns>
public static object SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, in PROPERTYKEY pkResult) =>
device.SendCommand(cmd).GetValue(pkResult).Value;
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="pkResult">The PROPERTYKEY of the result value to return.</param>
/// <returns>The value returned in <paramref name="pkResult"/>.</returns>
public static object? SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, in PROPERTYKEY pkResult) =>
device.SendCommand(cmd).GetValue(pkResult).Value;
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="addParams">
/// An action that can optionally be called to manipulate the <see cref="IPortableDeviceValues"/> instance passed to <see
/// cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.
/// </param>
/// <returns>The <see cref="IPortableDeviceValues"/> instance returned by <see cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.</returns>
public static IPortableDeviceValues SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, Action<IPortableDeviceValues> addParams = null)
{
IPortableDeviceValues cmdParams = new();
cmdParams.SetCommandPKey(cmd);
addParams?.Invoke(cmdParams);
var cmdResults = device.SendCommand(0, cmdParams);
cmdResults.GetErrorValue(WPD_PROPERTY_COMMON_HRESULT).ThrowIfFailed();
return cmdResults;
}
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="addParams">
/// An action that can optionally be called to manipulate the <see cref="IPortableDeviceValues"/> instance passed to <see
/// cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.
/// </param>
/// <returns>The <see cref="IPortableDeviceValues"/> instance returned by <see cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.</returns>
public static IPortableDeviceValues SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, Action<IPortableDeviceValues>? addParams = null)
{
IPortableDeviceValues cmdParams = new();
cmdParams.SetCommandPKey(cmd);
addParams?.Invoke(cmdParams);
var cmdResults = device.SendCommand(0, cmdParams);
cmdResults.GetErrorValue(WPD_PROPERTY_COMMON_HRESULT).ThrowIfFailed();
return cmdResults;
}
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="addParams">
/// A list of <see cref="PROPERTYKEY"/>/ <see cref="object"/> tuples representing the property keys and their related values to add
/// to <see cref="IPortableDeviceValues"/>.
/// </param>
/// <returns>The <see cref="IPortableDeviceValues"/> instance returned by <see cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.</returns>
public static IPortableDeviceValues SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, params (PROPERTYKEY key, object value)[] addParams) =>
SendCommand(device, cmd, v =>
{
if (addParams is not null)
foreach ((PROPERTYKEY key, object value) in addParams)
v.SetValue(key, new PROPVARIANT(value));
});
/// <summary>Sends a command to the device and retrieves the results synchronously.</summary>
/// <param name="device">The portable device.</param>
/// <param name="cmd">The command's PROPERTYKEY.</param>
/// <param name="addParams">
/// A list of <see cref="PROPERTYKEY"/>/ <see cref="object"/> tuples representing the property keys and their related values to add
/// to <see cref="IPortableDeviceValues"/>.
/// </param>
/// <returns>The <see cref="IPortableDeviceValues"/> instance returned by <see cref="IPortableDevice.SendCommand(uint, IPortableDeviceValues)"/>.</returns>
public static IPortableDeviceValues SendCommand(this IPortableDevice device, in PROPERTYKEY cmd, params (PROPERTYKEY key, object value)[] addParams) =>
SendCommand(device, cmd, v =>
{
if (addParams is not null)
foreach ((PROPERTYKEY key, object value) in addParams)
v.SetValue(key, new PROPVARIANT(value));
});
/// <summary>Adds a new enumeration value or overwrites an existing one.</summary>
/// <typeparam name="T">The type of the enumeration value.</typeparam>
/// <param name="vals">The <see cref="IPortableDeviceValues"/> instance.</param>
/// <param name="key">A <c>PROPERTYKEY</c> that specifies the item to create or overwrite.</param>
/// <param name="enumVal">The enum value.</param>
/// <remarks>
/// If an existing value has the same key that is specified by the key parameter, it overwrites the existing value without any
/// warning. The existing key memory is released appropriately.
/// </remarks>
public static void SetEnumValue<T>(this IPortableDeviceValues vals, in PROPERTYKEY key, T enumVal) where T : Enum, IConvertible =>
vals.SetValue(key, new PROPVARIANT(Convert.ChangeType(enumVal, Enum.GetUnderlyingType(typeof(T)))));
/// <summary>Adds a new enumeration value or overwrites an existing one.</summary>
/// <typeparam name="T">The type of the enumeration value.</typeparam>
/// <param name="vals">The <see cref="IPortableDeviceValues"/> instance.</param>
/// <param name="key">A <c>PROPERTYKEY</c> that specifies the item to create or overwrite.</param>
/// <param name="enumVal">The enum value.</param>
/// <remarks>
/// If an existing value has the same key that is specified by the key parameter, it overwrites the existing value without any
/// warning. The existing key memory is released appropriately.
/// </remarks>
public static void SetEnumValue<T>(this IPortableDeviceValues vals, in PROPERTYKEY key, T enumVal) where T : Enum, IConvertible =>
vals.SetValue(key, new PROPVARIANT(Convert.ChangeType(enumVal, Enum.GetUnderlyingType(typeof(T)))));
/// <summary>Tries to get the command information associated with a provided Command <c>PROPERTYKEY</c>.</summary>
/// <param name="key">The <see cref="PROPERTYKEY"/> of the WPD command.</param>
/// <param name="cmd">The <see cref="WPDCommandAttribute"/> instance with detail about the command.</param>
/// <param name="param">
/// An array of <see cref="WPDCommandParamAttribute"/> instances representing valid parameters for <paramref name="key"/>.
/// </param>
/// <param name="result">
/// An array of <see cref="WPDCommandResultAttribute"/> instances representing valid results for <paramref name="key"/>.
/// </param>
/// <param name="parentType">The type in which <paramref name="key"/> is defined, if not other than <see cref="Vanara.PInvoke.PortableDeviceApi"/>.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="key"/> is a valid command in <paramref name="parentType"/>; <see langword="false"/> otherwise.
/// </returns>
/// <remarks>
/// The reflection based lookup is cached so that subsequent lookups are accelerated. As such, expect a slower response the first
/// time this method is called with each unique <paramref name="parentType"/>.
/// </remarks>
public static bool TryGetCommandInfo(this PROPERTYKEY key, out WPDCommandAttribute cmd, out WPDCommandParamAttribute[] param, out WPDCommandResultAttribute[] result, Type parentType = null)
{
var pi = GetPI(key, parentType);
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
if (pi is not null)
{
cmd = pi.GetCustomAttribute<WPDCommandAttribute>();
if (cmd is not null)
{
param = pi.GetCustomAttributes<WPDCommandParamAttribute>()?.ToArray() ?? new WPDCommandParamAttribute[0];
result = pi.GetCustomAttributes<WPDCommandResultAttribute>()?.ToArray() ?? new WPDCommandResultAttribute[0];
return true;
}
}
cmd = null;
param = null;
result = null;
return false;
}
/// <summary>Tries to get the command information associated with a provided Command <c>PROPERTYKEY</c>.</summary>
/// <param name="key">The <see cref="PROPERTYKEY"/> of the WPD command.</param>
/// <param name="cmd">The <see cref="WPDCommandAttribute"/> instance with detail about the command.</param>
/// <param name="param">
/// An array of <see cref="WPDCommandParamAttribute"/> instances representing valid parameters for <paramref name="key"/>.
/// </param>
/// <param name="result">
/// An array of <see cref="WPDCommandResultAttribute"/> instances representing valid results for <paramref name="key"/>.
/// </param>
/// <param name="parentType">The type in which <paramref name="key"/> is defined, if not other than <see cref="Vanara.PInvoke.PortableDeviceApi"/>.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="key"/> is a valid command in <paramref name="parentType"/>; <see langword="false"/> otherwise.
/// </returns>
/// <remarks>
/// The reflection based lookup is cached so that subsequent lookups are accelerated. As such, expect a slower response the first
/// time this method is called with each unique <paramref name="parentType"/>.
/// </remarks>
public static bool TryGetCommandInfo(this PROPERTYKEY key, [NotNullWhen(true)] out WPDCommandAttribute? cmd, [NotNullWhen(true)] out WPDCommandParamAttribute[]? param, [NotNullWhen(true)] out WPDCommandResultAttribute[]? result, Type? parentType = null)
{
var pi = GetPI(key, parentType);
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
if (pi is not null)
{
cmd = pi.GetCustomAttribute<WPDCommandAttribute>();
if (cmd is not null)
{
param = pi.GetCustomAttributes<WPDCommandParamAttribute>()?.ToArray() ?? new WPDCommandParamAttribute[0];
result = pi.GetCustomAttributes<WPDCommandResultAttribute>()?.ToArray() ?? new WPDCommandResultAttribute[0];
return true;
}
}
cmd = null;
param = null;
result = null;
return false;
}
internal static PROPERTYKEY? GetKeyFromName(string keyName, Type parentType = null)
{
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
var kv = GetDict(parentType).FirstOrDefault(kv => kv.Value.Name == keyName);
return kv.Value is null ? null : kv.Key;
}
internal static PROPERTYKEY? GetKeyFromName(string keyName, Type? parentType = null)
{
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
var kv = GetDict(parentType).FirstOrDefault(kv => kv.Value.Name == keyName);
return kv.Value is null ? null : kv.Key;
}
private static Dictionary<PROPERTYKEY, PropertyInfo> GetDict(Type type)
{
if (!gReversePKLookup.TryGetValue(type, out var dict))
{
dict = type.GetProperties(BindStPub).ToDictionary(m => (PROPERTYKEY)m.GetValue(null, null));
gReversePKLookup.Add(type, dict);
}
return dict;
}
private static Dictionary<PROPERTYKEY, PropertyInfo> GetDict(Type type)
{
if (!gReversePKLookup.TryGetValue(type, out var dict))
{
dict = type.GetProperties(BindStPub).ToDictionary(m => (PROPERTYKEY)m.GetValue(null, null)!);
gReversePKLookup.Add(type, dict);
}
return dict;
}
private static PropertyInfo GetPI(in PROPERTYKEY key, Type parentType = null)
{
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
return GetDict(parentType).TryGetValue(key, out var pi) ? pi : null;
}
}
private static PropertyInfo? GetPI(in PROPERTYKEY key, Type? parentType = null)
{
parentType ??= typeof(Vanara.PInvoke.PortableDeviceApi);
return GetDict(parentType).TryGetValue(key, out var pi) ? pi : null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -9,205 +9,205 @@ using static Vanara.PInvoke.PortableDeviceApi;
namespace Vanara.PInvoke.Tests;
[TestFixture]
public class PortableDeviceApiTests
{
IPortableDevice device = null;
IPortableDeviceManager manager;
static readonly Ole32.PROPERTYKEY eventNameProp = new(Guid.NewGuid(), 1);
public class PortableDeviceApiTests
{
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
IPortableDevice device;
IPortableDeviceManager manager;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
static readonly Ole32.PROPERTYKEY eventNameProp = new(Guid.NewGuid(), 1);
[OneTimeSetUp]
public void _Setup()
{
manager = new();
var deviceId = manager.GetDevices().FirstOrDefault() ?? throw new ArgumentNullException("Device");
[OneTimeSetUp]
public void _Setup()
{
manager = new();
var deviceId = manager.GetDevices().FirstOrDefault() ?? throw new ArgumentNullException("Device");
device = new();
device.Open(deviceId, GetClientInfo());
}
device.Open(deviceId, GetClientInfo());
}
[OneTimeTearDown]
public void _TearDown()
{
device?.Close();
device = null;
manager = null;
}
[OneTimeTearDown]
public void _TearDown()
{
device?.Close();
}
[Test]
public void DeviceEventing()
{
string evtName = nameof(DeviceEventing);
EventWaitHandle evt = new(false, EventResetMode.ManualReset, evtName);
EventCallback callback = new();
IPortableDeviceValues vals = new();
vals.SetStringValue(eventNameProp, evtName);
device.Advise(0, callback, vals, out var cookie);
TestContext.WriteLine($"Fired = {evt.WaitOne(200)}");
device.Unadvise(cookie);
}
[Test]
public void DeviceEventing()
{
string evtName = nameof(DeviceEventing);
EventWaitHandle evt = new(false, EventResetMode.ManualReset, evtName);
EventCallback callback = new();
IPortableDeviceValues vals = new();
vals.SetStringValue(eventNameProp, evtName);
device.Advise(0, callback, vals, out var cookie);
TestContext.WriteLine($"Fired = {evt.WaitOne(200)}");
device.Unadvise(cookie);
}
class EventCallback : IPortableDeviceEventCallback
{
void IPortableDeviceEventCallback.OnEvent(IPortableDeviceValues pEventParameters)
{
var evtName = pEventParameters.GetStringValue(eventNameProp);
if (EventWaitHandle.TryOpenExisting(evtName, out var evt))
evt.Set();
}
}
class EventCallback : IPortableDeviceEventCallback
{
void IPortableDeviceEventCallback.OnEvent(IPortableDeviceValues? pEventParameters)
{
var evtName = pEventParameters?.GetStringValue(eventNameProp);
if (evtName is not null && EventWaitHandle.TryOpenExisting(evtName, out var evt))
evt.Set();
}
}
[Test]
public void EnumDevices()
{
try
{
var devices = manager.GetDevices();
TestContext.WriteLine("{0} Windows Portable Device(s) found in the system", devices.Length);
[Test]
public void EnumDevices()
{
try
{
var devices = manager.GetDevices();
TestContext.WriteLine("{0} Windows Portable Device(s) found in the system", devices.Length);
foreach (var device in devices)
{
TestContext.WriteLine(device +
"\n\tManufacturer: " + manager.GetDeviceManufacturer(device) +
"\n\tDescription: " + manager.GetDeviceDescription(device) +
"\n\tFriendly Name: " + manager.GetDeviceFriendlyName(device));
}
}
catch (COMException exception)
{
Console.WriteLine("COM exception: code {1}, {0}", exception.Message, exception.ErrorCode);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
}
foreach (var device in devices)
{
TestContext.WriteLine(device +
"\n\tManufacturer: " + manager.GetDeviceManufacturer(device) +
"\n\tDescription: " + manager.GetDeviceDescription(device) +
"\n\tFriendly Name: " + manager.GetDeviceFriendlyName(device));
}
}
catch (COMException exception)
{
Console.WriteLine("COM exception: code {1}, {0}", exception.Message, exception.ErrorCode);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
}
[Test]
public void EnumDeviceProps()
{
var content = device.Content();
[Test]
public void EnumDeviceProps()
{
var content = device.Content();
foreach (var s in RecursiveEnumerate(WPD_DEVICE_OBJECT_ID, content).Take(20))
TestContext.WriteLine(s);
foreach (var s in RecursiveEnumerate(WPD_DEVICE_OBJECT_ID, content).Take(20))
TestContext.WriteLine(s);
static IEnumerable<string> RecursiveEnumerate(string objId, IPortableDeviceContent content)
{
var enumObjs = content.EnumObjects(0, objId);
foreach (var sobj in enumObjs.Enumerate())
{
yield return sobj;
foreach (var s in RecursiveEnumerate(sobj, content))
yield return s;
}
}
}
static IEnumerable<string> RecursiveEnumerate(string objId, IPortableDeviceContent content)
{
var enumObjs = content.EnumObjects(0, objId);
foreach (var sobj in enumObjs.Enumerate())
{
yield return sobj;
foreach (var s in RecursiveEnumerate(sobj, content))
yield return s;
}
}
}
[Test]
public void EnumDeviceFuncCats()
{
var caps = device.Capabilities();
[Test]
public void EnumDeviceFuncCats()
{
var caps = device.Capabilities();
foreach (var catid in caps.GetFunctionalCategories().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
{
TestContext.WriteLine(GetPI(catid, "WPD_FUNCTIONAL_CATEGORY_")?.Name ?? catid.ToString());
foreach (var type in caps.GetSupportedContentTypes(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
{
TestContext.WriteLine(" t: " + (GetPI(type, "WPD_CONTENT_TYPE_")?.Name ?? type.ToString()));
foreach (var fmt in caps.GetSupportedFormats(type).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
TestContext.WriteLine(" f: " + (GetPI(fmt, "WPD_OBJECT_FORMAT_")?.Name ?? type.ToString()));
}
foreach (var obj in caps.GetFunctionalObjects(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_LPWSTR).Select(pv => pv.pwszVal))
TestContext.WriteLine(" o: " + obj);
}
}
foreach (var catid in caps.GetFunctionalCategories().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid!.Value))
{
TestContext.WriteLine(GetPI(catid, "WPD_FUNCTIONAL_CATEGORY_")?.Name ?? catid.ToString());
foreach (var type in caps.GetSupportedContentTypes(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid!.Value))
{
TestContext.WriteLine(" t: " + (GetPI(type, "WPD_CONTENT_TYPE_")?.Name ?? type.ToString()));
foreach (var fmt in caps.GetSupportedFormats(type).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid!.Value))
TestContext.WriteLine(" f: " + (GetPI(fmt, "WPD_OBJECT_FORMAT_")?.Name ?? type.ToString()));
}
foreach (var obj in caps.GetFunctionalObjects(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_LPWSTR).Select(pv => pv.pwszVal))
TestContext.WriteLine(" o: " + obj);
}
}
[Test]
public void EnumDeviceCmds()
{
var caps = device.Capabilities();
[Test]
public void EnumDeviceCmds()
{
var caps = device.Capabilities();
foreach (var cmd in caps.GetSupportedCommands().Enumerate())
{
TestContext.WriteLine(GetPI(cmd, "WPD_COMMAND_")?.Name ?? cmd.ToString());
Write(caps.GetCommandOptions(cmd), "WPD_OPTION_", " ");
}
}
foreach (var cmd in caps.GetSupportedCommands().Enumerate())
{
TestContext.WriteLine(GetPI(cmd, "WPD_COMMAND_")?.Name ?? cmd.ToString());
Write(caps.GetCommandOptions(cmd), "WPD_OPTION_", " ");
}
}
[Test]
public void EnumDeviceResources()
{
var content = device.Content();
var res = content.Transfer();
foreach (var cmd in res.GetSupportedResources(WPD_DEVICE_OBJECT_ID).Enumerate())
{
Write(res.GetResourceAttributes(WPD_DEVICE_OBJECT_ID, cmd), "WPD_");
}
}
[Test]
public void EnumDeviceResources()
{
var content = device.Content();
var res = content.Transfer();
foreach (var cmd in res.GetSupportedResources(WPD_DEVICE_OBJECT_ID).Enumerate())
{
Write(res.GetResourceAttributes(WPD_DEVICE_OBJECT_ID, cmd), "WPD_");
}
}
private void Write(IPortableDeviceValues vals, string lookupFilter, string prefix = "")
{
foreach (var val in vals.Enumerate())
{
var name = GetPI(val.Item1, lookupFilter)?.Name ?? val.Item1.ToString();
TestContext.WriteLine($"{prefix}{name} = {val.Item2.Value}");
}
}
private void Write(IPortableDeviceValues vals, string lookupFilter, string prefix = "")
{
foreach (var val in vals.Enumerate())
{
var name = GetPI(val.Item1, lookupFilter)?.Name ?? val.Item1.ToString();
TestContext.WriteLine($"{prefix}{name} = {val.Item2.Value}");
}
}
[Test]
public void CallDeviceCmd()
{
var pkey = WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED;
Assert.IsTrue(device.Capabilities().GetSupportedCommands().Enumerate().Contains(pkey));
var objId = device.Content().EnumObjects(0, WPD_DEVICE_OBJECT_ID).Enumerate().First();
IPortableDeviceValues vals = new();
vals.SetCommandPKey(pkey);
vals.SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, objId);
IPortableDeviceKeyCollection result = new();
vals.SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, result);
device.SendCommand(0, vals);
foreach (var key in result.Enumerate())
TestContext.WriteLine(key);
}
[Test]
public void CallDeviceCmd()
{
var pkey = WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED;
Assert.IsTrue(device.Capabilities().GetSupportedCommands().Enumerate().Contains(pkey));
var objId = device.Content().EnumObjects(0, WPD_DEVICE_OBJECT_ID).Enumerate().First();
IPortableDeviceValues vals = new();
vals.SetCommandPKey(pkey);
vals.SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, objId);
IPortableDeviceKeyCollection result = new();
vals.SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, result);
device.SendCommand(0, vals);
foreach (var key in result.Enumerate())
TestContext.WriteLine(key);
}
[Test]
public void EnumDeviceEvents()
{
var caps = device.Capabilities();
[Test]
public void EnumDeviceEvents()
{
var caps = device.Capabilities();
foreach (var evt in caps.GetSupportedEvents().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
{
TestContext.WriteLine(GetPI(evt, "WPD_EVENT_")?.Name ?? evt.ToString());
foreach (var opt in caps.GetEventOptions(evt).Enumerate())
TestContext.WriteLine($" {(GetPI(opt.Item1, "WPD_EVENT_OPTION_")?.Name ?? opt.Item1.ToString())} = {opt.Item2.Value}");
}
}
foreach (var evt in caps.GetSupportedEvents().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid!.Value))
{
TestContext.WriteLine(GetPI(evt, "WPD_EVENT_")?.Name ?? evt.ToString());
foreach (var opt in caps.GetEventOptions(evt).Enumerate())
TestContext.WriteLine($" {(GetPI(opt.Item1, "WPD_EVENT_OPTION_")?.Name ?? opt.Item1.ToString())} = {opt.Item2.Value}");
}
}
private static PropertyInfo GetPI<T>(T t, string prefix) =>
typeof(PortableDeviceApi).GetProperties(BindingFlags.Public | BindingFlags.Static).
FirstOrDefault(pi => pi.Name.StartsWith(prefix) && pi.PropertyType.Equals(typeof(T)) && t.Equals(pi.GetValue(null)));
private static PropertyInfo? GetPI<T>(T t, string prefix) =>
typeof(PortableDeviceApi).GetProperties(BindingFlags.Public | BindingFlags.Static).
FirstOrDefault(pi => pi.Name.StartsWith(prefix) && pi.PropertyType.Equals(typeof(T)) && Equals(t, pi.GetValue(null)));
private IPortableDeviceValues GetClientInfo(bool readOnly = true)
{
// Client information is optional. The client can choose to identify itself, or
// to remain unknown to the driver. It is beneficial to identify yourself because
// drivers may be able to optimize their behavior for known clients. (e.g. An
// IHV may want their bundled driver to perform differently when connected to their
// bundled software.)
private IPortableDeviceValues GetClientInfo(bool readOnly = true)
{
// Client information is optional. The client can choose to identify itself, or
// to remain unknown to the driver. It is beneficial to identify yourself because
// drivers may be able to optimize their behavior for known clients. (e.g. An
// IHV may want their bundled driver to perform differently when connected to their
// bundled software.)
// CoCreate an IPortableDeviceValues interface to hold the client information.
IPortableDeviceValues clientInformation = new();
// CoCreate an IPortableDeviceValues interface to hold the client information.
IPortableDeviceValues clientInformation = new();
// Attempt to set all bits of client information
clientInformation.SetStringValue(WPD_CLIENT_NAME, "Test");
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 1);
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 2);
if (readOnly)
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, ACCESS_MASK.GENERIC_READ);
// Attempt to set all bits of client information
clientInformation.SetStringValue(WPD_CLIENT_NAME, "Test");
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 1);
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 2);
if (readOnly)
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, ACCESS_MASK.GENERIC_READ);
// Some device drivers need to impersonate the caller in order to function correctly. Since our application does not
// need to restrict its identity, specify SECURITY_IMPERSONATION so that we work with all devices.
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, (uint)FileFlagsAndAttributes.SECURITY_IMPERSONATION);
// Some device drivers need to impersonate the caller in order to function correctly. Since our application does not
// need to restrict its identity, specify SECURITY_IMPERSONATION so that we work with all devices.
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, (uint)FileFlagsAndAttributes.SECURITY_IMPERSONATION);
return clientInformation;
}
}
return clientInformation;
}
}