Mit VBnet ermitteln der Soundeinstellungen (Win11)

  • VB.NET

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von dherr.

    Mit VBnet ermitteln der Soundeinstellungen (Win11)

    Hallo zusammen,

    Windows zeigt in den Einstellungen für Sound alle im System verfügbaren Lautsprecher, siehe dazu den Screenshot.
    Meine Frage ist: Kann man mittels VBnet genau diese Einstellungen abfragen und dann eventuell in einer ListBox anzeigen?
    Bin für einen Tipp dankbar!

    Grüße - Dietrich

    *Topic verschoben*
    Bilder
    • Sound Einstellungen.png

      13,38 kB, 657×444, 139 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @dherr k.A. ob @RodFromGermany dafür das gleiche nutzt. Mein erster Gedanke geht hier her: learn.microsoft.com/en-us/wind…enumerating-audio-devices Mein zweiter Gedanke geht in Richtung WinRT: learn.microsoft.com/de-de/uwp/…meration?view=winrt-22621 Hier würde ich dich sogar auf den DevicePicker verweisen der einen Dialog mit den entsprechenden Geräten (Filter) anzeigt.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    Hi

    Solche Sachen sind standardmäßig nicht in .NET vorhanden. Das muss man entweder selbst programmieren oder falls vorhanden, entsprechende Nuget-Pakete oder Drittanbieter-DLLs verwenden. Ich bin mir sehr sicher das Du einige Beispiele zu den von mir vorgeschlagenen Möglichkeiten im Internet findest.
    Mfg -Franky-
    @dherr Weil heute Sonntag ist und ich etwas Zeit hatte. Auf die schnelle zusammengeklöppelt und alles in einer Form. Daher sicher Verbesserungswürdig. Einige Interface-Funktionen sind nicht komplett da diese sowieso nicht benötigt werden. Wenn man die entsprechenden PKeys kennt, kann man die Info direkt auslesen anstatt durch alle Properties zu enumerieren.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class Form1
    3. Private Const S_OK As Integer = 0
    4. Private Const CLSID_MMDeviceEnumerator As String = "bcde0395-e52f-467c-8e3d-c4579291692e"
    5. Private Const IID_IMMDeviceEnumerator As String = "a95664d2-9614-4f35-a746-de8db63617e6"
    6. Private Const IID_IMMDeviceCollection As String = "0bd7a1be-7a1a-44db-8397-cc5392387b5e"
    7. Private Const IID_IMMDevice As String = "d666063f-1587-4e43-81f1-b948e807363f"
    8. Private Const IID_IPropertyStore As String = "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"
    9. Private Enum DATA_FLOW As Integer
    10. Render = 0
    11. Capture = 1
    12. All = 2
    13. DataFlow_Enum_Count = 3
    14. End Enum
    15. Private Enum DEVICE_STATE As Integer
    16. Active = 1
    17. Disabled = 2
    18. NotPresent = 4
    19. Unplugged = 8
    20. End Enum
    21. Private Enum STGM_ACCESS As Integer
    22. Read = 0
    23. Write = 1
    24. ReadWrite = 2
    25. End Enum
    26. Private Enum VARENUM As UShort
    27. VT_EMPTY = 0
    28. VT_NULL = 1
    29. VT_I2 = 2
    30. VT_I4 = 3
    31. VT_R4 = 4
    32. VT_R8 = 5
    33. VT_CY = 6
    34. VT_DATE = 7
    35. VT_BSTR = 8
    36. VT_DISPATCH = 9
    37. VT_ERROR = 10
    38. VT_BOOL = 11
    39. VT_VARIANT = 12
    40. VT_UNKNOWN = 13
    41. VT_DECIMAL = 14
    42. VT_I1 = 16
    43. VT_UI1 = 17
    44. VT_UI2 = 18
    45. VT_UI4 = 19
    46. VT_I8 = 20
    47. VT_UI8 = 21
    48. VT_INT = 22
    49. VT_UINT = 23
    50. VT_VOID = 24
    51. VT_HRESULT = 25
    52. VT_PTR = 26
    53. VT_SAFEARRAY = 27
    54. VT_CARRAY = 28
    55. VT_USERDEFINED = 29
    56. VT_LPSTR = 30
    57. VT_LPWSTR = 31
    58. VT_RECORD = 36
    59. VT_INT_PTR = 37
    60. VT_UINT_PTR = 38
    61. VT_FILETIME = 64
    62. VT_BLOB = 65
    63. VT_STREAM = 66
    64. VT_STORAGE = 67
    65. VT_STREAMED_OBJECT = 68
    66. VT_STORED_OBJECT = 69
    67. VT_BLOB_OBJECT = 70
    68. VT_CF = 71
    69. VT_CLSID = 72
    70. VT_VERSIONED_STREAM = 73
    71. VT_BSTR_BLOB = 4095
    72. VT_VECTOR = 4096
    73. VT_ARRAY = 8192
    74. VT_BYREF = 16384
    75. VT_RESERVED = 32768
    76. VT_ILLEGAL = 65535
    77. VT_ILLEGALMASKED = 4095
    78. VT_TYPEMASK = 4095
    79. End Enum
    80. Private Structure PROPERTYKEY
    81. Dim fmtid As Guid
    82. Dim pid As UInteger
    83. End Structure
    84. Private Structure CArray
    85. Dim cElems As UInteger
    86. Dim pElems As IntPtr
    87. End Structure
    88. <StructLayout(LayoutKind.Explicit, Size:=16)> 'gekürzt
    89. Private Structure PROPVARIANT
    90. <FieldOffset(0)> Dim vt As VARENUM
    91. <FieldOffset(2)> Dim wReserved1 As UShort
    92. <FieldOffset(4)> Dim wReserved2 As UShort
    93. <FieldOffset(6)> Dim wReserved3 As UShort
    94. <FieldOffset(8)> Dim bVal As Byte
    95. <FieldOffset(8)> Dim cVal As SByte
    96. <FieldOffset(8)> Dim uiVal As UShort
    97. <FieldOffset(8)> Dim iVal As Short
    98. <FieldOffset(8)> Dim uintVal As UInteger
    99. <FieldOffset(8)> Dim intVal As Integer
    100. <FieldOffset(8)> Dim ulVal As ULong
    101. <FieldOffset(8)> Dim lVal As Long
    102. <FieldOffset(8)> Dim fltVal As Single
    103. <FieldOffset(8)> Dim dblVal As Double
    104. <FieldOffset(8)> Dim boolVal As Short
    105. <FieldOffset(8)> Dim pclsidVal As IntPtr
    106. <FieldOffset(8)> Dim pszVal As IntPtr
    107. <FieldOffset(8)> Dim pwszVal As IntPtr
    108. <FieldOffset(8)> Dim punkVal As IntPtr
    109. <FieldOffset(8)> Dim cArray As CArray
    110. End Structure
    111. <DllImport("Ole32.dll", EntryPoint:="PropVariantClear")>
    112. <PreserveSig> Private Shared Function PropVariantClear(<[In]> ByRef pvar As PROPVARIANT) As Integer
    113. End Function
    114. <DllImport("Propsys.dll", EntryPoint:="PSGetNameFromPropertyKey")>
    115. <PreserveSig> Private Shared Function PSGetNameFromPropertyKey(<[In]> ByRef propkey As PROPERTYKEY,
    116. <Out> ByRef ppszCanonicalName As IntPtr) As Integer
    117. End Function
    118. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    119. Dim MMDeviceEnumerator As IMMDeviceEnumerator = CType(Activator.CreateInstance(Type.GetTypeFromCLSID(New Guid(CLSID_MMDeviceEnumerator))), IMMDeviceEnumerator)
    120. If MMDeviceEnumerator IsNot Nothing Then
    121. Dim MMDeviceCollection As IMMDeviceCollection = Nothing
    122. If MMDeviceEnumerator.EnumAudioEndpoints(DATA_FLOW.Render, DEVICE_STATE.Active, MMDeviceCollection) = S_OK Then
    123. If MMDeviceCollection IsNot Nothing Then
    124. Dim Devices As UInteger
    125. If MMDeviceCollection.GetCount(Devices) = S_OK Then
    126. Debug.Print("Devices: " & Devices.ToString)
    127. If Devices > 0 Then
    128. For Device As UInteger = 0 To Devices - 1UI
    129. Dim MMDevive As IMMDevice = Nothing
    130. If MMDeviceCollection.Item(Device, MMDevive) = S_OK Then
    131. If MMDevive IsNot Nothing Then
    132. Dim DevId As String = Nothing
    133. If MMDevive.GetId(DevId) = S_OK Then
    134. Debug.Print("DEVICE_ID: " & DevId)
    135. End If
    136. Dim DevState As New DEVICE_STATE
    137. If MMDevive.GetState(DevState) = S_OK Then
    138. Debug.Print("DEVICE_STATE: " & DevState.ToString)
    139. End If
    140. Dim PropertyStore As IPropertyStore = Nothing
    141. If MMDevive.OpenPropertyStore(STGM_ACCESS.Read, PropertyStore) = S_OK Then
    142. If PropertyStore IsNot Nothing Then
    143. Dim Props As UInteger
    144. If PropertyStore.GetCount(Props) = S_OK Then
    145. Debug.Print("Properties: " & Props.ToString)
    146. If Props > 0 Then
    147. For Prop As UInteger = 0 To Props - 1UI
    148. Dim PropKey As New PROPERTYKEY
    149. If PropertyStore.GetAt(Prop, PropKey) = S_OK Then
    150. Debug.Print("PKey: " & PropKey.fmtid.ToString & "," & PropKey.pid.ToString)
    151. Dim pCanonicalName As IntPtr
    152. If PSGetNameFromPropertyKey(PropKey, pCanonicalName) = S_OK Then
    153. Debug.Print("CanonicalName: " & Marshal.PtrToStringUni(pCanonicalName))
    154. Marshal.FreeCoTaskMem(pCanonicalName)
    155. End If
    156. Dim PropVar As New PROPVARIANT
    157. If PropertyStore.GetValue(PropKey, PropVar) = S_OK Then
    158. Debug.Print("PropVar.vt: " & PropVar.vt.ToString)
    159. Select Case PropVar.vt
    160. Case VARENUM.VT_BOOL
    161. Debug.Print(CBool(PropVar.boolVal).ToString)
    162. Case VARENUM.VT_I2
    163. Debug.Print(PropVar.iVal.ToString)
    164. Case VARENUM.VT_I4
    165. Debug.Print(PropVar.intVal.ToString)
    166. Case VARENUM.VT_I8
    167. Debug.Print(PropVar.lVal.ToString)
    168. Case VARENUM.VT_UI2
    169. Debug.Print(PropVar.uiVal.ToString)
    170. Case VARENUM.VT_UI4
    171. Debug.Print(PropVar.uintVal.ToString)
    172. Case VARENUM.VT_UI8
    173. Debug.Print(PropVar.ulVal.ToString)
    174. Case VARENUM.VT_LPWSTR
    175. Debug.Print(Marshal.PtrToStringUni(PropVar.pwszVal))
    176. End Select
    177. PropVariantClear(PropVar)
    178. End If
    179. End If
    180. Next
    181. End If
    182. End If
    183. Marshal.ReleaseComObject(PropertyStore)
    184. End If
    185. End If
    186. Marshal.ReleaseComObject(MMDevive)
    187. End If
    188. End If
    189. Next
    190. End If
    191. End If
    192. Marshal.ReleaseComObject(MMDeviceCollection)
    193. End If
    194. End If
    195. Marshal.ReleaseComObject(MMDeviceEnumerator)
    196. End If
    197. End Sub
    198. <ComImport>
    199. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    200. <Guid(IID_IMMDeviceEnumerator)>
    201. Private Interface IMMDeviceEnumerator
    202. <PreserveSig> Function EnumAudioEndpoints(<[In]> dataFlow As DATA_FLOW,
    203. <[In]> dwStateMask As DEVICE_STATE,
    204. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppDevices As IMMDeviceCollection) As Integer
    205. <PreserveSig> Function GetDefaultAudioEndpoint() As Integer
    206. <PreserveSig> Function GetDevice() As Integer
    207. <PreserveSig> Function RegisterEndpointNotificationCallback() As Integer
    208. <PreserveSig> Function UnregisterEndpointNotificationCallback() As Integer
    209. End Interface
    210. <ComImport>
    211. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    212. <Guid(IID_IMMDeviceCollection)>
    213. Private Interface IMMDeviceCollection
    214. <PreserveSig> Function GetCount(<Out> ByRef pcDevices As UInteger) As Integer
    215. <PreserveSig> Function Item(<[In]> nDevice As UInteger,
    216. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppDevice As IMMDevice) As Integer
    217. End Interface
    218. <ComImport>
    219. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    220. <Guid(IID_IMMDevice)>
    221. Private Interface IMMDevice
    222. <PreserveSig> Function Activate() As Integer
    223. <PreserveSig> Function OpenPropertyStore(<[In]> stgmAccess As STGM_ACCESS,
    224. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppProperties As IPropertyStore) As Integer
    225. <PreserveSig> Function GetId(<Out, MarshalAs(UnmanagedType.LPWStr)> ByRef ppstrId As String) As Integer
    226. <PreserveSig> Function GetState(<Out> ByRef pdwState As DEVICE_STATE) As Integer
    227. End Interface
    228. <ComImport>
    229. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    230. <Guid(IID_IPropertyStore)>
    231. Private Interface IPropertyStore
    232. <PreserveSig> Function GetCount(<Out> ByRef cProps As UInteger) As Integer
    233. <PreserveSig> Function GetAt(<[In]> iProp As UInteger,
    234. <Out> ByRef pkey As PROPERTYKEY) As Integer
    235. <PreserveSig> Function GetValue(<[In]> ByRef pkey As PROPERTYKEY,
    236. <Out> ByRef pv As PROPVARIANT) As Integer
    237. <PreserveSig> Function SetValue() As Integer
    238. <PreserveSig> Function Commit() As Integer
    239. End Interface
    240. End Class

    Mfg -Franky-
    Mittlerweile wende ich folgenden Code an:
    Es wird zunächst benötigt Imports System.Management

    Quellcode

    1. Function DeviceDetected() As Array
    2. Dim info As ManagementObject, search As ManagementObjectSearcher
    3. Dim Name, Manu As String, temp(0) As String, i As Short, actFlag As Boolean = False
    4. search = New ManagementObjectSearcher("SELECT * From Win32_PnPEntity")
    5. For Each info In search.Get()
    6. Name = CType(info("Caption"), String)
    7. Manu = CType(info("Manufacturer"), String)
    8. If Not IsNothing(Name) AndAlso (Name.Contains("Lautsprecher") _
    9. OrElse Name.Contains("Speaker") OrElse Name.Contains("Audio)")) Then
    10. ''Debug.Print("~~~~~~~~~~~~~~~~")
    11. ''For Each p In info.Properties
    12. '' wert = p.Value
    13. '' If Not wert Is Nothing Then
    14. '' Debug.Print(p.Name & " = " & wert.ToString())
    15. '' Else
    16. '' Debug.Print(p.Name & " = (nicht belegt)")
    17. '' End If
    18. ''Next
    19. ReDim Preserve temp(i)
    20. temp(i) = Name
    21. i += 1
    22. End If
    23. Next
    24. Return temp
    25. End Function


    Der doppelt auskommentierte Code zeigt folgende Properties an:
    Availability= (nicht belegt)
    Caption=C27F390 (NVIDIA High Definition Audio)
    ClassGuid={c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
    CompatibleID=System.String[]
    ConfigManagerErrorCode=0
    ConfigManagerUserConfig=False
    CreationClassName=Win32_PnPEntity
    Description=Audioendpunkt
    DeviceID=SWD\MMDEVAPI\{0.0.0.00000000}.{D8AAA027-616D-4C8E-80C4-53B80A8C191C}
    ErrorCleared= (nicht belegt)
    ErrorDescription= (nicht belegt)
    HardwareID=System.String[]
    InstallDate= (nicht belegt)
    LastErrorCode= (nicht belegt)
    Manufacturer=Microsoft
    Name=C27F390 (NVIDIA High Definition Audio)
    PNPClass=AudioEndpoint
    PNPDeviceID=SWD\MMDEVAPI\{0.0.0.00000000}.{D8AAA027-616D-4C8E-80C4-53B80A8C191C}
    PowerManagementCapabilities= (nicht belegt)
    PowerManagementSupported= (nicht belegt)
    Present=True
    Service= (nicht belegt)
    Status=OK
    StatusInfo= (nicht belegt)
    SystemCreationClassName=Win32_ComputerSystem

    Leider ist da keine Eigenschaft dabei, die vermittelt, welches Soundgerät gerade aktuell ist (siehe Screenshot-Auswahl).

    Grüße - Dietrich

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „dherr“ ()

    dherr schrieb:

    Leider ist da keine Eigenschaft dabei, die vermittelt, welches Soundgerät gerade aktuell ist

    Wenn Du Dir meinen Code anschaust, dann fällt Dir sicher IMMDeviceEnumerator::GetDefaultAudioEndpoint auf. Die MS-Doku sagt dazu: Die GetDefaultAudioEndpoint-Methode ruft den Standard-Audioendpunkt für die angegebene Datenflussrichtung und -rolle ab. Das wäre das, was Du noch suchst. Auch wenn ich diese Interface-Funktion nicht komplett übersetzt habe, sollte es Dir keine Probleme bereiten diese Funktion zu vervollständigen. Siehe dazu in die MS-Doku: learn.microsoft.com/de-de/wind…r-getdefaultaudioendpoint
    Mfg -Franky-
    Hi, Franky, danke für deine Tipps. Leider habe ich doch ein Problem mit der Vervollständigung, respektive Aufruf der Funktion GetDefaultAudioEndpoint. Kenne C++ überhaupt nicht, weiß nicht, wie ich das auf VB übertragen kann...
    Ich habe mittlerweile deinen Code in ein Module gepackt.
    Vielleicht kannst du bitte noch den Code für GetDefaultAudioEndpoint bereitstellen?
    Grüße - Dietrich
    Hi Zunächst brauchst Du eine zusätzliche Enum

    VB.NET-Quellcode

    1. Private Enum Role As Integer
    2. Console = 0
    3. Multimedia = 1
    4. Communications = 2
    5. End Enum

    Im Interface IMMDeviceEnumerator entsprechend die Funktion GetDefaultAudioEndpoint anpassen.

    VB.NET-Quellcode

    1. <PreserveSig> Function GetDefaultAudioEndpoint(<[In]> dataFlow As DATA_FLOW,
    2. <[In]> role As Role,
    3. <Out, MarshalAs(UnmanagedType.Interface)> ByRef ppEndpoint As IMMDevice) As Integer

    Im Grunde rufst "MMDeviceEnumerator.GetDefaultAudioEndpoint(DATA_FLOW.Render, Role.Multimedia, MMDevice)" auf, lass Dir dann mit MMDevice.GetId die Id ausgeben. Diese Id vergleichst mit der Id, die Du über die Enumeration aller Rendergeräte ermitteln kannst. Stimmen die Ids überein, hast Du das derzeit eingestellte Standardgerät.
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    Also ich habe hinter die Zeile
    For Device As UInteger = 0 To Devices - 1UI

    die Zeile
    Dim idd = MMDeviceEnumerator.GetDefaultAudioEndpoint(DATA_FLOW.Render, Role.Multimedia, MMDevice)
    gesetzt. Da ergibt sich aber bei jedem Gerät immer nur 0 ...
    Hi Das was Du noch machen möchtest, kommt unter der Zeile If MMDeviceEnumerator IsNot Nothing Then und ja, der Aufruf von MMDeviceEnumerator.GetDefaultAudioEndpoint gibt 0 = S_Ok zurück, wenn der Aufruf erfolgreich war. Denn dann kannst Du MMDevice.GetId abfragen. Genauso wie nach der Zeile If MMDeviceCollection.Item(... nur das Du hier nur die ID brauchst. Den Rest nicht bis auf das Marshal.ReleaseComObject
    Mfg -Franky-
    Danke für deine Tipps, aber ich komme nicht wirklich klar damit.
    In deinen Code habe ich das jetzt folgendermaßen eingebaut:

    VB.NET-Quellcode

    1. ..........
    2. If MMDeviceCollection.Item(Device, MMDevive) = S_OK Then
    3. If MMDevive IsNot Nothing Then
    4. Dim DevId As String = Nothing
    5. If MMDevive.GetId(DevId) = S_OK Then
    6. 'Device Ident-Nummer
    7. outArt(StrDup(30, "~") & vbCrLf & "Nummer " _
    8. & (Device + 1).ToString & vbCrLf _
    9. & "DEVICE_ID: " & DevId & vbCrLf)
    10. If MMDeviceEnumerator.GetDefaultAudioEndpoint(DATA_FLOW.Render, Role.Multimedia, MMDevice) = S_OK Then
    11. outArt("EndPoint: " & DevId & vbCrLf)
    12. End If
    13. End If
    14. ..................
    (outArt ist nur mein Ersatz für das einfache Debug.Print)
    Das funktioniert so aber nicht. Was heißt "nur die ID" MMDevice.GetID bringt einen String...

    (Ooops, wo kommt denn dieses Brainfuck her???)

    Code-Tags korrigiert. (Brainfuck kommt daher, dass die Forensoftware versucht, die Sprache zu erkennen, wenn man nur [code] anstatt z. B. [vbnet] verwendet, und die vielen Punkte am Anfang wohl nach Brainfuck aussahen.) ~Thunderbolt

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Thunderbolt“ ()

    Hi Also, ich hatte geschrieben das MMDeviceEnumerator.GetDefaultAudioEndpoint direkt unter "If MMDeviceEnumerator IsNot Nothing Then" kommt. Bei Dir steht der Code aber ganz wo anders. Dann fragst Du bei MMDeviceEnumerator.GetDefaultAudioEndpoint -> MMDevice gar nicht die ID ab (MMDevice.GetId(DevId)) -> String. Diesen String vergleichst mit der ID, die Du innerhalb von MMDeviceCollection.Item(Device, MMDevive) -> MMDevive.GetId(DevId) bekommst. Huch, da hab ich mich wohl vertippelt: MMDevive sollte eigentlich MMDevice benannt werden. Also aus v mach c. ;)

    dherr schrieb:

    (Ooops, wo kommt denn dieses Brainfuck her???)

    Wahrscheinlich wegen den Punkten in Zeile 1 und 14. :D
    Mfg -Franky-
    Hi. Das geht über "Erweiterte Antwort". Da kannst Du Dateianhänge hochladen. In der ZIP dürfen keine ausführbaren Dateien enthalten sein! Speziell die Ordner BIN und OBJ (bzw. deren UnterOrdner von BIN und OBJ) von Deinem Projekt sollten leer sein.
    Mfg -Franky-