2 * Copyright 2009 Maarten Lankhorst
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
27 #include "wine/debug.h"
28 #include "wine/list.h"
32 #include "mmdeviceapi.h"
35 #include "audioclient.h"
36 #include "endpointvolume.h"
37 #include "audiopolicy.h"
38 #include "spatialaudioclient.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
45 static HKEY key_render
;
46 static HKEY key_capture
;
48 typedef struct MMDevPropStoreImpl
50 IPropertyStore IPropertyStore_iface
;
56 typedef struct MMDevEnumImpl
58 IMMDeviceEnumerator IMMDeviceEnumerator_iface
;
62 static MMDevice
*MMDevice_def_rec
, *MMDevice_def_play
;
63 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl
;
64 static const IMMDeviceCollectionVtbl MMDevColVtbl
;
65 static const IMMDeviceVtbl MMDeviceVtbl
;
66 static const IPropertyStoreVtbl MMDevPropVtbl
;
67 static const IMMEndpointVtbl MMEndpointVtbl
;
69 static MMDevEnumImpl enumerator
;
70 static struct list device_list
= LIST_INIT(device_list
);
71 static IMMDevice info_device
;
73 typedef struct MMDevColImpl
75 IMMDeviceCollection IMMDeviceCollection_iface
;
81 typedef struct IPropertyBagImpl
{
82 IPropertyBag IPropertyBag_iface
;
86 static const IPropertyBagVtbl PB_Vtbl
;
88 static HRESULT
MMDevPropStore_Create(MMDevice
*This
, DWORD access
, IPropertyStore
**ppv
);
90 static inline MMDevPropStore
*impl_from_IPropertyStore(IPropertyStore
*iface
)
92 return CONTAINING_RECORD(iface
, MMDevPropStore
, IPropertyStore_iface
);
95 static inline MMDevEnumImpl
*impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator
*iface
)
97 return CONTAINING_RECORD(iface
, MMDevEnumImpl
, IMMDeviceEnumerator_iface
);
100 static inline MMDevColImpl
*impl_from_IMMDeviceCollection(IMMDeviceCollection
*iface
)
102 return CONTAINING_RECORD(iface
, MMDevColImpl
, IMMDeviceCollection_iface
);
105 static inline IPropertyBagImpl
*impl_from_IPropertyBag(IPropertyBag
*iface
)
107 return CONTAINING_RECORD(iface
, IPropertyBagImpl
, IPropertyBag_iface
);
110 static const WCHAR propkey_formatW
[] = L
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X},%d";
112 static HRESULT
MMDevPropStore_OpenPropKey(const GUID
*guid
, DWORD flow
, HKEY
*propkey
)
117 StringFromGUID2(guid
, buffer
, 39);
118 if ((ret
= RegOpenKeyExW(flow
== eRender
? key_render
: key_capture
, buffer
, 0, KEY_READ
|KEY_WRITE
|KEY_WOW64_64KEY
, &key
)) != ERROR_SUCCESS
)
120 WARN("Opening key %s failed with %u\n", debugstr_w(buffer
), ret
);
123 ret
= RegOpenKeyExW(key
, L
"Properties", 0, KEY_READ
|KEY_WRITE
|KEY_WOW64_64KEY
, propkey
);
125 if (ret
!= ERROR_SUCCESS
)
127 WARN("Opening key Properties failed with %u\n", ret
);
133 static HRESULT
MMDevice_GetPropValue(const GUID
*devguid
, DWORD flow
, REFPROPERTYKEY key
, PROPVARIANT
*pv
)
136 const GUID
*id
= &key
->fmtid
;
142 hr
= MMDevPropStore_OpenPropKey(devguid
, flow
, ®key
);
145 wsprintfW( buffer
, propkey_formatW
, id
->Data1
, id
->Data2
, id
->Data3
,
146 id
->Data4
[0], id
->Data4
[1], id
->Data4
[2], id
->Data4
[3],
147 id
->Data4
[4], id
->Data4
[5], id
->Data4
[6], id
->Data4
[7], key
->pid
);
148 ret
= RegGetValueW(regkey
, NULL
, buffer
, RRF_RT_ANY
, &type
, NULL
, &size
);
149 if (ret
!= ERROR_SUCCESS
)
151 WARN("Reading %s returned %d\n", debugstr_w(buffer
), ret
);
153 PropVariantClear(pv
);
162 pv
->pwszVal
= CoTaskMemAlloc(size
);
166 RegGetValueW(regkey
, NULL
, buffer
, RRF_RT_REG_SZ
, NULL
, (BYTE
*)pv
->pwszVal
, &size
);
172 RegGetValueW(regkey
, NULL
, buffer
, RRF_RT_REG_DWORD
, NULL
, (BYTE
*)&pv
->ulVal
, &size
);
178 pv
->blob
.cbSize
= size
;
179 pv
->blob
.pBlobData
= CoTaskMemAlloc(size
);
180 if (!pv
->blob
.pBlobData
)
183 RegGetValueW(regkey
, NULL
, buffer
, RRF_RT_REG_BINARY
, NULL
, (BYTE
*)pv
->blob
.pBlobData
, &size
);
187 ERR("Unknown/unhandled type: %u\n", type
);
188 PropVariantClear(pv
);
195 static HRESULT
MMDevice_SetPropValue(const GUID
*devguid
, DWORD flow
, REFPROPERTYKEY key
, REFPROPVARIANT pv
)
198 const GUID
*id
= &key
->fmtid
;
203 hr
= MMDevPropStore_OpenPropKey(devguid
, flow
, ®key
);
206 wsprintfW( buffer
, propkey_formatW
, id
->Data1
, id
->Data2
, id
->Data3
,
207 id
->Data4
[0], id
->Data4
[1], id
->Data4
[2], id
->Data4
[3],
208 id
->Data4
[4], id
->Data4
[5], id
->Data4
[6], id
->Data4
[7], key
->pid
);
213 ret
= RegSetValueExW(regkey
, buffer
, 0, REG_DWORD
, (const BYTE
*)&pv
->ulVal
, sizeof(DWORD
));
218 ret
= RegSetValueExW(regkey
, buffer
, 0, REG_BINARY
, pv
->blob
.pBlobData
, pv
->blob
.cbSize
);
219 TRACE("Blob %p %u\n", pv
->blob
.pBlobData
, pv
->blob
.cbSize
);
225 ret
= RegSetValueExW(regkey
, buffer
, 0, REG_SZ
, (const BYTE
*)pv
->pwszVal
, sizeof(WCHAR
)*(1+lstrlenW(pv
->pwszVal
)));
230 FIXME("Unhandled type %u\n", pv
->vt
);
235 TRACE("Writing %s returned %u\n", debugstr_w(buffer
), ret
);
239 static HRESULT
set_driver_prop_value(GUID
*id
, const EDataFlow flow
, const PROPERTYKEY
*prop
)
244 if (!drvs
.pGetPropValue
)
247 hr
= drvs
.pGetPropValue(id
, prop
, &pv
);
251 MMDevice_SetPropValue(id
, flow
, prop
, &pv
);
252 PropVariantClear(&pv
);
258 /* Creates or updates the state of a device
259 * If GUID is null, a random guid will be assigned
260 * and the device will be created
262 static MMDevice
*MMDevice_Create(WCHAR
*name
, GUID
*id
, EDataFlow flow
, DWORD state
, BOOL setdefault
)
265 MMDevice
*device
, *cur
= NULL
;
268 static const PROPERTYKEY deviceinterface_key
= {
269 {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
272 static const PROPERTYKEY devicepath_key
= {
273 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
276 LIST_FOR_EACH_ENTRY(device
, &device_list
, MMDevice
, entry
)
278 if (device
->flow
== flow
&& IsEqualGUID(&device
->devguid
, id
)){
285 /* No device found, allocate new one */
286 cur
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*cur
));
290 cur
->IMMDevice_iface
.lpVtbl
= &MMDeviceVtbl
;
291 cur
->IMMEndpoint_iface
.lpVtbl
= &MMEndpointVtbl
;
293 InitializeCriticalSection(&cur
->crst
);
294 cur
->crst
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": MMDevice.crst");
296 list_add_tail(&device_list
, &cur
->entry
);
297 }else if(cur
->ref
> 0)
298 WARN("Modifying an MMDevice with postitive reference count!\n");
300 HeapFree(GetProcessHeap(), 0, cur
->drv_id
);
307 StringFromGUID2(&cur
->devguid
, guidstr
, ARRAY_SIZE(guidstr
));
314 if (RegCreateKeyExW(root
, guidstr
, 0, NULL
, 0, KEY_WRITE
|KEY_READ
|KEY_WOW64_64KEY
, NULL
, &key
, NULL
) == ERROR_SUCCESS
)
317 RegSetValueExW(key
, L
"DeviceState", 0, REG_DWORD
, (const BYTE
*)&state
, sizeof(DWORD
));
318 if (!RegCreateKeyExW(key
, L
"Properties", 0, NULL
, 0, KEY_WRITE
|KEY_READ
|KEY_WOW64_64KEY
, NULL
, &keyprop
, NULL
))
324 MMDevice_SetPropValue(id
, flow
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pv
);
325 MMDevice_SetPropValue(id
, flow
, (const PROPERTYKEY
*)&DEVPKEY_DeviceInterface_FriendlyName
, &pv
);
326 MMDevice_SetPropValue(id
, flow
, (const PROPERTYKEY
*)&DEVPKEY_Device_DeviceDesc
, &pv
);
328 pv
.pwszVal
= guidstr
;
329 MMDevice_SetPropValue(id
, flow
, &deviceinterface_key
, &pv
);
331 set_driver_prop_value(id
, flow
, &devicepath_key
);
333 if (FAILED(set_driver_prop_value(id
, flow
, &PKEY_AudioEndpoint_FormFactor
)))
336 pv
.ulVal
= (flow
== eCapture
) ? Microphone
: Speakers
;
338 MMDevice_SetPropValue(id
, flow
, &PKEY_AudioEndpoint_FormFactor
, &pv
);
341 if (flow
!= eCapture
)
345 PropVariantInit(&pv2
);
347 /* make read-write by not overwriting if already set */
348 if (FAILED(MMDevice_GetPropValue(id
, flow
, &PKEY_AudioEndpoint_PhysicalSpeakers
, &pv2
)) || pv2
.vt
!= VT_UI4
)
349 set_driver_prop_value(id
, flow
, &PKEY_AudioEndpoint_PhysicalSpeakers
);
351 PropVariantClear(&pv2
);
354 RegCloseKey(keyprop
);
362 MMDevice_def_play
= cur
;
364 MMDevice_def_rec
= cur
;
369 HRESULT
load_devices_from_reg(void)
376 ret
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
,
377 L
"Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio", 0, NULL
, 0,
378 KEY_WRITE
|KEY_READ
|KEY_WOW64_64KEY
, NULL
, &root
, NULL
);
379 if (ret
== ERROR_SUCCESS
)
380 ret
= RegCreateKeyExW(root
, L
"Capture", 0, NULL
, 0, KEY_READ
|KEY_WRITE
|KEY_WOW64_64KEY
, NULL
, &key_capture
, NULL
);
381 if (ret
== ERROR_SUCCESS
)
382 ret
= RegCreateKeyExW(root
, L
"Render", 0, NULL
, 0, KEY_READ
|KEY_WRITE
|KEY_WOW64_64KEY
, NULL
, &key_render
, NULL
);
386 if (ret
!= ERROR_SUCCESS
)
388 RegCloseKey(key_capture
);
389 key_render
= key_capture
= NULL
;
390 WARN("Couldn't create key: %u\n", ret
);
398 PROPVARIANT pv
= { VT_EMPTY
};
400 len
= ARRAY_SIZE(guidvalue
);
401 ret
= RegEnumKeyExW(cur
, i
++, guidvalue
, &len
, NULL
, NULL
, NULL
, NULL
);
402 if (ret
== ERROR_NO_MORE_ITEMS
)
404 if (cur
== key_capture
)
413 if (ret
!= ERROR_SUCCESS
)
415 if (SUCCEEDED(CLSIDFromString(guidvalue
, &guid
))
416 && SUCCEEDED(MMDevice_GetPropValue(&guid
, curflow
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pv
))
417 && pv
.vt
== VT_LPWSTR
)
419 DWORD size_bytes
= (lstrlenW(pv
.pwszVal
) + 1) * sizeof(WCHAR
);
420 WCHAR
*name
= HeapAlloc(GetProcessHeap(), 0, size_bytes
);
421 memcpy(name
, pv
.pwszVal
, size_bytes
);
422 MMDevice_Create(name
, &guid
, curflow
,
423 DEVICE_STATE_NOTPRESENT
, FALSE
);
424 CoTaskMemFree(pv
.pwszVal
);
431 static HRESULT
set_format(MMDevice
*dev
)
434 IAudioClient
*client
;
436 PROPVARIANT pv
= { VT_EMPTY
};
438 hr
= drvs
.pGetAudioEndpoint(&dev
->devguid
, &dev
->IMMDevice_iface
, &client
);
442 hr
= IAudioClient_GetMixFormat(client
, &fmt
);
444 IAudioClient_Release(client
);
448 IAudioClient_Release(client
);
451 pv
.blob
.cbSize
= sizeof(WAVEFORMATEX
) + fmt
->cbSize
;
452 pv
.blob
.pBlobData
= (BYTE
*)fmt
;
453 MMDevice_SetPropValue(&dev
->devguid
, dev
->flow
,
454 &PKEY_AudioEngine_DeviceFormat
, &pv
);
455 MMDevice_SetPropValue(&dev
->devguid
, dev
->flow
,
456 &PKEY_AudioEngine_OEMFormat
, &pv
);
462 HRESULT
load_driver_devices(EDataFlow flow
)
469 if(!drvs
.pGetEndpointIDs
)
472 hr
= drvs
.pGetEndpointIDs(flow
, &ids
, &guids
, &num
, &def
);
476 for(i
= 0; i
< num
; ++i
){
478 dev
= MMDevice_Create(ids
[i
], &guids
[i
], flow
, DEVICE_STATE_ACTIVE
,
483 HeapFree(GetProcessHeap(), 0, guids
);
484 HeapFree(GetProcessHeap(), 0, ids
);
489 static void MMDevice_Destroy(MMDevice
*This
)
491 TRACE("Freeing %s\n", debugstr_w(This
->drv_id
));
492 list_remove(&This
->entry
);
493 This
->crst
.DebugInfo
->Spare
[0] = 0;
494 DeleteCriticalSection(&This
->crst
);
495 HeapFree(GetProcessHeap(), 0, This
->drv_id
);
496 HeapFree(GetProcessHeap(), 0, This
);
499 static inline MMDevice
*impl_from_IMMDevice(IMMDevice
*iface
)
501 return CONTAINING_RECORD(iface
, MMDevice
, IMMDevice_iface
);
504 static HRESULT WINAPI
MMDevice_QueryInterface(IMMDevice
*iface
, REFIID riid
, void **ppv
)
506 MMDevice
*This
= impl_from_IMMDevice(iface
);
507 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
512 if (IsEqualIID(riid
, &IID_IUnknown
)
513 || IsEqualIID(riid
, &IID_IMMDevice
))
514 *ppv
= &This
->IMMDevice_iface
;
515 else if (IsEqualIID(riid
, &IID_IMMEndpoint
))
516 *ppv
= &This
->IMMEndpoint_iface
;
519 IUnknown_AddRef((IUnknown
*)*ppv
);
522 WARN("Unknown interface %s\n", debugstr_guid(riid
));
523 return E_NOINTERFACE
;
526 static ULONG WINAPI
MMDevice_AddRef(IMMDevice
*iface
)
528 MMDevice
*This
= impl_from_IMMDevice(iface
);
531 ref
= InterlockedIncrement(&This
->ref
);
532 TRACE("Refcount now %i\n", ref
);
536 static ULONG WINAPI
MMDevice_Release(IMMDevice
*iface
)
538 MMDevice
*This
= impl_from_IMMDevice(iface
);
541 ref
= InterlockedDecrement(&This
->ref
);
542 TRACE("Refcount now %i\n", ref
);
546 static HRESULT WINAPI
MMDevice_Activate(IMMDevice
*iface
, REFIID riid
, DWORD clsctx
, PROPVARIANT
*params
, void **ppv
)
548 HRESULT hr
= E_NOINTERFACE
;
549 MMDevice
*This
= impl_from_IMMDevice(iface
);
551 TRACE("(%p)->(%s, %x, %p, %p)\n", iface
, debugstr_guid(riid
), clsctx
, params
, ppv
);
556 if (IsEqualIID(riid
, &IID_IAudioClient
) ||
557 IsEqualIID(riid
, &IID_IAudioClient2
) ||
558 IsEqualIID(riid
, &IID_IAudioClient3
)){
559 hr
= drvs
.pGetAudioEndpoint(&This
->devguid
, iface
, (IAudioClient
**)ppv
);
560 }else if (IsEqualIID(riid
, &IID_IAudioEndpointVolume
) ||
561 IsEqualIID(riid
, &IID_IAudioEndpointVolumeEx
))
562 hr
= AudioEndpointVolume_Create(This
, (IAudioEndpointVolumeEx
**)ppv
);
563 else if (IsEqualIID(riid
, &IID_IAudioSessionManager
)
564 || IsEqualIID(riid
, &IID_IAudioSessionManager2
))
566 hr
= drvs
.pGetAudioSessionManager(iface
, (IAudioSessionManager2
**)ppv
);
568 else if (IsEqualIID(riid
, &IID_IBaseFilter
))
570 if (This
->flow
== eRender
)
571 hr
= CoCreateInstance(&CLSID_DSoundRender
, NULL
, clsctx
, riid
, ppv
);
573 ERR("Not supported for recording?\n");
576 IPersistPropertyBag
*ppb
;
577 hr
= IUnknown_QueryInterface((IUnknown
*)*ppv
, &IID_IPersistPropertyBag
, (void **)&ppb
);
580 /* ::Load cannot assume the interface stays alive after the function returns,
581 * so just create the interface on the stack, saves a lot of complicated code */
582 IPropertyBagImpl bag
= { { &PB_Vtbl
} };
583 bag
.devguid
= This
->devguid
;
584 hr
= IPersistPropertyBag_Load(ppb
, &bag
.IPropertyBag_iface
, NULL
);
585 IPersistPropertyBag_Release(ppb
);
587 IBaseFilter_Release((IBaseFilter
*)*ppv
);
591 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
596 else if (IsEqualIID(riid
, &IID_IDeviceTopology
))
598 FIXME("IID_IDeviceTopology unsupported\n");
600 else if (IsEqualIID(riid
, &IID_IDirectSound
)
601 || IsEqualIID(riid
, &IID_IDirectSound8
))
603 if (This
->flow
== eRender
)
604 hr
= CoCreateInstance(&CLSID_DirectSound8
, NULL
, clsctx
, riid
, ppv
);
607 hr
= IDirectSound_Initialize((IDirectSound
*)*ppv
, &This
->devguid
);
609 IDirectSound_Release((IDirectSound
*)*ppv
);
612 else if (IsEqualIID(riid
, &IID_IDirectSoundCapture
))
614 if (This
->flow
== eCapture
)
615 hr
= CoCreateInstance(&CLSID_DirectSoundCapture8
, NULL
, clsctx
, riid
, ppv
);
618 hr
= IDirectSoundCapture_Initialize((IDirectSoundCapture
*)*ppv
, &This
->devguid
);
620 IDirectSoundCapture_Release((IDirectSoundCapture
*)*ppv
);
623 else if (IsEqualIID(riid
, &IID_ISpatialAudioClient
))
625 hr
= SpatialAudioClient_Create(iface
, (ISpatialAudioClient
**)ppv
);
628 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid
));
633 TRACE("Returning %08x\n", hr
);
637 static HRESULT WINAPI
MMDevice_OpenPropertyStore(IMMDevice
*iface
, DWORD access
, IPropertyStore
**ppv
)
639 MMDevice
*This
= impl_from_IMMDevice(iface
);
640 TRACE("(%p)->(%x,%p)\n", This
, access
, ppv
);
644 return MMDevPropStore_Create(This
, access
, ppv
);
647 static HRESULT WINAPI
MMDevice_GetId(IMMDevice
*iface
, WCHAR
**itemid
)
649 MMDevice
*This
= impl_from_IMMDevice(iface
);
651 GUID
*id
= &This
->devguid
;
653 TRACE("(%p)->(%p)\n", This
, itemid
);
656 *itemid
= str
= CoTaskMemAlloc(56 * sizeof(WCHAR
));
658 return E_OUTOFMEMORY
;
659 wsprintfW(str
, L
"{0.0.%u.00000000}.{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
660 This
->flow
, id
->Data1
, id
->Data2
, id
->Data3
,
661 id
->Data4
[0], id
->Data4
[1], id
->Data4
[2], id
->Data4
[3],
662 id
->Data4
[4], id
->Data4
[5], id
->Data4
[6], id
->Data4
[7]);
663 TRACE("returning %s\n", wine_dbgstr_w(str
));
667 static HRESULT WINAPI
MMDevice_GetState(IMMDevice
*iface
, DWORD
*state
)
669 MMDevice
*This
= impl_from_IMMDevice(iface
);
670 TRACE("(%p)->(%p)\n", iface
, state
);
674 *state
= This
->state
;
678 static const IMMDeviceVtbl MMDeviceVtbl
=
680 MMDevice_QueryInterface
,
684 MMDevice_OpenPropertyStore
,
689 static inline MMDevice
*impl_from_IMMEndpoint(IMMEndpoint
*iface
)
691 return CONTAINING_RECORD(iface
, MMDevice
, IMMEndpoint_iface
);
694 static HRESULT WINAPI
MMEndpoint_QueryInterface(IMMEndpoint
*iface
, REFIID riid
, void **ppv
)
696 MMDevice
*This
= impl_from_IMMEndpoint(iface
);
697 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
698 return IMMDevice_QueryInterface(&This
->IMMDevice_iface
, riid
, ppv
);
701 static ULONG WINAPI
MMEndpoint_AddRef(IMMEndpoint
*iface
)
703 MMDevice
*This
= impl_from_IMMEndpoint(iface
);
704 TRACE("(%p)\n", This
);
705 return IMMDevice_AddRef(&This
->IMMDevice_iface
);
708 static ULONG WINAPI
MMEndpoint_Release(IMMEndpoint
*iface
)
710 MMDevice
*This
= impl_from_IMMEndpoint(iface
);
711 TRACE("(%p)\n", This
);
712 return IMMDevice_Release(&This
->IMMDevice_iface
);
715 static HRESULT WINAPI
MMEndpoint_GetDataFlow(IMMEndpoint
*iface
, EDataFlow
*flow
)
717 MMDevice
*This
= impl_from_IMMEndpoint(iface
);
718 TRACE("(%p)->(%p)\n", This
, flow
);
725 static const IMMEndpointVtbl MMEndpointVtbl
=
727 MMEndpoint_QueryInterface
,
730 MMEndpoint_GetDataFlow
733 static HRESULT
MMDevCol_Create(IMMDeviceCollection
**ppv
, EDataFlow flow
, DWORD state
)
737 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
740 return E_OUTOFMEMORY
;
741 This
->IMMDeviceCollection_iface
.lpVtbl
= &MMDevColVtbl
;
745 *ppv
= &This
->IMMDeviceCollection_iface
;
749 static void MMDevCol_Destroy(MMDevColImpl
*This
)
751 HeapFree(GetProcessHeap(), 0, This
);
754 static HRESULT WINAPI
MMDevCol_QueryInterface(IMMDeviceCollection
*iface
, REFIID riid
, void **ppv
)
756 MMDevColImpl
*This
= impl_from_IMMDeviceCollection(iface
);
757 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
761 if (IsEqualIID(riid
, &IID_IUnknown
)
762 || IsEqualIID(riid
, &IID_IMMDeviceCollection
))
763 *ppv
= &This
->IMMDeviceCollection_iface
;
767 return E_NOINTERFACE
;
768 IUnknown_AddRef((IUnknown
*)*ppv
);
772 static ULONG WINAPI
MMDevCol_AddRef(IMMDeviceCollection
*iface
)
774 MMDevColImpl
*This
= impl_from_IMMDeviceCollection(iface
);
775 LONG ref
= InterlockedIncrement(&This
->ref
);
776 TRACE("Refcount now %i\n", ref
);
780 static ULONG WINAPI
MMDevCol_Release(IMMDeviceCollection
*iface
)
782 MMDevColImpl
*This
= impl_from_IMMDeviceCollection(iface
);
783 LONG ref
= InterlockedDecrement(&This
->ref
);
784 TRACE("Refcount now %i\n", ref
);
786 MMDevCol_Destroy(This
);
790 static HRESULT WINAPI
MMDevCol_GetCount(IMMDeviceCollection
*iface
, UINT
*numdevs
)
792 MMDevColImpl
*This
= impl_from_IMMDeviceCollection(iface
);
795 TRACE("(%p)->(%p)\n", This
, numdevs
);
800 LIST_FOR_EACH_ENTRY(cur
, &device_list
, MMDevice
, entry
)
802 if ((cur
->flow
== This
->flow
|| This
->flow
== eAll
)
803 && (cur
->state
& This
->state
))
809 static HRESULT WINAPI
MMDevCol_Item(IMMDeviceCollection
*iface
, UINT n
, IMMDevice
**dev
)
811 MMDevColImpl
*This
= impl_from_IMMDeviceCollection(iface
);
815 TRACE("(%p)->(%u, %p)\n", This
, n
, dev
);
819 LIST_FOR_EACH_ENTRY(cur
, &device_list
, MMDevice
, entry
)
821 if ((cur
->flow
== This
->flow
|| This
->flow
== eAll
)
822 && (cur
->state
& This
->state
)
825 *dev
= &cur
->IMMDevice_iface
;
826 IMMDevice_AddRef(*dev
);
830 WARN("Could not obtain item %u\n", n
);
835 static const IMMDeviceCollectionVtbl MMDevColVtbl
=
837 MMDevCol_QueryInterface
,
844 HRESULT
MMDevEnum_Create(REFIID riid
, void **ppv
)
846 return IMMDeviceEnumerator_QueryInterface(&enumerator
.IMMDeviceEnumerator_iface
, riid
, ppv
);
849 void MMDevEnum_Free(void)
851 MMDevice
*device
, *next
;
852 LIST_FOR_EACH_ENTRY_SAFE(device
, next
, &device_list
, MMDevice
, entry
)
853 MMDevice_Destroy(device
);
854 RegCloseKey(key_render
);
855 RegCloseKey(key_capture
);
856 key_render
= key_capture
= NULL
;
859 static HRESULT WINAPI
MMDevEnum_QueryInterface(IMMDeviceEnumerator
*iface
, REFIID riid
, void **ppv
)
861 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
862 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
866 if (IsEqualIID(riid
, &IID_IUnknown
)
867 || IsEqualIID(riid
, &IID_IMMDeviceEnumerator
))
868 *ppv
= &This
->IMMDeviceEnumerator_iface
;
872 return E_NOINTERFACE
;
873 IUnknown_AddRef((IUnknown
*)*ppv
);
877 static ULONG WINAPI
MMDevEnum_AddRef(IMMDeviceEnumerator
*iface
)
879 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
880 LONG ref
= InterlockedIncrement(&This
->ref
);
881 TRACE("Refcount now %i\n", ref
);
885 static ULONG WINAPI
MMDevEnum_Release(IMMDeviceEnumerator
*iface
)
887 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
888 LONG ref
= InterlockedDecrement(&This
->ref
);
889 TRACE("Refcount now %i\n", ref
);
893 static HRESULT WINAPI
MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator
*iface
, EDataFlow flow
, DWORD mask
, IMMDeviceCollection
**devices
)
895 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
896 TRACE("(%p)->(%u,%u,%p)\n", This
, flow
, mask
, devices
);
900 if (flow
>= EDataFlow_enum_count
)
902 if (mask
& ~DEVICE_STATEMASK_ALL
)
904 return MMDevCol_Create(devices
, flow
, mask
);
907 static HRESULT WINAPI
MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator
*iface
, EDataFlow flow
, ERole role
, IMMDevice
**device
)
909 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
914 TRACE("(%p)->(%u,%u,%p)\n", This
, flow
, role
, device
);
919 if((flow
!= eRender
&& flow
!= eCapture
) ||
920 (role
!= eConsole
&& role
!= eMultimedia
&& role
!= eCommunications
)){
921 WARN("Unknown flow (%u) or role (%u)\n", flow
, role
);
927 if(!drvs
.module_name
[0])
930 lstrcpyW(reg_key
, drv_keyW
);
931 lstrcatW(reg_key
, L
"\\");
932 lstrcatW(reg_key
, drvs
.module_name
);
934 if(RegOpenKeyW(HKEY_CURRENT_USER
, reg_key
, &key
) == ERROR_SUCCESS
){
935 const WCHAR
*reg_x_name
, *reg_vx_name
;
937 DWORD size
= sizeof(def_id
), state
;
940 reg_x_name
= L
"DefaultOutput";
941 reg_vx_name
= L
"DefaultVoiceOutput";
943 reg_x_name
= L
"DefaultInput";
944 reg_vx_name
= L
"DefaultVoiceInput";
947 if(role
== eCommunications
&&
948 RegQueryValueExW(key
, reg_vx_name
, 0, NULL
,
949 (BYTE
*)def_id
, &size
) == ERROR_SUCCESS
){
950 hr
= IMMDeviceEnumerator_GetDevice(iface
, def_id
, device
);
952 if(SUCCEEDED(IMMDevice_GetState(*device
, &state
)) &&
953 state
== DEVICE_STATE_ACTIVE
){
959 TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id
));
962 if(RegQueryValueExW(key
, reg_x_name
, 0, NULL
,
963 (BYTE
*)def_id
, &size
) == ERROR_SUCCESS
){
964 hr
= IMMDeviceEnumerator_GetDevice(iface
, def_id
, device
);
966 if(SUCCEEDED(IMMDevice_GetState(*device
, &state
)) &&
967 state
== DEVICE_STATE_ACTIVE
){
973 TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id
));
980 *device
= &MMDevice_def_play
->IMMDevice_iface
;
982 *device
= &MMDevice_def_rec
->IMMDevice_iface
;
986 IMMDevice_AddRef(*device
);
990 static HRESULT WINAPI
MMDevEnum_GetDevice(IMMDeviceEnumerator
*iface
, const WCHAR
*name
, IMMDevice
**device
)
992 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
994 IMMDevice
*dev
= NULL
;
996 TRACE("(%p)->(%s,%p)\n", This
, debugstr_w(name
), device
);
1001 if(!lstrcmpW(name
, L
"Wine info device")){
1002 *device
= &info_device
;
1006 LIST_FOR_EACH_ENTRY(impl
, &device_list
, MMDevice
, entry
)
1010 dev
= &impl
->IMMDevice_iface
;
1011 hr
= IMMDevice_GetId(dev
, &str
);
1014 WARN("GetId failed: %08x\n", hr
);
1018 if (str
&& !lstrcmpW(str
, name
))
1021 IMMDevice_AddRef(dev
);
1027 TRACE("Could not find device %s\n", debugstr_w(name
));
1028 return E_INVALIDARG
;
1031 struct NotificationClientWrapper
{
1032 IMMNotificationClient
*client
;
1036 static struct list g_notif_clients
= LIST_INIT(g_notif_clients
);
1037 static HANDLE g_notif_thread
;
1039 static CRITICAL_SECTION g_notif_lock
;
1040 static CRITICAL_SECTION_DEBUG g_notif_lock_debug
=
1042 0, 0, &g_notif_lock
,
1043 { &g_notif_lock_debug
.ProcessLocksList
, &g_notif_lock_debug
.ProcessLocksList
},
1044 0, 0, { (DWORD_PTR
)(__FILE__
": g_notif_lock") }
1046 static CRITICAL_SECTION g_notif_lock
= { &g_notif_lock_debug
, -1, 0, 0, 0, 0 };
1048 static void notify_clients(EDataFlow flow
, ERole role
, const WCHAR
*id
)
1050 struct NotificationClientWrapper
*wrapper
;
1051 LIST_FOR_EACH_ENTRY(wrapper
, &g_notif_clients
,
1052 struct NotificationClientWrapper
, entry
)
1053 IMMNotificationClient_OnDefaultDeviceChanged(wrapper
->client
, flow
,
1056 /* Windows 7 treats changes to eConsole as changes to eMultimedia */
1057 if(role
== eConsole
)
1058 notify_clients(flow
, eMultimedia
, id
);
1061 static BOOL
notify_if_changed(EDataFlow flow
, ERole role
, HKEY key
,
1062 const WCHAR
*val_name
, WCHAR
*old_val
, IMMDevice
*def_dev
)
1064 WCHAR new_val
[64], *id
;
1068 size
= sizeof(new_val
);
1069 if(RegQueryValueExW(key
, val_name
, 0, NULL
,
1070 (BYTE
*)new_val
, &size
) != ERROR_SUCCESS
){
1071 if(old_val
[0] != 0){
1072 /* set by user -> system default */
1074 hr
= IMMDevice_GetId(def_dev
, &id
);
1076 ERR("GetId failed: %08x\n", hr
);
1082 notify_clients(flow
, role
, id
);
1089 /* system default -> system default, noop */
1093 if(!lstrcmpW(old_val
, new_val
)){
1094 /* set by user -> same value */
1098 if(new_val
[0] != 0){
1099 /* set by user -> different value */
1100 notify_clients(flow
, role
, new_val
);
1101 memcpy(old_val
, new_val
, sizeof(new_val
));
1105 /* set by user -> system default */
1107 hr
= IMMDevice_GetId(def_dev
, &id
);
1109 ERR("GetId failed: %08x\n", hr
);
1115 notify_clients(flow
, role
, id
);
1122 static DWORD WINAPI
notif_thread_proc(void *user
)
1126 WCHAR out_name
[64], vout_name
[64], in_name
[64], vin_name
[64];
1129 lstrcpyW(reg_key
, drv_keyW
);
1130 lstrcatW(reg_key
, L
"\\");
1131 lstrcatW(reg_key
, drvs
.module_name
);
1133 if(RegCreateKeyExW(HKEY_CURRENT_USER
, reg_key
, 0, NULL
, 0,
1134 MAXIMUM_ALLOWED
, NULL
, &key
, NULL
) != ERROR_SUCCESS
){
1135 ERR("RegCreateKeyEx failed: %u\n", GetLastError());
1139 size
= sizeof(out_name
);
1140 if(RegQueryValueExW(key
, L
"DefaultOutput", 0, NULL
, (BYTE
*)out_name
, &size
) != ERROR_SUCCESS
)
1143 size
= sizeof(vout_name
);
1144 if(RegQueryValueExW(key
, L
"DefaultVoiceOutput", 0, NULL
, (BYTE
*)vout_name
, &size
) != ERROR_SUCCESS
)
1147 size
= sizeof(in_name
);
1148 if(RegQueryValueExW(key
, L
"DefaultInput", 0, NULL
, (BYTE
*)in_name
, &size
) != ERROR_SUCCESS
)
1151 size
= sizeof(vin_name
);
1152 if(RegQueryValueExW(key
, L
"DefaultVoiceInput", 0, NULL
, (BYTE
*)vin_name
, &size
) != ERROR_SUCCESS
)
1156 if(RegNotifyChangeKeyValue(key
, FALSE
, REG_NOTIFY_CHANGE_LAST_SET
,
1157 NULL
, FALSE
) != ERROR_SUCCESS
){
1158 ERR("RegNotifyChangeKeyValue failed: %u\n", GetLastError());
1160 g_notif_thread
= NULL
;
1164 EnterCriticalSection(&g_notif_lock
);
1166 notify_if_changed(eRender
, eConsole
, key
, L
"DefaultOutput",
1167 out_name
, &MMDevice_def_play
->IMMDevice_iface
);
1168 notify_if_changed(eRender
, eCommunications
, key
, L
"DefaultVoiceOutput",
1169 vout_name
, &MMDevice_def_play
->IMMDevice_iface
);
1170 notify_if_changed(eCapture
, eConsole
, key
, L
"DefaultInput",
1171 in_name
, &MMDevice_def_rec
->IMMDevice_iface
);
1172 notify_if_changed(eCapture
, eCommunications
, key
, L
"DefaultVoiceInput",
1173 vin_name
, &MMDevice_def_rec
->IMMDevice_iface
);
1175 LeaveCriticalSection(&g_notif_lock
);
1180 g_notif_thread
= NULL
;
1185 static HRESULT WINAPI
MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator
*iface
, IMMNotificationClient
*client
)
1187 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
1188 struct NotificationClientWrapper
*wrapper
;
1190 TRACE("(%p)->(%p)\n", This
, client
);
1195 wrapper
= HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper
));
1197 return E_OUTOFMEMORY
;
1199 wrapper
->client
= client
;
1201 EnterCriticalSection(&g_notif_lock
);
1203 list_add_tail(&g_notif_clients
, &wrapper
->entry
);
1205 if(!g_notif_thread
){
1206 g_notif_thread
= CreateThread(NULL
, 0, notif_thread_proc
, NULL
, 0, NULL
);
1208 ERR("CreateThread failed: %u\n", GetLastError());
1211 LeaveCriticalSection(&g_notif_lock
);
1216 static HRESULT WINAPI
MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator
*iface
, IMMNotificationClient
*client
)
1218 MMDevEnumImpl
*This
= impl_from_IMMDeviceEnumerator(iface
);
1219 struct NotificationClientWrapper
*wrapper
;
1221 TRACE("(%p)->(%p)\n", This
, client
);
1226 EnterCriticalSection(&g_notif_lock
);
1228 LIST_FOR_EACH_ENTRY(wrapper
, &g_notif_clients
, struct NotificationClientWrapper
, entry
){
1229 if(wrapper
->client
== client
){
1230 list_remove(&wrapper
->entry
);
1231 HeapFree(GetProcessHeap(), 0, wrapper
);
1232 LeaveCriticalSection(&g_notif_lock
);
1237 LeaveCriticalSection(&g_notif_lock
);
1242 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl
=
1244 MMDevEnum_QueryInterface
,
1247 MMDevEnum_EnumAudioEndpoints
,
1248 MMDevEnum_GetDefaultAudioEndpoint
,
1249 MMDevEnum_GetDevice
,
1250 MMDevEnum_RegisterEndpointNotificationCallback
,
1251 MMDevEnum_UnregisterEndpointNotificationCallback
1254 static MMDevEnumImpl enumerator
=
1260 static HRESULT
MMDevPropStore_Create(MMDevice
*parent
, DWORD access
, IPropertyStore
**ppv
)
1262 MMDevPropStore
*This
;
1263 if (access
!= STGM_READ
1264 && access
!= STGM_WRITE
1265 && access
!= STGM_READWRITE
)
1267 WARN("Invalid access %08x\n", access
);
1268 return E_INVALIDARG
;
1270 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1271 *ppv
= &This
->IPropertyStore_iface
;
1273 return E_OUTOFMEMORY
;
1274 This
->IPropertyStore_iface
.lpVtbl
= &MMDevPropVtbl
;
1276 This
->parent
= parent
;
1277 This
->access
= access
;
1281 static void MMDevPropStore_Destroy(MMDevPropStore
*This
)
1283 HeapFree(GetProcessHeap(), 0, This
);
1286 static HRESULT WINAPI
MMDevPropStore_QueryInterface(IPropertyStore
*iface
, REFIID riid
, void **ppv
)
1288 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1289 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
1293 if (IsEqualIID(riid
, &IID_IUnknown
)
1294 || IsEqualIID(riid
, &IID_IPropertyStore
))
1295 *ppv
= &This
->IPropertyStore_iface
;
1299 return E_NOINTERFACE
;
1300 IUnknown_AddRef((IUnknown
*)*ppv
);
1304 static ULONG WINAPI
MMDevPropStore_AddRef(IPropertyStore
*iface
)
1306 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1307 LONG ref
= InterlockedIncrement(&This
->ref
);
1308 TRACE("Refcount now %i\n", ref
);
1312 static ULONG WINAPI
MMDevPropStore_Release(IPropertyStore
*iface
)
1314 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1315 LONG ref
= InterlockedDecrement(&This
->ref
);
1316 TRACE("Refcount now %i\n", ref
);
1318 MMDevPropStore_Destroy(This
);
1322 static HRESULT WINAPI
MMDevPropStore_GetCount(IPropertyStore
*iface
, DWORD
*nprops
)
1324 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1330 TRACE("(%p)->(%p)\n", iface
, nprops
);
1333 hr
= MMDevPropStore_OpenPropKey(&This
->parent
->devguid
, This
->parent
->flow
, &propkey
);
1338 DWORD len
= ARRAY_SIZE(buffer
);
1339 if (RegEnumValueW(propkey
, i
, buffer
, &len
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
1343 RegCloseKey(propkey
);
1344 TRACE("Returning %i\n", i
);
1349 static HRESULT WINAPI
MMDevPropStore_GetAt(IPropertyStore
*iface
, DWORD prop
, PROPERTYKEY
*key
)
1351 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1353 DWORD len
= ARRAY_SIZE(buffer
);
1357 TRACE("(%p)->(%u,%p)\n", iface
, prop
, key
);
1361 hr
= MMDevPropStore_OpenPropKey(&This
->parent
->devguid
, This
->parent
->flow
, &propkey
);
1365 if (RegEnumValueW(propkey
, prop
, buffer
, &len
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
1368 WARN("GetAt %u failed\n", prop
);
1369 return E_INVALIDARG
;
1371 RegCloseKey(propkey
);
1373 CLSIDFromString(buffer
, &key
->fmtid
);
1374 key
->pid
= wcstol(&buffer
[39], NULL
, 10);
1378 static HRESULT WINAPI
MMDevPropStore_GetValue(IPropertyStore
*iface
, REFPROPERTYKEY key
, PROPVARIANT
*pv
)
1380 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1381 TRACE("(%p)->(\"%s,%u\", %p)\n", This
, key
? debugstr_guid(&key
->fmtid
) : NULL
, key
? key
->pid
: 0, pv
);
1385 if (This
->access
!= STGM_READ
1386 && This
->access
!= STGM_READWRITE
)
1387 return STG_E_ACCESSDENIED
;
1390 if (IsEqualPropertyKey(*key
, PKEY_AudioEndpoint_GUID
))
1393 pv
->pwszVal
= CoTaskMemAlloc(39 * sizeof(WCHAR
));
1395 return E_OUTOFMEMORY
;
1396 StringFromGUID2(&This
->parent
->devguid
, pv
->pwszVal
, 39);
1400 return MMDevice_GetPropValue(&This
->parent
->devguid
, This
->parent
->flow
, key
, pv
);
1403 static HRESULT WINAPI
MMDevPropStore_SetValue(IPropertyStore
*iface
, REFPROPERTYKEY key
, REFPROPVARIANT pv
)
1405 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1406 TRACE("(%p)->(\"%s,%u\", %p)\n", This
, key
? debugstr_guid(&key
->fmtid
) : NULL
, key
? key
->pid
: 0, pv
);
1411 if (This
->access
!= STGM_WRITE
1412 && This
->access
!= STGM_READWRITE
)
1413 return STG_E_ACCESSDENIED
;
1414 return MMDevice_SetPropValue(&This
->parent
->devguid
, This
->parent
->flow
, key
, pv
);
1417 static HRESULT WINAPI
MMDevPropStore_Commit(IPropertyStore
*iface
)
1419 MMDevPropStore
*This
= impl_from_IPropertyStore(iface
);
1420 TRACE("(%p)\n", iface
);
1422 if (This
->access
!= STGM_WRITE
1423 && This
->access
!= STGM_READWRITE
)
1424 return STG_E_ACCESSDENIED
;
1426 /* Does nothing - for mmdevapi, the propstore values are written on SetValue,
1432 static const IPropertyStoreVtbl MMDevPropVtbl
=
1434 MMDevPropStore_QueryInterface
,
1435 MMDevPropStore_AddRef
,
1436 MMDevPropStore_Release
,
1437 MMDevPropStore_GetCount
,
1438 MMDevPropStore_GetAt
,
1439 MMDevPropStore_GetValue
,
1440 MMDevPropStore_SetValue
,
1441 MMDevPropStore_Commit
1445 /* Property bag for IBaseFilter activation */
1446 static HRESULT WINAPI
PB_QueryInterface(IPropertyBag
*iface
, REFIID riid
, void **ppv
)
1448 ERR("Should not be called\n");
1450 return E_NOINTERFACE
;
1453 static ULONG WINAPI
PB_AddRef(IPropertyBag
*iface
)
1455 ERR("Should not be called\n");
1459 static ULONG WINAPI
PB_Release(IPropertyBag
*iface
)
1461 ERR("Should not be called\n");
1465 static HRESULT WINAPI
PB_Read(IPropertyBag
*iface
, LPCOLESTR name
, VARIANT
*var
, IErrorLog
*log
)
1467 IPropertyBagImpl
*This
= impl_from_IPropertyBag(iface
);
1468 TRACE("Trying to read %s, type %u\n", debugstr_w(name
), var
->n1
.n2
.vt
);
1469 if (!lstrcmpW(name
, L
"DSGuid"))
1472 StringFromGUID2(&This
->devguid
, guidstr
,ARRAY_SIZE(guidstr
));
1473 var
->n1
.n2
.vt
= VT_BSTR
;
1474 var
->n1
.n2
.n3
.bstrVal
= SysAllocString(guidstr
);
1477 ERR("Unknown property '%s' queried\n", debugstr_w(name
));
1481 static HRESULT WINAPI
PB_Write(IPropertyBag
*iface
, LPCOLESTR name
, VARIANT
*var
)
1483 ERR("Should not be called\n");
1487 static const IPropertyBagVtbl PB_Vtbl
=
1496 static ULONG WINAPI
info_device_ps_AddRef(IPropertyStore
*iface
)
1501 static ULONG WINAPI
info_device_ps_Release(IPropertyStore
*iface
)
1506 static HRESULT WINAPI
info_device_ps_GetValue(IPropertyStore
*iface
,
1507 REFPROPERTYKEY key
, PROPVARIANT
*pv
)
1509 TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key
->fmtid
), key
? key
->pid
: 0, pv
);
1514 if (IsEqualPropertyKey(*key
, DEVPKEY_Device_Driver
))
1516 INT size
= (lstrlenW(drvs
.module_name
) + 1) * sizeof(WCHAR
);
1518 pv
->pwszVal
= CoTaskMemAlloc(size
);
1520 return E_OUTOFMEMORY
;
1521 memcpy(pv
->pwszVal
, drvs
.module_name
, size
);
1525 return E_INVALIDARG
;
1528 static const IPropertyStoreVtbl info_device_ps_Vtbl
=
1531 info_device_ps_AddRef
,
1532 info_device_ps_Release
,
1535 info_device_ps_GetValue
,
1540 static IPropertyStore info_device_ps
= {
1541 &info_device_ps_Vtbl
1544 static ULONG WINAPI
info_device_AddRef(IMMDevice
*iface
)
1549 static ULONG WINAPI
info_device_Release(IMMDevice
*iface
)
1554 static HRESULT WINAPI
info_device_OpenPropertyStore(IMMDevice
*iface
,
1555 DWORD access
, IPropertyStore
**ppv
)
1557 TRACE("(static)->(%x, %p)\n", access
, ppv
);
1558 *ppv
= &info_device_ps
;
1562 static const IMMDeviceVtbl info_device_Vtbl
=
1566 info_device_Release
,
1568 info_device_OpenPropertyStore
,
1573 static IMMDevice info_device
= {