diff --git a/PInvoke/CfgMgr32/SWDevice.cs b/PInvoke/CfgMgr32/SWDevice.cs new file mode 100644 index 00000000..31d687a4 --- /dev/null +++ b/PInvoke/CfgMgr32/SWDevice.cs @@ -0,0 +1,415 @@ +using System; +using System.Runtime.InteropServices; +using Vanara.InteropServices; + +namespace Vanara.PInvoke +{ + /// Items from the CfgMgr32.dll + public static partial class CfgMgr32 + { + /// + /// Provides a device with backing in the registry and allows the caller to then make calls to Software Device API functions with + /// the hSwDevice handle. + /// + /// The handle for the software device. + /// An HRESULT that indicates if the enumeration of the software device was successful. + /// The context that was optionally supplied by the client app to SwDeviceCreate. + /// The device instance ID that PnP assigned to the device. + /// None + /// + /// + /// The operating system calls the SW_DEVICE_CREATE_CALLBACK callback function after PnP enumerates the device. After the + /// callback function is called, the device has backing in the registry and calls to Software Device API functions can be made by + /// using the hSwDevice handle. You can also use other APIs that work with devices for the device that is created. + /// + /// + /// PnP enumeration of a device is the first step that a device undergoes. After PnP enumeration of the device, the device only has + /// registry backing, and you can set properties against the device. Just because PnP enumerated the device, the device hasn't + /// started yet, and no driver for the device has registered or enabled interfaces yet. In many cases, we recommend that apps wait + /// for device-interface arrival if they want to use the device. + /// + /// + /// Note The callback function supplies the device instance ID for the created device. We recommend that callers of the + /// Software Device API not try to guess at or construct the device instance ID themselves; always use the value provided by the + /// callback function. + /// + /// + /// The callback function will execute on an arbitrary thread-pool thread. Client apps can perform as much work as needed in the + /// callback function. + /// + /// + /// In Windows 8, you can't call SwDeviceClose inside the callback function. Doing so will cause a deadlock. Be careful of releasing + /// a ref counted object that will call SwDeviceClose when its destructor runs. In Windows 8.1, this restriction is lifted, + /// and you can call SwDeviceClose inside the callback function. + /// + /// Always check the HRESULT that is passed to CreateResult to make sure PnP was able to enumerate the device. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/swdevice/nc-swdevice-sw_device_create_callback SW_DEVICE_CREATE_CALLBACK + // SwDeviceCreateCallback; void SwDeviceCreateCallback( HSWDEVICE hSwDevice, HRESULT CreateResult, PVOID pContext, PCWSTR + // pszDeviceInstanceId ) {...} + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [PInvokeData("swdevice.h", MSDNShortId = "NC:swdevice.SW_DEVICE_CREATE_CALLBACK")] + public delegate void SW_DEVICE_CREATE_CALLBACK([In] HSWDEVICE hSwDevice, HRESULT CreateResult, [In, Optional] IntPtr pContext, [Optional, MarshalAs(UnmanagedType.LPWStr)] string pszDeviceInstanceId); + + /// Specifies capabilities of the software device. + [PInvokeData("swdevicedef.h", MSDNShortId = "NS:swdevicedef._SW_DEVICE_CREATE_INFO")] + [Flags] + public enum SW_DEVICE_CAPABILITIES + { + /// No capabilities have been specified. + SWDeviceCapabilitiesNone = 0x00000000, + + /// + /// This bit specifies that the device is removable from its parent. Setting this flag is equivalent to a bus driver setting the + /// Removable member of the DEVICE_CAPABILTIES structure for a PDO. + /// + SWDeviceCapabilitiesRemovable = 0x00000001, + + /// + /// This bit suppresses UI that would normally be shown during installation. Setting this flag is equivalent to a bus driver + /// setting the SilentInstall member of the DEVICE_CAPABILTIES structure for a PDO. + /// + SWDeviceCapabilitiesSilentInstall = 0x00000002, + + /// + /// This bit prevents the device from being displayed in some UI. Setting this flag is equivalent to a bus driver setting the + /// NoDisplayInUI member of the DEVICE_CAPABILTIES structure for a PDO. + /// + SWDeviceCapabilitiesNoDisplayInUI = 0x00000004, + + /// + /// Specify this bit when the client wants a driver to be loaded on the device and when this driver is required for correct + /// function of the client’s feature. When this bit is specified, at least one of pszzHardwareIds or pszzCompatibleIds must be + /// filled in. If this bit is specified and if a driver can't be found, the device shows a yellow bang in Device Manager to + /// indicate that the device has a problem, and Troubleshooters flag this as a device with a problem. Setting this bit is + /// equivalent to a bus driver not setting the RawDeviceOK member of the DEVICE_CAPABILTIES structure for a PDO. When this bit + /// is specified, the driver owns creating interfaces for the device, and you can't call SwDeviceInterfaceRegister for the device. + /// + SWDeviceCapabilitiesDriverRequired = 0x00000008 + } + + [PInvokeData("swdevicedef.h")] + public enum SW_DEVICE_LIFETIME + { + SWDeviceLifetimeHandle, + SWDeviceLifetimeParentPresent, + SWDeviceLifetimeMax + } + + /// Closes the software device handle. When the handle is closed, PnP will initiate the process of removing the device. + /// The HSWDEVICE handle to close. + /// None + /// + /// + /// After SwDeviceClose returns, the operating system is guaranteed to not call the SW_DEVICE_CREATE_CALLBACK callback + /// function, and any calls to Software Device API functions that were in progress are guaranteed to have completed. + /// + /// You can call SwDeviceClose at any time even if the callback function hasn't been called yet. + /// + /// In Windows 8, you can't call SwDeviceClose inside the SW_DEVICE_CREATE_CALLBACK callback function. Doing so will cause a + /// deadlock. Be careful of releasing a ref counted object that will call SwDeviceClose when its destructor runs. In Windows + /// 8.1, this restriction is lifted, and you can call SwDeviceClose inside the callback function. + /// + /// + /// By calling SwDeviceClose, you initiate the process of removing a device from PnP. The call to SwDeviceClose + /// returns before this removal is complete. But you can safely call SwDeviceCreate immediately after SwDeviceClose. The new + /// create will be queued until the previous removal processing completes, and then the device will be re-created. + /// + /// + /// PnP removal makes the device "Not present." PnP removal of a device is the same us unplugging a USB device. All the persisted + /// property state for the device remains in memory. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/swdevice/nf-swdevice-swdeviceclose void SwDeviceClose( HSWDEVICE hSwDevice ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("swdevice.h", MSDNShortId = "NF:swdevice.SwDeviceClose")] + public static extern void SwDeviceClose(HSWDEVICE hSwDevice); + + /// Provides a handle to a software device. + [StructLayout(LayoutKind.Sequential)] + public struct HSWDEVICE : IHandle + { + private readonly IntPtr handle; + + /// Initializes a new instance of the struct. + /// An object that represents the pre-existing handle to use. + public HSWDEVICE(IntPtr preexistingHandle) => handle = preexistingHandle; + + /// Returns an invalid handle by instantiating a object with . + public static HSWDEVICE NULL => new(IntPtr.Zero); + + /// Gets a value indicating whether this instance is a null handle. + public bool IsNull => handle == IntPtr.Zero; + + /// Performs an explicit conversion from to . + /// The handle. + /// The result of the conversion. + public static explicit operator IntPtr(HSWDEVICE h) => h.handle; + + /// Performs an implicit conversion from to . + /// The pointer to a handle. + /// The result of the conversion. + public static implicit operator HSWDEVICE(IntPtr h) => new(h); + + /// Implements the operator !=. + /// The first handle. + /// The second handle. + /// The result of the operator. + public static bool operator !=(HSWDEVICE h1, HSWDEVICE h2) => !(h1 == h2); + + /// Implements the operator ==. + /// The first handle. + /// The second handle. + /// The result of the operator. + public static bool operator ==(HSWDEVICE h1, HSWDEVICE h2) => h1.Equals(h2); + + /// + public override bool Equals(object obj) => obj is HSWDEVICE h && handle == h.handle; + + /// + public override int GetHashCode() => handle.GetHashCode(); + + /// + public IntPtr DangerousGetHandle() => handle; + } + + /// Describes info that PnP uses to create the software device. + /// + /// You can only specify this info at creation time, and you can't later call the Software Device API to modify this info, by + /// setting properties, for example. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/swdevicedef/ns-swdevicedef-sw_device_create_info typedef struct + // _SW_DEVICE_CREATE_INFO { ULONG cbSize; PCWSTR pszInstanceId; PCZZWSTR pszzHardwareIds; PCZZWSTR pszzCompatibleIds; const GUID + // *pContainerId; ULONG CapabilityFlags; PCWSTR pszDeviceDescription; PCWSTR pszDeviceLocation; const SECURITY_DESCRIPTOR + // *pSecurityDescriptor; } SW_DEVICE_CREATE_INFO, *PSW_DEVICE_CREATE_INFO; + [PInvokeData("swdevicedef.h", MSDNShortId = "NS:swdevicedef._SW_DEVICE_CREATE_INFO")] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SW_DEVICE_CREATE_INFO + { + /// The size in bytes of this structure. Use it as a version field. Initialize it to sizeof(SW_DEVICE_CREATE_INFO). + public uint cbSize; + + /// + /// A string that represents the instance ID portion of the device instance ID. This value is used for IRP_MN_QUERY_ID + /// BusQueryInstanceID. Because all software devices are considered "UniqueId" devices, this string must be a unique name + /// for all devices on this software device enumerator. For more info, see Instance IDs. + /// + public string pszInstanceId; + + /// + /// A list of strings for the hardware IDs for the software device. This value is used for IRP_MN_QUERY_ID + /// BusQueryHardwareIDs. If a client expects a driver or device metadata to bind to the device, the client specifies + /// hardware IDs. + /// + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullTermStringArrayMarshaler))] + public string[] pszzHardwareIds; + + /// + /// A list of strings for the compatible IDs for the software device. This value is used for IRP_MN_QUERY_ID + /// BusQueryCompatibleIDs. If a client expects a class driver to load, the client specifies compatible IDs that match the + /// class driver. If a driver isn't needed, we recommend to specify a compatible ID to classify the type of software device. In + /// addition to the compatible IDs specified in this member, SWD\Generic and possibly SWD\GenericRaw will always be added as the + /// least specific compatible IDs. + /// + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullTermStringArrayMarshaler))] + public string[] pszzCompatibleIds; + + /// + /// A value that is used to control the base container ID for the software device. This value will be used for IRP_MN_QUERY_ID + /// BusQueryContainerIDs. For typical situations, we recommend to set this member to NULL and use the + /// SWDeviceCapabilitiesRemovable flag to control whether the device inherits the parent's container ID or if PnP assigns + /// a new random container ID. If the client needs to explicitly control the container ID, specify a GUID in the variable + /// that this member points to. + /// + public GuidPtr pContainerId; + + /// + /// + /// A combination of SW_DEVICE_CAPABILITIES values that are combined by using a bitwise OR operation. The resulting value + /// specifies capabilities of the software device. The capability that you can specify when you create a software device are a + /// subset of the capabilities that a bus driver can specify by using the DEVICE_CAPABILTIES structure. Only capabilities + /// that make sense to allow changing for a software only device are supported. The rest receive appropriate default values. + /// Here are possible values: + /// + /// + /// + /// Value + /// Meaning + /// + /// + /// SWDeviceCapabilitiesNone 0x00000000 + /// No capabilities have been specified. + /// + /// + /// SWDeviceCapabilitiesRemovable 0x00000001 + /// + /// This bit specifies that the device is removable from its parent. Setting this flag is equivalent to a bus driver setting the + /// Removable member of the DEVICE_CAPABILTIES structure for a PDO. + /// + /// + /// + /// SWDeviceCapabilitiesSilentInstall 0x00000002 + /// + /// This bit suppresses UI that would normally be shown during installation. Setting this flag is equivalent to a bus driver + /// setting the SilentInstall member of the DEVICE_CAPABILTIES structure for a PDO. + /// + /// + /// + /// SWDeviceCapabilitiesNoDisplayInUI 0x00000004 + /// + /// This bit prevents the device from being displayed in some UI. Setting this flag is equivalent to a bus driver setting the + /// NoDisplayInUI member of the DEVICE_CAPABILTIES structure for a PDO. + /// + /// + /// + /// SWDeviceCapabilitiesDriverRequired 0x00000008 + /// + /// Specify this bit when the client wants a driver to be loaded on the device and when this driver is required for correct + /// function of the client’s feature. When this bit is specified, at least one of pszzHardwareIds or pszzCompatibleIds must be + /// filled in. If this bit is specified and if a driver can't be found, the device shows a yellow bang in Device Manager to + /// indicate that the device has a problem, and Troubleshooters flag this as a device with a problem. Setting this bit is + /// equivalent to a bus driver not setting the RawDeviceOK member of the DEVICE_CAPABILTIES structure for a PDO. When this bit + /// is specified, the driver owns creating interfaces for the device, and you can't call SwDeviceInterfaceRegister for the device. + /// + /// + /// + /// + public SW_DEVICE_CAPABILITIES CapabilityFlags; + + /// + /// + /// A string that contains the text that is displayed for the device name in the UI. This value is used for + /// IRP_MN_QUERY_DEVICE_TEXT DeviceTextDescription. + /// + /// Note + /// + /// When an INF is matched against the device, the name from the INF overrides this name unless steps are taken to preserve this name. + /// + /// We recommend that this string be a reference to a localizable resource. For the syntax of referencing resources, see DEVPROP_TYPE_STRING_INDIRECT. + /// + /// + public string pszDeviceDescription; + + /// + /// + /// A string that contains the text that is displayed for the device location in the UI. This value is used for + /// IRP_MN_QUERY_DEVICE_TEXT DeviceTextLocationInformation. + /// + /// Note Specifying a location is uncommon. + /// + public string pszDeviceLocation; + + /// + /// A pointer to a SECURITY_DESCRIPTOR structure that contains the security information associated with the software device. If + /// this member is NULL, the I/O Manager assigns the default security descriptor to the device. If a custom security + /// descriptor is needed, specify a self-relative security descriptor. + /// + public PSECURITY_DESCRIPTOR pSecurityDescriptor; + } + + /// Provides a for that is disposed using . + public class SafeHSWDEVICE : SafeHANDLE + { + /// Initializes a new instance of the class and assigns an existing handle. + /// An object that represents the pre-existing handle to use. + /// + /// to reliably release the handle during the finalization phase; otherwise, (not recommended). + /// + public SafeHSWDEVICE(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { } + + /// Initializes a new instance of the class. + private SafeHSWDEVICE() : base() { } + + /// Performs an implicit conversion from to . + /// The safe handle instance. + /// The result of the conversion. + public static implicit operator HSWDEVICE(SafeHSWDEVICE h) => h.handle; + + /// + protected override bool InternalReleaseHandle() { SwDeviceClose(handle); return true; } + } + + /* + /// Initiates the enumeration of a software device. + /// + /// A string that names the enumerator of the software device. Choose a name that represents the component that enumerates the devices. + /// + /// + /// A string that specifies the device instance ID of the device that is the parent of the software device. + /// + /// This can be HTREE\ROOT\0, but we recommend to keep children of the root device to a minimum. We also recommend that the + /// preferred parent of a software device be a real device that the software device is extending the functionality for. In + /// situations where a software device doesn't have such a natural parent, create a device as a child of the root that can collect + /// all the software devices that a component will enumerate; then, enumerate the actual software devices as children of this device + /// grouping node. This keeps the children of the root device to a manageable number. + /// + /// + /// A pointer to a SW_DEVICE_CREATE_INFO structure that describes info that PnP uses to create the device. + /// The number of DEVPROPERTY structures in the pProperties array. + /// + /// An optional array of DEVPROPERTY structures. These properties are set on the device after it is created but before a + /// notification that the device has been created are sent. For more info, see Remarks. This pointer can be NULL. + /// + /// + /// The SW_DEVICE_CREATE_CALLBACK callback function that the operating system calls after PnP enumerates the device. + /// + /// + /// An optional client context that the operating system passes to the callback function. This pointer can be NULL. + /// + /// + /// A pointer to a variable that receives the HSWDEVICE handle that represents the device. Call SwDeviceClose to close this + /// handle after the client app wants PnP to remove the device. + /// + /// + /// S_OK is returned if device enumeration was successfully initiated. This does not mean that the device has been successfully + /// enumerated. Check the CreateResult parameter of the SW_DEVICE_CREATE_CALLBACK callback function to determine if the device was + /// successfully enumerated. + /// + /// + /// SwDeviceCreate returns a handle that represents the device. After this handle is closed, PnP will remove the device. + /// The calling process must have Administrator access in order to initiate the enumeration of a software device. + /// + /// PnP forms the device instance ID of a software device as "SWD<pszEnumeratorName><pszInstanceId>," but this + /// string might change or PnP might decorate the name. Always get the device instance ID from the callback function. + /// + /// + /// There is a subtle difference between properties that are set as part of a SwDeviceCreate call and properties that are + /// later set by calling SwDevicePropertySet. Properties that are set as part of SwDeviceCreate are stored in memory; if the + /// device is uninstalled or a null driver wipes out the property stores, these properties are written out again by the Software + /// Device API feature when PnP re-enumerates the devices. This is all transparent to the client. Properties that are set using + /// SwDevicePropertySet after the enumeration don't persist in memory. But, if you set a property by using + /// SwDeviceCreate, you can update the value with SwDevicePropertySet, and this update is applied to the in-memory + /// value as well as the persisted store. + /// + /// + /// We recommend that all properties be specified as part of the call to SwDeviceCreate when possible and that these + /// properties be specified for every call to SwDeviceCreate. + /// + /// + /// Note The operating system might possibly call SW_DEVICE_CREATE_CALLBACK before the call to SwDeviceCreate returns. + /// For this reason, the software device handle for the device is supplied as a parameter to the callback function. + /// + /// + /// You can create a software device as the child of a parent that is not present at the time. PnP will enumerate the software + /// device after the parent becomes present. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/swdevice/nf-swdevice-swdevicecreate HRESULT SwDeviceCreate( PCWSTR + // pszEnumeratorName, PCWSTR pszParentDeviceInstance, const SW_DEVICE_CREATE_INFO *pCreateInfo, ULONG cPropertyCount, const + // DEVPROPERTY *pProperties, SW_DEVICE_CREATE_CALLBACK pCallback, PVOID pContext, PHSWDEVICE phSwDevice ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("swdevice.h", MSDNShortId = "NF:swdevice.SwDeviceCreate")] + public static extern HRESULT SwDeviceCreate([MarshalAs(UnmanagedType.LPWStr)] string pszEnumeratorName, [MarshalAs(UnmanagedType.LPWStr)] string pszParentDeviceInstance, + in SW_DEVICE_CREATE_INFO pCreateInfo, uint cPropertyCount, [In, Optional, MarshalAs(UnmanagedType.LPArray)] DEVPROPERTY[] pProperties, SW_DEVICE_CREATE_CALLBACK pCallback, + [In, Optional] IntPtr pContext, out SafeHSWDEVICE phSwDevice); + + SwDeviceGetLifetime Gets the lifetime of a software device. + SwDeviceInterfacePropertySet Sets properties on a software device interface. + SwDeviceInterfaceRegister Registers a device interface for a software device and optionally sets properties on that interface. + SwDeviceInterfaceSetState Enables or disables a device interface for a software device. + SwDevicePropertySet Sets properties on a software device. + SwDeviceSetLifetime Manages the lifetime of a software device. + SwMemFree Frees memory that other Software Device API functions allocated. + */ + } +} \ No newline at end of file diff --git a/Vanara.sln b/Vanara.sln index bc04fccf..6f05b699 100644 --- a/Vanara.sln +++ b/Vanara.sln @@ -309,7 +309,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vanara.PInvoke.CfgMgr32", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vanara.PInvoke.TextServicesFramework", "PInvoke\TextServicesFramework\Vanara.PInvoke.TextServicesFramework.csproj", "{17DE6265-EE0F-42E5-AE1A-7ACCDD8A45E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CfgMgr32", "UnitTests\PInvoke\CfgMgr32\CfgMgr32.csproj", "{6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CfgMgr32", "UnitTests\PInvoke\CfgMgr32\CfgMgr32.csproj", "{6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1012,9 +1012,7 @@ Global {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.DebugNoTests|Any CPU.ActiveCfg = Debug|Any CPU - {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.DebugNoTests|Any CPU.Build.0 = Debug|Any CPU {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6F4649D2-CB4E-463A-A5DE-CCC3B0DBD07A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE