diff --git a/PInvoke/CfgMgr32/CfgMgr32.Structs.cs b/PInvoke/CfgMgr32/CfgMgr32.Structs.cs index 3aea421a..95063d45 100644 --- a/PInvoke/CfgMgr32/CfgMgr32.Structs.cs +++ b/PInvoke/CfgMgr32/CfgMgr32.Structs.cs @@ -2,6 +2,7 @@ using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; using System.Text; +using Vanara.Extensions; using Vanara.InteropServices; using static Vanara.PInvoke.AdvApi32; using static Vanara.PInvoke.SetupAPI; @@ -359,6 +360,14 @@ namespace Vanara.PInvoke [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CONFLICT_DETAILS { + /// Initializes a new instance of the struct with the size and mask set. + /// The mask of items to retrieve. + public CONFLICT_DETAILS(CM_CDMASK mask) : this() + { + CD_ulSize = (uint)Marshal.SizeOf(typeof(CONFLICT_DETAILS)); + CD_ulMask = mask; + } + /// Size, in bytes, of the CONFLICT_DETAILS structure. public uint CD_ulSize; @@ -434,9 +443,6 @@ namespace Vanara.PInvoke /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 /*MAX_PATH*/)] public string CD_szDescription; - - /// Gets a default value for the structure with the size field set. - public static readonly CONFLICT_DETAILS Default = new() { CD_ulSize = (uint)Marshal.SizeOf(typeof(CONFLICT_DETAILS)) }; } /// diff --git a/PInvoke/CfgMgr32/CfgMgr32.cs b/PInvoke/CfgMgr32/CfgMgr32.cs index c1144893..74b25c18 100644 --- a/PInvoke/CfgMgr32/CfgMgr32.cs +++ b/PInvoke/CfgMgr32/CfgMgr32.cs @@ -1743,6 +1743,55 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Free_Res_Des")] public static extern CONFIGRET CM_Free_Res_Des(out SafeRES_DES prdResDes, RES_DES rdResDes, uint ulFlags = 0); + /// The CM_Free_Res_Des function removes a resource descriptor from a logical configuration on the local machine. + /// + /// Caller-supplied location to receive a handle to the configuration's previous resource descriptor. This parameter can be + /// NULL. For more information, see the following Remarks section. + /// + /// + /// + /// Caller-supplied handle to the resource descriptor to be removed. This handle must have been previously obtained by calling one + /// of the following functions: + /// + /// CM_Add_Res_Des + /// CM_Add_Res_Des_Ex + /// CM_Get_Next_Res_Des + /// CM_Get_Next_Res_Des_Ex + /// CM_Modify_Res_Des + /// CM_Modify_Res_Des_Ex + /// + /// Not used, must be zero. + /// + /// + /// If the operation succeeds, the function returns CR_SUCCESS. Otherwise, it returns one of the CR_-prefixed error codes defined in Cfgmgr32.h. + /// + /// + /// Note Starting with Windows 8, CM_Free_Res_Des returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. To + /// request information about the hardware resources on a local machine it is necessary implement an architecture-native version of + /// the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// + /// + /// Resource descriptors for each configuration are stored in an array. If you specify an address for prdResDes, then + /// CM_Free_Res_Des returns a handle to the resource descriptor that was previous, in the array, to the one removed. If the + /// handle specified by rdResDes represents the resource descriptor located first in the array, then prdResDes receives a handle to + /// the logical configuration. + /// + /// + /// Note that calling CM_Free_Res_Des frees the resource descriptor, but not the descriptor's handle. To free the handle, + /// call CM_Free_Res_Des_Handle. + /// + /// + /// Callers of this function must have SeLoadDriverPrivilege. (Privileges are described in the Microsoft Windows SDK documentation.) + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_free_res_des CMAPI CONFIGRET CM_Free_Res_Des( PRES_DES + // prdResDes, RES_DES rdResDes, ULONG ulFlags ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Free_Res_Des")] + public static extern CONFIGRET CM_Free_Res_Des([In, Optional] IntPtr prdResDes, RES_DES rdResDes, uint ulFlags = 0); + /// /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Free_Res_Des instead.] /// @@ -1808,6 +1857,71 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Free_Res_Des_Ex")] public static extern CONFIGRET CM_Free_Res_Des_Ex(out SafeRES_DES prdResDes, RES_DES rdResDes, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// + /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Free_Res_Des instead.] + /// + /// The CM_Free_Res_Des_Ex function removes a resource descriptor from a logical configuration on either a local or a remote machine. + /// + /// + /// + /// Caller-supplied location to receive a handle to the configuration's previous resource descriptor. This parameter can be + /// NULL. For more information, see the following Remarks section. + /// + /// + /// + /// Caller-supplied handle to the resource descriptor to be removed. This handle must have been previously obtained by calling one + /// of the following functions: + /// + /// CM_Add_Res_Des + /// CM_Add_Res_Des_Ex + /// CM_Get_Next_Res_Des + /// CM_Get_Next_Res_Des_Ex + /// CM_Modify_Res_Des + /// CM_Modify_Res_Des_Ex + /// + /// Not used, must be zero. + /// + /// Caller-supplied machine handle, obtained from a previous call to CM_Connect_Machine. + /// + /// Note Using this function to access remote machines is not supported beginning with Windows 8 and Windows Server 2012, as + /// this functionality has been removed. + /// + /// + /// + /// + /// If the operation succeeds, the function returns CR_SUCCESS. Otherwise, it returns one of the CR_-prefixed error codes defined in Cfgmgr32.h. + /// + /// + /// Note Starting with Windows 8, CM_Free_Res_Des_Ex returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. To + /// request information about the hardware resources on a local machine it is necessary implement an architecture-native version of + /// the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// + /// + /// Resource descriptors for each configuration are stored in an array. If you specify an address for prdResDes, then + /// CM_Free_Res_Des returns a handle to the resource descriptor that was previous, in the array, to the one removed. If the + /// handle specified by rdResDes represents the resource descriptor located first in the array, then prdResDes receives a handle to + /// the logical configuration. + /// + /// + /// Note that calling CM_Free_Res_Des_Ex frees the resource descriptor, but not the descriptor's handle. To free the handle, + /// call CM_Free_Res_Des_Handle_Ex. + /// + /// + /// Callers of this function must have SeLoadDriverPrivilege. (Privileges are described in the Microsoft Windows SDK documentation.) + /// + /// + /// Functionality to access remote machines has been removed in Windows 8 and Windows Server 2012 and later operating systems thus + /// you cannot access remote machines when running on these versions of Windows. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_free_res_des_ex CMAPI CONFIGRET CM_Free_Res_Des_Ex( + // PRES_DES prdResDes, RES_DES rdResDes, ULONG ulFlags, HMACHINE hMachine ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Free_Res_Des_Ex")] + public static extern CONFIGRET CM_Free_Res_Des_Ex([In, Optional] IntPtr prdResDes, RES_DES rdResDes, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// /// The CM_Free_Res_Des_Handle function invalidates a resource description handle and frees its associated memory allocation. /// @@ -2289,7 +2403,7 @@ namespace Vanara.PInvoke // ulFlags, HMACHINE hMachine ); [DllImport(Lib_Cfgmgr32, SetLastError = false, CharSet = CharSet.Auto)] [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Class_Registry_PropertyW")] - public static extern CONFIGRET CM_Get_Class_Registry_Property(in Guid ClassGuid, CM_DRP ulProperty, out REG_VALUE_TYPE pulRegDataType, [Out, Optional] IntPtr Buffer, + public static extern CONFIGRET CM_Get_Class_Registry_Property(in Guid ClassGuid, CM_CRP ulProperty, out REG_VALUE_TYPE pulRegDataType, [Out, Optional] IntPtr Buffer, ref uint pulLength, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); /// diff --git a/PInvoke/CfgMgr32/CfgMgr32_2.cs b/PInvoke/CfgMgr32/CfgMgr32_2.cs index 89015139..5e13b6cc 100644 --- a/PInvoke/CfgMgr32/CfgMgr32_2.cs +++ b/PInvoke/CfgMgr32/CfgMgr32_2.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -356,7 +358,9 @@ namespace Vanara.PInvoke CM_PROB_GUEST_ASSIGNMENT_FAILED = 0x00000039, } - /// configuration flags + /// Configuration flags + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_HW_Prof_FlagsA")] + [Flags] public enum CSCONFIGFLAG { /// The device instance is disabled in the specified hardware profile. @@ -367,9 +371,6 @@ namespace Vanara.PInvoke /// The device cannot be started in the specified hardware profile. CSCONFIGFLAG_DO_NOT_START = 4, - - /// Bitwise OR of the other CSCONFIGFLAG_Xxx flags. - CSCONFIGFLAG_BITS = 7, } /// @@ -1575,6 +1576,68 @@ namespace Vanara.PInvoke public static extern CONFIGRET CM_Get_HW_Prof_Flags_Ex([MarshalAs(UnmanagedType.LPTStr)] string pDeviceID, uint ulHardwareProfile, out CSCONFIGFLAG pulValue, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// + /// The CM_Get_Log_Conf_List function obtains the logical configurations, of a specified configuration type, associated with + /// a specified device instance on the local machine. + /// + /// Caller-supplied device instance handle that is bound to the local machine. + /// + /// + /// Caller-supplied flag value indicating the type of logical configuration being requested. One of the flags in the following table + /// must be specified. + /// + /// + /// + /// Configuration Type Flags + /// Definitions + /// + /// + /// BASIC_LOG_CONF + /// The caller is requesting basic configuration information. + /// + /// + /// FILTERED_LOG_CONF + /// The caller is requesting filtered configuration information. + /// + /// + /// ALLOC_LOG_CONF + /// The caller is requesting allocated configuration information. + /// + /// + /// BOOT_LOG_CONF + /// The caller is requesting boot configuration information. + /// + /// + /// FORCED_LOG_CONF + /// The caller is requesting forced configuration information. + /// + /// + /// OVERRIDE_LOG_CONF + /// The caller is requesting override configuration information. + /// + /// + /// + /// + /// A sequence of safe handles to a logical configurations. + /// + /// Note Starting with Windows 8, CM_Get_First_Log_Conf returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. + /// To request information about the hardware resources on a local machine it is necessary implement an architecture-native version + /// of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_First_Log_Conf")] + public static IEnumerable CM_Get_Log_Conf_List(uint dnDevInst, LOG_CONF_FLAG ulFlags) + { + CONFIGRET ret = CM_Get_First_Log_Conf(out var lc, dnDevInst, ulFlags); + if (ret == CONFIGRET.CR_NO_MORE_LOG_CONF) + yield break; + ret.ThrowIfFailed(); + yield return lc; + while ((ret = CM_Get_Next_Log_Conf(out lc, lc)) == CONFIGRET.CR_SUCCESS) + yield return lc; + if (ret != CONFIGRET.CR_NO_MORE_LOG_CONF) ret.ThrowIfFailed(); + } + /// /// The CM_Get_Log_Conf_Priority function obtains the configuration priority of a specified logical configuration on the /// local machine. @@ -1937,6 +2000,54 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Next_Res_Des")] public static extern CONFIGRET CM_Get_Next_Res_Des(out SafeRES_DES prdResDes, RES_DES rdResDes, RESOURCEID ForResource, out RESOURCEID pResourceID, uint ulFlags = 0); + /// + /// The CM_Get_Next_Res_Des function obtains a handle to the next resource descriptor, of a specified resource type, for a + /// logical configuration on the local machine. + /// + /// Pointer to a location to receive a resource descriptor handle. + /// + /// Caller-supplied handle to either a resource descriptor or a logical configuration. For more information, see the following + /// Remarks section. + /// + /// + /// Caller-supplied resource type identifier, indicating the type of resource descriptor being requested. This must be one of the + /// ResType_-prefixed constants defined in Cfgmgr32.h. + /// + /// + /// Pointer to a location to receive a resource type identifier, if ForResource specifies ResType_All. For any other + /// ForResource value, callers should set this to NULL. + /// + /// Not used, must be zero. + /// + /// + /// If the operation succeeds, the function returns CR_SUCCESS. Otherwise, it returns one of the CR_-prefixed error codes defined in Cfgmgr32.h. + /// + /// + /// Note Starting with Windows 8, CM_Get_Next_Res_Des returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. + /// To request information about the hardware resources on a local machine it is necessary implement an architecture-native version + /// of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// + /// + /// To enumerate a logical configuration's resource descriptors, begin by calling CM_Get_Next_Res_Des with the logical + /// configuration's handle as the argument for rdResDes. This obtains a handle to the first resource descriptor of the type + /// specified by ForResource. Then for each subsequent call to CM_Get_Next_Res_Des, specify the most recently obtained + /// descriptor handle as the argument for rdResDes. Repeat until the function returns CR_NO_MORE_RES_DES. + /// + /// To retrieve the information stored in a resource descriptor, call CM_Get_Res_Des_Data. + /// To modify the information stored in a resource descriptor, call CM_Modify_Res_Des. + /// + /// Callers of CM_Get_Next_Res_Des must call CM_Free_Res_Des_Handle to deallocate the resource descriptor handle, after it is + /// no longer needed. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_get_next_res_des CMAPI CONFIGRET CM_Get_Next_Res_Des( + // PRES_DES prdResDes, RES_DES rdResDes, RESOURCEID ForResource, PRESOURCEID pResourceID, ULONG ulFlags ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Next_Res_Des")] + public static extern CONFIGRET CM_Get_Next_Res_Des(out SafeRES_DES prdResDes, LOG_CONF rdResDes, RESOURCEID ForResource, out RESOURCEID pResourceID, uint ulFlags = 0); + /// /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Get_Next_Res_Des instead.] /// @@ -2000,6 +2111,69 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Next_Res_Des_Ex")] public static extern CONFIGRET CM_Get_Next_Res_Des_Ex(out SafeRES_DES prdResDes, RES_DES rdResDes, RESOURCEID ForResource, out RESOURCEID pResourceID, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// + /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Get_Next_Res_Des instead.] + /// + /// The CM_Get_Next_Res_Des_Ex function obtains a handle to the next resource descriptor, of a specified resource type, for a + /// logical configuration on a local or a remote machine. + /// + /// + /// Pointer to a location to receive a resource descriptor handle. + /// + /// Caller-supplied handle to either a resource descriptor or a logical configuration. For more information, see the following + /// Remarks section. + /// + /// + /// Caller-supplied resource type identifier, indicating the type of resource descriptor being requested. This must be one of the + /// ResType_-prefixed constants defined in Cfgmgr32.h. + /// + /// + /// Pointer to a location to receive a resource type identifier, if ForResource specifies ResType_All. For any other + /// ForResource value, callers should set this to NULL. + /// + /// Not used, must be zero. + /// + /// Caller-supplied machine handle, obtained from a previous call to CM_Connect_Machine. + /// + /// Note Using this function to access remote machines is not supported beginning with Windows 8 and Windows Server 2012, as + /// this functionality has been removed. + /// + /// + /// + /// + /// If the operation succeeds, the function returns CR_SUCCESS. Otherwise, it returns one of the CR_-prefixed error codes defined in Cfgmgr32.h. + /// + /// + /// Note Starting with Windows 8, CM_Get_Next_Res_Des_Ex returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 + /// scenario. To request information about the hardware resources on a local machine it is necessary implement an + /// architecture-native version of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// + /// + /// To enumerate a logical configuration's resource descriptors, begin by calling CM_Get_Next_Res_Des_Ex with the logical + /// configuration's handle as the argument for rdResDes. This obtains a handle to the first resource descriptor of the type + /// specified by ForResource. Then for each subsequent call to CM_Get_Next_Res_Des_Ex, specify the most recently obtained + /// descriptor handle as the argument for rdResDes. Repeat until the function returns CR_NO_MORE_RES_DES. + /// + /// To retrieve the information stored in a resource descriptor, call CM_Get_Res_Des_Data_Ex. + /// To modify the information stored in a resource descriptor, call CM_Modify_Res_Des_Ex. + /// + /// Callers of CM_Get_Next_Res_Des_Ex must call CM_Free_Res_Des_Handle to deallocate the resource descriptor handle, after it + /// is no longer needed. + /// + /// + /// Functionality to access remote machines has been removed in Windows 8 and Windows Server 2012 and later operating systems thus + /// you cannot access remote machines when running on these versions of Windows. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_get_next_res_des_ex CMAPI CONFIGRET + // CM_Get_Next_Res_Des_Ex( PRES_DES prdResDes, RES_DES rdResDes, RESOURCEID ForResource, PRESOURCEID pResourceID, ULONG ulFlags, + // HMACHINE hMachine ); + [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Next_Res_Des_Ex")] + public static extern CONFIGRET CM_Get_Next_Res_Des_Ex(out SafeRES_DES prdResDes, LOG_CONF rdResDes, RESOURCEID ForResource, out RESOURCEID pResourceID, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Get_Parent instead.] /// @@ -2064,6 +2238,30 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Res_Des_Data")] public static extern CONFIGRET CM_Get_Res_Des_Data(RES_DES rdResDes, [Out] IntPtr Buffer, uint BufferLen, uint ulFlags = 0); + /// + /// The CM_Get_Res_Des_Data function retrieves the information stored in a resource descriptor on the local machine. + /// + /// The type of the data to retrieve. + /// Caller-supplied handle to a resource descriptor, obtained by a previous call to CM_Get_Next_Res_Des. + /// + /// + /// Returns the structure specified in as the contents of the resource descriptor. + /// + /// + /// Note Starting with Windows 8, CM_Get_Res_Des_Data throws CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. + /// To request information about the hardware resources on a local machine it is necessary implement an architecture-native version + /// of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Res_Des_Data")] + public static T CM_Get_Res_Des_Data(RES_DES rdResDes) where T : struct + { + CM_Get_Res_Des_Data_Size(out var size, rdResDes).ThrowIfFailed(); + using var mem = new SafeCoTaskMemHandle(size); + CM_Get_Res_Des_Data(rdResDes, mem, size).ThrowIfFailed(); + return mem.ToStructure(); + } + /// /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Get_Res_Des_Data instead.] /// @@ -2184,6 +2382,38 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Res_Des_Data_Size_Ex")] public static extern CONFIGRET CM_Get_Res_Des_Data_Size_Ex(out uint pulSize, RES_DES rdResDes, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + /// + /// The CM_Get_Next_Res_Des function obtains a handle to the next resource descriptor, of a specified resource type, for a + /// logical configuration on the local machine. + /// + /// Handle to a logical configuration. + /// + /// Caller-supplied resource type identifier, indicating the type of resource descriptor being requested. This must be one of the + /// ResType_-prefixed constants defined in Cfgmgr32.h. + /// + /// A sequence of resource descriptor handles and their associated resource type identifiers. + /// + /// + /// Note Starting with Windows 8, CM_Get_Next_Res_Des returns CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. + /// To request information about the hardware resources on a local machine it is necessary implement an architecture-native version + /// of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// To retrieve the information stored in a resource descriptor, call CM_Get_Res_Des_Data. + /// To modify the information stored in a resource descriptor, call CM_Modify_Res_Des. + /// + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Get_Next_Res_Des")] + public static IEnumerable<(SafeRES_DES prdResDes, RESOURCEID pResourceID)> CM_Get_Res_Des_List([In] LOG_CONF lcLogConf, RESOURCEID ForResource = RESOURCEID.ResType_All) + { + CONFIGRET ret = CM_Get_Next_Res_Des(out var rd, lcLogConf, ForResource, out var resId); + if (ret == CONFIGRET.CR_NO_MORE_RES_DES) + yield break; + ret.ThrowIfFailed(); + yield return (rd, resId); + while ((ret = CM_Get_Next_Res_Des(out rd, rd, ForResource, out resId)) == CONFIGRET.CR_SUCCESS) + yield return (rd, resId); + if (ret != CONFIGRET.CR_NO_MORE_RES_DES) ret.ThrowIfFailed(); + } + /// /// The CM_Get_Resource_Conflict_Count function obtains the number of conflicts contained in a specified resource conflict list. /// diff --git a/PInvoke/CfgMgr32/CfgMgr32_3.cs b/PInvoke/CfgMgr32/CfgMgr32_3.cs index 74532417..5d71ef13 100644 --- a/PInvoke/CfgMgr32/CfgMgr32_3.cs +++ b/PInvoke/CfgMgr32/CfgMgr32_3.cs @@ -2,6 +2,7 @@ using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; using System.Text; +using Vanara.InteropServices; using static Vanara.PInvoke.AdvApi32; using static Vanara.PInvoke.SetupAPI; @@ -1103,6 +1104,58 @@ namespace Vanara.PInvoke [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Modify_Res_Des")] public static extern CONFIGRET CM_Modify_Res_Des(out SafeRES_DES prdResDes, RES_DES rdResDes, RESOURCEID ResourceID, [In] IntPtr ResourceData, uint ResourceLen, uint ulFlags = 0); + /// The CM_Modify_Res_Des function modifies a specified resource descriptor on the local machine. + /// The type of the data. + /// + /// + /// Caller-supplied handle to the resource descriptor to be modified. This handle must have been previously obtained by calling one + /// of the following functions: + /// + /// CM_Add_Res_Des + /// CM_Add_Res_Des_Ex + /// CM_Get_Next_Res_Des + /// CM_Get_Next_Res_Des_Ex + /// CM_Modify_Res_Des + /// CM_Modify_Res_Des_Ex + /// + /// + /// Caller-supplied pointer to a resource descriptor, which can be one of the structures listed under the CM_Add_Res_Des function's + /// description of ResourceData. + /// + /// + /// Caller-supplied resource type identifier. This must be one of the ResType_-prefixed constants defined in Cfgmgr32.h. + /// + /// + /// A handle to the modified resource descriptor. + /// + /// Note Starting with Windows 8, CM_Modify_Res_Des throws CR_CALL_NOT_IMPLEMENTED when used in a Wow64 scenario. To + /// request information about the hardware resources on a local machine it is necessary implement an architecture-native version of + /// the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// Unable to determine RESOURCEID from type. - T + /// + /// + /// The caller-supplied resource descriptor data replaces the existing data. The values specified for ResourceID and ResourceLen do + /// not have to match the existing resource descriptor. + /// + /// + /// If the value specified for ResourceID is ResType_ClassSpecific, then the specified resource descriptor must be the last + /// one associated with the logical configuration. + /// + /// + /// Callers of this function must have SeLoadDriverPrivilege. (Privileges are described in the Microsoft Windows SDK documentation.) + /// + /// + public static SafeRES_DES CM_Modify_Res_Des(RES_DES rdResDes, T data, RESOURCEID ResourceID = 0) where T : struct + { + if (ResourceID == 0 && !CorrespondingTypeAttribute.CanSet(out ResourceID)) + throw new ArgumentException("Unable to determine RESOURCEID from type.", nameof(T)); + using var mem = new SafeAnysizeStruct(data); + CM_Modify_Res_Des(out var hRD, rdResDes, ResourceID, mem, mem.Size).ThrowIfFailed(); + return hRD; + } + /// /// [Beginning with Windows 8 and Windows Server 2012, this function has been deprecated. Please use CM_Modify_Res_Des instead.] /// The CM_Modify_Res_Des_Ex function modifies a specified resource descriptor on a local or a remote machine. @@ -1684,8 +1737,53 @@ namespace Vanara.PInvoke // ULONG ResourceLen, ULONG ulFlags, HMACHINE hMachine ); [DllImport(Lib_Cfgmgr32, SetLastError = false, ExactSpelling = true)] [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Query_Resource_Conflict_List")] - public static extern CONFIGRET CM_Query_Resource_Conflict_List(out SafeCONFLICT_LIST pclConflictList, uint dnDevInst, RESOURCEID ResourceID, IntPtr ResourceData, - uint ResourceLen, uint ulFlags, [In, Optional] HMACHINE hMachine); + public static extern CONFIGRET CM_Query_Resource_Conflict_List(out SafeCONFLICT_LIST pclConflictList, uint dnDevInst, RESOURCEID ResourceID, [In] IntPtr ResourceData, + uint ResourceLen, [In, Optional] uint ulFlags, [In, Optional] HMACHINE hMachine); + + /// + /// The CM_Query_Resource_Conflict_List function identifies device instances having resource requirements that conflict with + /// a specified device instance's resource description. + /// + /// The type of the supplied data. + /// Caller-supplied device instance handle that is bound to the machine handle supplied by hMachine. + /// + /// Caller-supplied pointer to a resource descriptor, which can be one of the structures listed under the CM_Add_Res_Des function's + /// description of ResourceData. + /// + /// + /// Caller-supplied resource type identifier. If 0, then the is determined by . + /// + /// + /// A handle to a conflict list. + /// + /// Note Starting with Windows 8, CM_Query_Resource_Conflict_List throws CR_CALL_NOT_IMPLEMENTED when used in a Wow64 + /// scenario. To request information about the hardware resources on a local machine it is necessary implement an + /// architecture-native version of the application using the hardware resource APIs. For example: An AMD64 application for AMD64 systems. + /// + /// + /// + /// + /// When calling CM_Query_Resource_Conflict_List, specify a device instance handle and resource descriptor. (Resource + /// descriptors for existing device nodes can be obtained by calling CM_Get_Res_Des_Data.) These parameters indicate the specific + /// resources you'd like a specific device to use. The resulting conflict list identifies devices that use the same resources, along + /// with resources reserved by the machine. + /// + /// + /// After calling CM_Query_Resource_Conflict_List, an application can call CM_Get_Resource_Conflict_Count to determine the + /// number of conflicts contained in the resource conflict list. (The number of conflicts can be zero.) Then the application can + /// call CM_Get_Resource_Conflict_Details for each entry in the conflict list. + /// + /// For information about using device instance handles that are bound to a local or a remote machine, see CM_Get_Child_Ex. + /// + [PInvokeData("cfgmgr32.h", MSDNShortId = "NF:cfgmgr32.CM_Query_Resource_Conflict_List")] + public static SafeCONFLICT_LIST CM_Query_Resource_Conflict_List(uint dnDevInst, T data, RESOURCEID ResourceID = 0) where T : struct + { + if (ResourceID == 0 && !CorrespondingTypeAttribute.CanSet(out ResourceID)) + throw new ArgumentException("Unable to determine RESOURCEID from type.", nameof(T)); + using var mem = new SafeAnysizeStruct(data); + CM_Query_Resource_Conflict_List(out var hcl, dnDevInst, ResourceID, mem, mem.Size).ThrowIfFailed(); + return hcl; + } /// /// The CM_Reenumerate_DevNode function enumerates the devices identified by a specified device node and all of its children. diff --git a/PInvoke/CfgMgr32/SWDevice.cs b/PInvoke/CfgMgr32/SWDevice.cs index 750f2901..ade8dfcb 100644 --- a/PInvoke/CfgMgr32/SWDevice.cs +++ b/PInvoke/CfgMgr32/SWDevice.cs @@ -522,6 +522,7 @@ namespace Vanara.PInvoke /// 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. /// + [MarshalAs(UnmanagedType.LPWStr)] public string pszInstanceId; /// @@ -529,8 +530,9 @@ namespace Vanara.PInvoke /// 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; + //[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullTermStringArrayMarshaler))] + //public string[] pszzHardwareIds; + public IntPtr pszzHardwareIds; /// /// A list of strings for the compatible IDs for the software device. This value is used for IRP_MN_QUERY_ID @@ -539,8 +541,9 @@ namespace Vanara.PInvoke /// 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; + //[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullTermStringArrayMarshaler))] + //public string[] pszzCompatibleIds; + public IntPtr 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 @@ -616,6 +619,7 @@ namespace Vanara.PInvoke /// We recommend that this string be a reference to a localizable resource. For the syntax of referencing resources, see DEVPROP_TYPE_STRING_INDIRECT. /// /// + [MarshalAs(UnmanagedType.LPWStr)] public string pszDeviceDescription; /// @@ -625,6 +629,7 @@ namespace Vanara.PInvoke /// /// Note Specifying a location is uncommon. /// + [MarshalAs(UnmanagedType.LPWStr)] public string pszDeviceLocation; /// diff --git a/UnitTests/PInvoke/CfgMgr32/CfgMgr32Tests.cs b/UnitTests/PInvoke/CfgMgr32/CfgMgr32Tests.cs index 2ab726ff..ac056a46 100644 --- a/UnitTests/PInvoke/CfgMgr32/CfgMgr32Tests.cs +++ b/UnitTests/PInvoke/CfgMgr32/CfgMgr32Tests.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; -using System.Security.AccessControl; using System.Text; using Vanara.Extensions; using Vanara.InteropServices; @@ -19,89 +17,93 @@ namespace Vanara.PInvoke.Tests { private static readonly Guid devguid = GUID_DEVCLASS_DISKDRIVE; private static readonly Lazy leaf = new(() => GetFirstLeaf(root.Value)); - private static readonly Lazy node = new(() => LocateNode(@"USB\ROOT_HUB30\4&3490C39&0&0")); + private static readonly Lazy node = new(() => LocateNode(@"SWD\PRINTENUM\WSD-F442E38C-F139-42D3-BCCC-A3653929E4B7")); //@"USB\ROOT_HUB30\4&3490C39&0&0")); private static readonly Lazy root = new(() => LocateNode()); private ElevPriv priv; public static uint LeafId => leaf.Value; - public static uint NodeId => node.Value; - public static uint RootId => root.Value; - private static uint LocateNode(string devId = null) - { - CM_Locate_DevNode(out var di, devId, CM_LOCATE_DEVNODE.CM_LOCATE_DEVNODE_NORMAL).ThrowIfFailed(); - return di; - } - [OneTimeSetUp] - public void _Setup() - { - priv = new ElevPriv("SeLoadDriverPrivilege"); - } + public void _Setup() => priv = new ElevPriv("SeLoadDriverPrivilege"); [OneTimeTearDown] - public void _TearDown() - { - priv?.Dispose(); - } + public void _TearDown() => priv?.Dispose(); - [Test] - public void StructSizeTest() => TestContext.Write(string.Join("\n", TestHelper.GetNestedStructSizes(typeof(CfgMgr32)))); + public void ClassesWithInterfacesTest() + { + foreach (System.Reflection.FieldInfo fi in typeof(SetupAPI).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public). + Where(fi => fi.FieldType == typeof(Guid) && fi.Name.StartsWith("GUID_DEVINTERFACE_"))) + { + CONFIGRET ret = CM_Get_Device_Interface_List_Size(out var len, (Guid)fi.GetValue(null), default, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (ret == CONFIGRET.CR_SUCCESS && len > 2) + TestContext.WriteLine($"{fi.Name}={len}"); + } + } [Test] public void CM_Add_Empty_Log_ConfTest() { - Assert.That(CM_Add_Empty_Log_Conf(out SafeLOG_CONF hConf, LeafId, PRIORITY.LCPRI_NORMAL, LOG_CONF_FLAG.BASIC_LOG_CONF), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - Assert.That(() => hConf.Dispose(), Throws.Nothing); + Assert.That(CM_Add_Empty_Log_Conf(out SafeLOG_CONF hConf, NodeId, PRIORITY.LCPRI_NORMAL, LOG_CONF_FLAG.BASIC_LOG_CONF), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + try + { + Assert.That(CM_Get_Log_Conf_Priority(hConf, out PRIORITY p), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(PRIORITY.LCPRI_NORMAL, Is.EqualTo(p)); + } + finally + { + Assert.That(CM_Free_Log_Conf(hConf), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(() => hConf.Dispose(), Throws.Nothing); + } } [Test] public void CM_Add_Res_DesTest() { - Assert.That(CM_Add_Empty_Log_Conf(out var BootLC1, NodeId, PRIORITY.LCPRI_NORMAL, LOG_CONF_FLAG.BOOT_LOG_CONF), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - - var memRes = new MEM_RESOURCE + Assert.That(CM_Add_Empty_Log_Conf(out SafeLOG_CONF BootLC1, NodeId, PRIORITY.LCPRI_BOOTCONFIG, LOG_CONF_FLAG.BOOT_LOG_CONF | LOG_CONF_FLAG.PRIORITY_EQUAL_FIRST), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + try { - MEM_Header = new MEM_DES - { - MD_Count = 2, - MD_Type = MType_Range, - MD_Alloc_Base = 0xD8000, - MD_Alloc_End = 0xD9000, - MD_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, - MD_Reserved = 0 - }, - MEM_Data = new MEM_RANGE[] - { - new MEM_RANGE - { - MR_Align = 8, //? - MR_nBytes = 4096, - MR_Min = 0xD8000, - MR_Max = 0xDC000, - MR_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, - MR_Reserved = 0 - }, + MEM_RESOURCE memRes = MakeResource(); - new MEM_RANGE - { - MR_Align = 8, //? - MR_nBytes = 4096, - MR_Min = 0xE0000, - MR_Max = 0xE4000, - MR_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, - MR_Reserved = 0 - } + SafeRES_DES hRD = null; + Assert.That(() => hRD = CM_Add_Res_Des(BootLC1, memRes), Throws.Nothing); + try + { + var list = new (SafeRES_DES prdResDes, RESOURCEID pResourceID)[0]; + Assert.That(() => list = CM_Get_Res_Des_List(BootLC1).ToArray(), Throws.Nothing); + Assert.That(list, Is.Not.Empty); + + Assert.That(list[0].pResourceID, Is.EqualTo(RESOURCEID.ResType_Mem)); + MEM_RESOURCE retMemRes = default; + Assert.That(() => retMemRes = CM_Get_Res_Des_Data(list[0].prdResDes), Throws.Nothing); + Assert.That(retMemRes.MEM_Data.Length, Is.EqualTo(memRes.MEM_Data.Length)); + + memRes.MEM_Data[0].MR_Min = 0xDA00; + memRes.MEM_Data[0].MR_Max = 0xDE00; + Assert.That(() => hRD = CM_Modify_Res_Des(list[0].prdResDes, memRes), Throws.Nothing); } - }; + finally + { + CM_Free_Res_Des(IntPtr.Zero, hRD); + hRD?.Dispose(); + } + } + finally + { + CM_Free_Log_Conf(BootLC1); + BootLC1.Dispose(); + } + } - SafeRES_DES hRD = null; - Assert.That(() => hRD = CM_Add_Res_Des(BootLC1, memRes), Throws.Nothing); - hRD?.Dispose(); - BootLC1.Dispose(); + [Test] + public void CM_Clear_Log_Conf_List() + { + var lc = new SafeLOG_CONF[0]; + Assert.That(() => lc = CM_Get_Log_Conf_List(NodeId, LOG_CONF_FLAG.BOOT_LOG_CONF).ToArray(), Throws.Nothing); + foreach (SafeLOG_CONF l in lc) + CM_Free_Log_Conf(l); } [Test] @@ -114,8 +116,8 @@ namespace Vanara.PInvoke.Tests [Test] public void CM_Enable_DevNodeTest() { - Assert.That(CM_Disable_DevNode(LeafId), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - Assert.That(CM_Enable_DevNode(LeafId), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(CM_Disable_DevNode(NodeId), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(CM_Enable_DevNode(NodeId), Is.EqualTo(CONFIGRET.CR_SUCCESS)); } [Test] @@ -160,19 +162,6 @@ namespace Vanara.PInvoke.Tests } } - [Test] - public void CM_Get_Class_PropertyTest() - { - var cls = devguid; - var pkey = DEVPKEY_DeviceClass_IconPath; - var sz = 0U; - Assert.That(CM_Get_Class_Property(cls, pkey, out var dpType, default, ref sz, CM_CLASS_PROPERTY.CM_CLASS_PROPERTY_INSTALLER), Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); - using var mem = new SafeCoTaskMemHandle(sz * StringHelper.GetCharSize()); - Assert.That(CM_Get_Class_Property(cls, pkey, out _, mem, ref sz, CM_CLASS_PROPERTY.CM_CLASS_PROPERTY_INSTALLER), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - TestContext.WriteLine($"{cls}: Type={dpType}, Size={sz}"); - mem.ToStringEnum().WriteValues(); - } - [Test] public void CM_Get_Class_Property_KeysTest() { @@ -184,28 +173,57 @@ namespace Vanara.PInvoke.Tests } [Test] - public void CM_Get_Class_Registry_PropertyTest() + public void CM_GetSet_Class_PropertyTest() { - var cls = devguid; - var prop = CM_DRP.CM_DRP_CAPABILITIES; + Guid cls = devguid; + DEVPROPKEY pkey = DEVPKEY_DeviceClass_IconPath; + + // Get value var sz = 0U; - Assert.That(CM_Get_Class_Registry_Property(cls, prop, out var dpType, default, ref sz), Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); - using var mem = new SafeCoTaskMemHandle(sz * StringHelper.GetCharSize()); - Assert.That(CM_Get_Class_Registry_Property(cls, prop, out _, mem, ref sz), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - TestContext.WriteLine($"{cls}: Type={dpType}, Size={sz}"); - TestContext.Write(mem.Dump); + Assert.That(CM_Get_Class_Property(cls, pkey, out DEVPROPTYPE dpType, default, ref sz, CM_CLASS_PROPERTY.CM_CLASS_PROPERTY_INSTALLER), Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); + var strings = new string[0]; + using (var mem = new SafeCoTaskMemHandle(sz * StringHelper.GetCharSize())) + { + Assert.That(CM_Get_Class_Property(cls, pkey, out _, mem, ref sz, CM_CLASS_PROPERTY.CM_CLASS_PROPERTY_INSTALLER), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + TestContext.WriteLine($"{cls}: Type={dpType}, Size={sz}"); + strings = mem.ToStringEnum().ToArray(); + strings.WriteValues(); + } + + // Set bogus value + using (var mem = SafeCoTaskMemHandle.CreateFromStringList(new[] { "A", "B" })) + { + Assert.That(CM_Set_Class_Property(devguid, pkey, DEVPROPTYPE.DEVPROP_TYPE_STRING_LIST, mem, mem.Size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + } + + // Reset correct value + using (var mem = SafeCoTaskMemHandle.CreateFromStringList(strings)) + { + Assert.That(CM_Set_Class_Property(devguid, pkey, DEVPROPTYPE.DEVPROP_TYPE_STRING_LIST, mem, mem.Size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + } } [Test] - public void CM_Get_Device_IDTest() + public void CM_GetSet_Class_Registry_PropertyTest() { - var di = RootId; - Assert.That(CM_Get_Device_ID_Size(out var len, di), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - Assert.That(len, Is.Not.Zero); - var sb = new StringBuilder((int)len+1); - Assert.That(CM_Get_Device_ID(di, sb, len+1), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - Assert.That(sb.Length, Is.Not.Zero); - TestContext.Write($"ID: {di} ({sb})"); + Guid cls = devguid; + CM_CRP prop = CM_CRP.CM_CRP_CHARACTERISTICS; + + var sz = 0U; + Assert.That(CM_Get_Class_Registry_Property(cls, prop, out _, default, ref sz), Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); + uint val = 0; + using (var mem = new SafeCoTaskMemHandle(sz * StringHelper.GetCharSize())) + { + Assert.That(CM_Get_Class_Registry_Property(cls, prop, out REG_VALUE_TYPE dpType, mem, ref sz), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + TestContext.WriteLine($"{cls}: Type={dpType}, Size={sz}"); + TestContext.Write(val = (uint)dpType.GetValue(mem, mem.Size)); + } + + using (var mem = SafeCoTaskMemHandle.CreateFromStructure(val > 0 ? 0U : 256)) + Assert.That(CM_Set_Class_Registry_Property(cls, prop, mem, mem.Size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + + using (var mem = SafeCoTaskMemHandle.CreateFromStructure(val)) + Assert.That(CM_Set_Class_Registry_Property(cls, prop, mem, mem.Size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); } [Test] @@ -217,99 +235,131 @@ namespace Vanara.PInvoke.Tests TestContext.Write(string.Join("\n", mem.ToStringEnum())); } + [Test] + public void CM_Get_Device_IDTest() + { + var di = RootId; + Assert.That(CM_Get_Device_ID_Size(out var len, di), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(len, Is.Not.Zero); + var sb = new StringBuilder((int)len + 1); + Assert.That(CM_Get_Device_ID(di, sb, len + 1), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(sb.Length, Is.Not.Zero); + TestContext.Write($"ID: {di} ({sb})"); + } + [Test] public void CM_Get_Device_Interface_AliasTest() { //Assert.That(CM_Get_Device_Interface_Alias(), Is.EqualTo(CONFIGRET.CR_SUCCESS)); } - public void ClassesWithInterfacesTest() + [Test] + public void CM_Get_Device_Interface_PropertiesTest() => Assert.That(() => { - foreach (var fi in typeof(SetupAPI).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public). - Where(fi => fi.FieldType == typeof(Guid) && fi.Name.StartsWith("GUID_DEVINTERFACE_"))) + foreach (var devInt in CM_Get_Device_Interface_List(GUID_DEVINTERFACE_NET, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) { - var ret = CM_Get_Device_Interface_List_Size(out var len, (Guid)fi.GetValue(null), default, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT); - if (ret == CONFIGRET.CR_SUCCESS && len > 2) - TestContext.WriteLine($"{fi.Name}={len}"); + TestContext.WriteLine(devInt); + + uint size = 0; + CONFIGRET ret = CM_Get_Device_Interface_Property_Keys(devInt, default, ref size); + if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; + Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); + Assert.That(size, Is.GreaterThan(0)); + var keys = new DEVPROPKEY[size]; + Assert.That(CM_Get_Device_Interface_Property_Keys(devInt, keys, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + + foreach (DEVPROPKEY key in keys) + { + TestContext.Write($" {key} ({(key.TryGetReadOnly(out var ro) ? (ro ? "R" : "RW") : "?")}): "); + size = 0; + ret = CM_Get_Device_Interface_Property(devInt, key, out DEVPROPTYPE pType, default, ref size); + if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; + Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); + Assert.That(size, Is.GreaterThan(0)); + using var mem = new SafeCoTaskMemHandle((int)size); + Assert.That(CM_Get_Device_Interface_Property(devInt, key, out _, mem, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + try + { + var o = pType.GetObject(mem); + TestContext.WriteLine(o is null ? "(null)" : (o.GetType().IsArray ? string.Join(", ", ((Array)o).Cast()) : (o is string s ? $"\"{s}\"" : o))); + } + catch { TestContext.WriteLine($"{pType} CONVERSION FAILED ({mem.Size})"); } + } + } + }, Throws.Nothing); + + [Test] + public void CM_Set_Device_Interface_PropertyTest() + { + var devInt = CM_Get_Device_Interface_List(GUID_DEVINTERFACE_VOLUME, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT).First(); + DEVPROPKEY pkey = DEVPKEY_DeviceInterface_FriendlyName; + using var mem = new SafeCoTaskMemString(256, System.Runtime.InteropServices.CharSet.Auto); + uint sz = mem.Size; + Assert.That(CM_Get_Device_Interface_Property(devInt, pkey, out var dpt, mem, ref sz), Is.EqualTo(CONFIGRET.CR_SUCCESS).Or.EqualTo(CONFIGRET.CR_NO_SUCH_VALUE)); + var orig = mem.ToString(); + mem.Set("DUMMY_VALUE$$"); + Assert.That(CM_Set_Device_Interface_Property(devInt, pkey, DEVPROPTYPE.DEVPROP_TYPE_STRING, mem, (uint)((mem.Length + 1) * StringHelper.GetCharSize())), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + if (string.IsNullOrEmpty(orig)) + Assert.That(CM_Set_Device_Interface_Property(devInt, pkey, DEVPROPTYPE.DEVPROP_TYPE_STRING, IntPtr.Zero, 0), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + else + { + mem.Set(orig); + Assert.That(CM_Set_Device_Interface_Property(devInt, pkey, DEVPROPTYPE.DEVPROP_TYPE_STRING, mem, (uint)((mem.Length + 1) * StringHelper.GetCharSize())), Is.EqualTo(CONFIGRET.CR_SUCCESS)); } } [Test] - public void CM_Get_Device_Interface_PropertiesTest() + public void CM_Set_DevNode_PropertyTest() { - Assert.That(() => + var devInt = NodeId; + var pkey = DEVPKEY_DeviceInterface_Enabled; + unsafe { - foreach (var devInt in CM_Get_Device_Interface_List(GUID_DEVINTERFACE_VOLUME, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) - { - TestContext.WriteLine(devInt); - - uint size = 0; - var ret = CM_Get_Device_Interface_Property_Keys(devInt, default, ref size); - if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; - Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); - Assert.That(size, Is.GreaterThan(0)); - var keys = new DEVPROPKEY[size]; - Assert.That(CM_Get_Device_Interface_Property_Keys(devInt, keys, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - - foreach (var key in keys) - { - TestContext.Write($" {key}: "); - size = 0; - ret = CM_Get_Device_Interface_Property(devInt, key, out DEVPROPTYPE pType, default, ref size); - if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; - Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); - Assert.That(size, Is.GreaterThan(0)); - using var mem = new SafeCoTaskMemHandle((int)size); - Assert.That(CM_Get_Device_Interface_Property(devInt, key, out _, mem, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - try - { - var o = pType.GetObject(mem); - TestContext.WriteLine(o is null ? "(null)" : (o.GetType().IsArray ? string.Join(", ", ((Array)o).Cast()) : (o is string s ? $"\"{s}\"" : o))); - } catch { TestContext.WriteLine($"{pType} CONVERSION FAILED ({mem.Size})"); } - } - } - }, Throws.Nothing); + byte boolVal = 0; + uint sz = 1; + Assert.That(CM_Get_DevNode_Property(devInt, pkey, out var dpt, (IntPtr)(&boolVal), ref sz), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + byte newBoolVal = boolVal == 0 ? (byte)1 : (byte)0; + Assert.That(CM_Set_DevNode_Property(devInt, pkey, dpt, (IntPtr)(&newBoolVal), 1), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(CM_Set_DevNode_Property(devInt, pkey, dpt, (IntPtr)(&boolVal), 1), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + } } [Test] - public void CM_Get_DevNode_PropertiesTest() + public void CM_Get_DevNode_PropertiesTest() => Assert.That(() => { - Assert.That(() => + foreach (var devId in CM_Get_Device_ID_List()) { - foreach (var devId in CM_Get_Device_ID_List()) - { - if (CM_Locate_DevNode(out var devInst, devId) != CONFIGRET.CR_SUCCESS) - continue; - TestContext.WriteLine($"{devId} ({devInst})"); + if (CM_Locate_DevNode(out var devInst, devId) != CONFIGRET.CR_SUCCESS) + continue; + TestContext.WriteLine($"{devId} ({devInst})"); - uint size = 0; - var ret = CM_Get_DevNode_Property_Keys(devInst, default, ref size); + uint size = 0; + CONFIGRET ret = CM_Get_DevNode_Property_Keys(devInst, default, ref size); + if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; + Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); + Assert.That(size, Is.GreaterThan(0)); + var keys = new DEVPROPKEY[size]; + Assert.That(CM_Get_DevNode_Property_Keys(devInst, keys, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + + foreach (DEVPROPKEY key in keys) + { + TestContext.Write($" {key}: "); + size = 0; + ret = CM_Get_DevNode_Property(devInst, key, out DEVPROPTYPE pType, default, ref size); if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); Assert.That(size, Is.GreaterThan(0)); - var keys = new DEVPROPKEY[size]; - Assert.That(CM_Get_DevNode_Property_Keys(devInst, keys, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - - foreach (var key in keys) + using var mem = new SafeCoTaskMemHandle((int)size); + Assert.That(CM_Get_DevNode_Property(devInst, key, out _, mem, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + try { - TestContext.Write($" {key}: "); - size = 0; - ret = CM_Get_DevNode_Property(devInst, key, out DEVPROPTYPE pType, default, ref size); - if (ret == CONFIGRET.CR_NO_SUCH_VALUE) continue; - Assert.That(ret, Is.EqualTo(CONFIGRET.CR_BUFFER_SMALL)); - Assert.That(size, Is.GreaterThan(0)); - using var mem = new SafeCoTaskMemHandle((int)size); - Assert.That(CM_Get_DevNode_Property(devInst, key, out _, mem, ref size), Is.EqualTo(CONFIGRET.CR_SUCCESS)); - try - { - var o = pType.GetObject(mem); - TestContext.WriteLine(o is null ? "(null)" : (o.GetType().IsArray ? string.Join(", ", ((Array)o).Cast()) : (o is string s ? $"\"{s}\"" : o))); - } - catch { TestContext.WriteLine($"{pType} CONVERSION FAILED ({mem.Size})"); } + var o = pType.GetObject(mem); + TestContext.WriteLine(o is null ? "(null)" : (o.GetType().IsArray ? string.Join(", ", ((Array)o).Cast()) : (o is string s ? $"\"{s}\"" : o))); } + catch { TestContext.WriteLine($"{pType} CONVERSION FAILED ({mem.Size})"); } } - }, Throws.Nothing); - } + } + }, Throws.Nothing); [Test] public void CM_Get_DevNode_Registry_PropertyTest() @@ -334,6 +384,69 @@ namespace Vanara.PInvoke.Tests TestContext.Write($"{stat} : {prob}"); } + [Test] + public void CM_Get_HW_Prof_FlagsTest() + { + Assert.That(CM_Get_HW_Prof_Flags(@"USB\ROOT_HUB30\4&3490C39&0&0", 0, out CSCONFIGFLAG cf), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(cf & (CSCONFIGFLAG.CSCONFIGFLAG_DISABLED | CSCONFIGFLAG.CSCONFIGFLAG_DO_NOT_CREATE | CSCONFIGFLAG.CSCONFIGFLAG_DO_NOT_START), Is.Not.Zero); + } + + //[Test] + //public void CM_Get_Log_Conf_ListTest() + //{ + // //Assert.That(CM_Add_Empty_Log_Conf(out SafeLOG_CONF hConf, NodeId, PRIORITY.LCPRI_NORMAL, LOG_CONF_FLAG.BASIC_LOG_CONF), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + // //using (hConf) + // { + // var lc = new SafeLOG_CONF[0]; + // Assert.That(() => lc = CM_Get_Log_Conf_List(NodeId, LOG_CONF_FLAG.BOOT_LOG_CONF).ToArray(), Throws.Nothing); + // Assert.That(lc.Length, Is.GreaterThanOrEqualTo(1)); + // Assert.That(() => { foreach (var l in lc) l.Dispose(); }, Throws.Nothing); + // } + //} + //[Test] + //public void CM_Get_Log_Conf_PriorityTest() + //{ + // Assert.That(CM_Add_Empty_Log_Conf(out SafeLOG_CONF hConf, NodeId, PRIORITY.LCPRI_NORMAL, LOG_CONF_FLAG.BASIC_LOG_CONF), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + // using (hConf) + // { + // using var lc = CM_Get_Log_Conf_List(NodeId, LOG_CONF_FLAG.BASIC_LOG_CONF).First(); + // Assert.That(CM_Get_Log_Conf_Priority(lc, out var p), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + // Assert.IsTrue(Enum.IsDefined(typeof(PRIORITY), p)); + // } + //} + + [Test] + public void CM_Get_Resource_Conflict_DetailsTest() + { + SafeCONFLICT_LIST hcl = null; + MEM_RESOURCE memRes = MakeResource(); + Assert.That(() => hcl = CM_Query_Resource_Conflict_List(NodeId, memRes), Throws.Nothing); + try + { + Assert.That(CM_Get_Resource_Conflict_Count(hcl, out var count), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(count, Is.GreaterThan(0)); + CONFLICT_DETAILS det = new(CM_CDMASK.CM_CDMASK_DESCRIPTION | CM_CDMASK.CM_CDMASK_DEVINST | CM_CDMASK.CM_CDMASK_FLAGS | CM_CDMASK.CM_CDMASK_RESDES); + Assert.That(CM_Get_Resource_Conflict_Details(hcl, 0, ref det), ResultIs.Successful); + Assert.That(det.CD_dnDevInst, Is.Not.Zero); + Assert.That(det.CD_szDescription, Is.Not.Null); + det.WriteValues(); + } + finally + { + hcl?.Dispose(); + } + } + + [Test] + public void CM_Is_Dock_Station_PresentTest() + { + Assert.That(CM_Is_Dock_Station_Present(out var present), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + TestContext.Write($"Dock present={present}"); + } + + [Test] + public void CM_Is_Version_AvailableTest() => Assert.That(CM_Is_Version_Available(0x0501), Is.True); + [Test] public void CM_Locate_DevNodeTest() { @@ -341,9 +454,55 @@ namespace Vanara.PInvoke.Tests Assert.That(di, Is.Not.Zero); } + [Test] + public void CM_Open_Class_KeyTest() + { + Assert.That(CM_Open_Class_Key(GUID_DEVCLASS_USB, null, AdvApi32.REGSAM.KEY_READ, REGDISPOSITION.RegDisposition_OpenExisting, + out var hReg, CM_OPEN_CLASS_KEY.CM_OPEN_CLASS_KEY_INSTALLER), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + Assert.That(hReg, ResultIs.ValidHandle); + hReg.Dispose(); + } + + [Test] + public void CM_Open_Device_Interface_KeyTest() + { + var devInf = CM_Get_Device_Interface_List(GUID_DEVINTERFACE_USB_DEVICE, CM_GET_DEVICE_INTERFACE_LIST.CM_GET_DEVICE_INTERFACE_LIST_PRESENT).First(); + Assert.That(CM_Open_Device_Interface_Key(devInf, AdvApi32.REGSAM.KEY_READ, REGDISPOSITION.RegDisposition_OpenExisting, + out var hReg), Is.EqualTo(CONFIGRET.CR_SUCCESS).Or.EqualTo(CONFIGRET.CR_NO_SUCH_REGISTRY_KEY)); + //Assert.That(hReg, ResultIs.ValidHandle); + hReg?.Dispose(); + } + + [Test] + public void CM_Open_DevNode_KeyTest() + { + Assert.That(CM_Open_DevNode_Key(NodeId, AdvApi32.REGSAM.KEY_READ, 0, REGDISPOSITION.RegDisposition_OpenExisting, out var hReg, CM_REGISTRY.CM_REGISTRY_USER), + Is.EqualTo(CONFIGRET.CR_SUCCESS).Or.EqualTo(CONFIGRET.CR_NO_SUCH_REGISTRY_KEY)); + hReg?.Dispose(); + } + + [Test] + public void CM_Query_And_Remove_SubTreeTest() + { + //Assert.That(CM_Query_And_Remove_SubTree(LeafId, out var veto, null, 0, CM_REMOVE.), ResultIs.Successful); + } + [Test] public void CM_Reenumerate_DevNodeTest() => Assert.That(CM_Reenumerate_DevNode(RootId, CM_REENUMERATE.CM_REENUMERATE_SYNCHRONOUS), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + [Test] + public void CM_Request_Device_EjectTest() + { + var sb = new StringBuilder(260); + Assert.That(CM_Request_Device_Eject(NodeId, out var veto, sb, (uint)sb.Capacity), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + } + + [Test] + public void CM_Request_Eject_PCTest() + { + Assert.That(CM_Request_Eject_PC(), Is.EqualTo(CONFIGRET.CR_SUCCESS)); + } + [Test] public void RegisterAllInterfacesTest() { @@ -354,6 +513,9 @@ namespace Vanara.PInvoke.Tests GC.KeepAlive(callback); } + [Test] + public void StructSizeTest() => TestContext.Write(string.Join("\n", TestHelper.GetNestedStructSizes(typeof(CfgMgr32)))); + private static IEnumerable CM_GetChildren(uint dnDevInst) { CONFIGRET ret = CM_Get_Child(out var di, dnDevInst); @@ -375,6 +537,47 @@ namespace Vanara.PInvoke.Tests return ret == CONFIGRET.CR_NO_SUCH_DEVNODE ? di : throw ret.GetException(); } + private static uint LocateNode(string devId = null) + { + CM_Locate_DevNode(out var di, devId, CM_LOCATE_DEVNODE.CM_LOCATE_DEVNODE_NORMAL).ThrowIfFailed(); + return di; + } + + private static MEM_RESOURCE MakeResource() => new() + { + MEM_Header = new MEM_DES + { + MD_Count = 2, + MD_Type = MType_Range, + MD_Alloc_Base = 0xD8000, + MD_Alloc_End = 0xD9000, + MD_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, + MD_Reserved = 0 + }, + MEM_Data = new MEM_RANGE[] + { + new MEM_RANGE + { + MR_Align = 8, //? + MR_nBytes = 4096, + MR_Min = 0xD8000, + MR_Max = 0xDC000, + MR_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, + MR_Reserved = 0 + }, + + new MEM_RANGE + { + MR_Align = 8, //? + MR_nBytes = 4096, + MR_Min = 0xE0000, + MR_Max = 0xE4000, + MR_Flags = MEM_DES_FLAGS.fMD_ROM | MEM_DES_FLAGS.fMD_32 | MEM_DES_FLAGS.fMD_ReadAllowed, + MR_Reserved = 0 + } + } + }; + private Win32Error Notification(HCMNOTIFICATION notify, IntPtr context, CM_NOTIFY_ACTION action, in CM_NOTIFY_EVENT_DATA eventData, uint eventDataSize) { switch (eventData.FilterType)